Member 14318223 Ответов: 3

C# как читать данные из текстового файла


Я написал программу для решения систем линейных уравнений
и я хочу прочитать данные из текстового файла в формате

число уравнений
коэффициенты строки № 1, включающие элемент вектора независимых членов
разделенные пространством
коэффициенты строки № 2, включающие элемент вектора независимых членов
разделенные пространством

но я уже нашел как читать файл строка за строкой в строку

using System;
using System.IO;
using System.Globalization;
namespace LUdecomposition
{
	class LUdecomposition
	{
		public const double ZERO = 1.0e-15;
		public static void Main()
		{
			char esc;
			string str,path;
			int i,j,n;
			double[,] A;
			double[] b;
			int[] p;
			double d;
			NumberFormatInfo nfi = new NumberFormatInfo();
			nfi.NumberDecimalSeparator = ".";
			Console.WriteLine("Podaj ścieżkę do pliku w którym chcesz zapisać wynik");
			path = Console.ReadLine();
			using(StreamWriter sw = new StreamWriter(path,true))
			{
				do
				{
					try
					{
						Console.Clear();
						Console.WriteLine("Rozwiązywanie układów równań liniowych metodą rozkładu macierzy LU = PA");
						Console.WriteLine();
						Console.WriteLine("Podaj liczbę równań n=");
						str = Console.ReadLine();
						int.TryParse(str,out n);
						A = new double[n,n];
						b = new double[n];
						p = new int[n];
						for(i = 0;i < n;i++)
						{
							Console.WriteLine("Wprowadz " + (i+1).ToString(nfi) + ". wiersz macierzy");
							for(j = 0;j < n;j++)
							{
								str = Console.ReadLine();
								double.TryParse(str,out A[i,j]);
							}
							Console.WriteLine("Wprowadz " + (i+1).ToString(nfi) + ". wyraz wolny");
							str = Console.ReadLine();
							double.TryParse(str,out b[i]);
						}
						FactorLU(n,A,p,out d);
						sw.WriteLine(d.ToString(nfi));
						for(i = 0;i < n && Math.Abs(d) > ZERO  ;i++)
						{
							for(j = 0;j < n; j++)
							{
								Console.Write(A[i,j].ToString(nfi)+" , ");
								sw.Write(A[i,j].ToString(nfi) + " , ");
							}
							Console.WriteLine();
							sw.WriteLine();
						}
						if(Math.Abs(d) > ZERO)
						{
							SolveLU(n,A,p,b);
							for(i = 0;i < n && Math.Abs(d) > ZERO ;i++)
							{
								Console.WriteLine(b[i].ToString(nfi));
								sw.WriteLine(b[i].ToString(nfi));
							}
							Console.WriteLine();
							sw.WriteLine();
						}
					}
					catch(System.IndexOutOfRangeException e)
					{
						
					}
					esc = (char)Console.ReadKey().Key;
				}
				while(esc != (char)ConsoleKey.Escape);
			}
		}
		public static void FactorLU(int n,double[,] A,int[] p,out double d)
		{
			int k,i,j,l;
			double pmax,tmp;
			d = 1.0;
			for(k = 0;k < n ;k++)
			{
				pmax = Math.Abs(A[k,k]);
				l = k;
				for(j = k + 1;j < n; j++)
				{
					if(Math.Abs(A[j,k]) > pmax)
					{
						pmax = Math.Abs(A[j,k]);
						l = j;
					}
				}
				if(l != k)
				{
					for(j = 0;j < n; j++)
					{
						tmp = A[k,j];
						A[k,j] = A[l,j];
						A[l,j] = tmp;
					}
					d = -d;
				}
				d *= A[k,k];
				if(pmax <= ZERO)
					return ;	
				p[k] = l;
				for(i = k + 1; i < n; i++)
				{
					A[i,k]/=A[k,k];
					for(j = k + 1;j < n; j++)
						A[i,j] -= A[i,k]*A[k,j];
				}
			}
		}
		public static void SolveLU(int n,double[,] LU,int[] p,double[] b)
		{
			int i,k;
			double tmp;
			for(k = 0;k < n - 1; k++)
			{
				tmp = b[k];
				b[k] = b[p[k]];
				b[p[k]] = tmp;
			}
			for(k = 1;k < n; k++)
				for(i = 0; i < k; i++)
					b[k] -= b[i]*LU[k,i];
			for(k = n - 1; k >= 0; k--)
			{
				for(i = k + 1;i < n; i++)
					b[k] -= LU[k,i]*b[i];
				b[k] /= LU[k,k];
			}
		}
	}
}


Что я уже пробовал:

Я нашел как читать файл строка за строкой в строку


using System;
using System.IO;
namespace FileApplication {
   class Program {
       static void Main(string[] args) {
           try{
              // Create an instance of StreamReader to read from a file.
              // The using statement also closes the StreamReader.
           string path;
           Console.ReadLine(path);
           using(StreamReader sr =newStreamReader(path)) {
              string line;

              // Read and display lines from the file until 
              // the end of the file is reached. 
             while((line = sr.ReadLine())!=null) {
               Console.WriteLine(line);
          }
        }
      }catch(Exception e) {
       // Let the user know what went wrong.
      Console.WriteLine("The file could not be read:");
      Console.WriteLine(e.Message);
    }
   Console.ReadKey();
 }
}
}


Я не хочу использовать функцию Split для анализа данных из строки
потому что ему нужно создать дополнительный строковый массив

Может быть, Trim,IndexOf, Substring будет достаточно
но как их использовать

3 Ответов

Рейтинг:
28

Thomas Daniels

Цитата:
Я не хочу использовать функцию Split для анализа данных из строки

Вы утверждаете, что у вас есть данные, разделенные пробелами, так зачем же делать это так сложно для себя? Split это абсолютно самый простой инструмент для этой работы. Если вы настаиваете на том, чтобы не использовать его, вы можете перебирать все символы вашего текстового файла и действовать по-разному с числами, пробелами и т. д. (Например, отслеживать цифры, пока не наткнетесь на новый пробел), но я действительно не вижу смысла в этом по сравнению с использованием Split.


Member 14318223

Вы не процитировали всего предложения
Я видел разбор нескольких чисел из строки с помощью Split
но я не хочу использовать дополнительный строковый массив и поэтому ищу другое решение
Разделение пространства неэффективное решение
Pascal имеет pos и copy , здесь C# должен иметь аналогичные функции

Рейтинг:
0

OriginalGriff

Все функции манипулирования строками создают новые строки - они должны, потому что строки неизменяемы; они не могут быть изменены вообще, как только они созданы.

Так что используйте строку.Split - это сделает ваш код намного понятнее и, вероятно, легче читаемым.
Кроме того, я бы прочитал файл вот так:

string[] lines = File.ReadAllLines(path);
foreach (string line in lines)
   {
   string[] parts = line.Split(" ");
   ...
   }


Рейтинг:
0

Richard Deeming

Если вы хотите избежать выделения новых строк и массивов строк, вы можете сколотить что-то вместе, используя относительно новые строки и массивы строк. ReadOnlySpan<T> тип.

C# - Все о Span: изучение нового оплота .NET | Microsoft Docs[^]

Похоже, что пока нет никакой встроенной поддержки для разделения строк, но довольно легко свернуть свои собственные:

public static class StringExtensions
{
    public ref struct SplitByCharEnumerator
    {
        private ReadOnlySpan<char> _value;
        private readonly ReadOnlySpan<char> _separator;
        private readonly bool _isEmpty;
        private bool _removeEmpty;
    
        public SplitByCharEnumerator(ReadOnlySpan<char> value, ReadOnlySpan<char> separator, StringSplitOptions options)
        {
            _value = value;
            _separator = separator;
            _removeEmpty = options == StringSplitOptions.RemoveEmptyEntries;
            _isEmpty = _value.IsEmpty || _separator.IsEmpty;
            Current = default;
        }
    
        public SplitByCharEnumerator GetEnumerator() => this;
    
        public bool MoveNext()
        {
            if (_isEmpty)
            {
                if (_removeEmpty) return false;
    
                _removeEmpty = true;
                Current = _value;
                return true;
            }
    
            while (!_value.IsEmpty)
            {
                int index = _value.IndexOfAny(_separator);
                if (index == -1)
                {
                    Current = _value;
                    _value = default;
                    return true;
                }
    
                if (index != 0 || !_removeEmpty)
                {
                    Current = _value.Slice(0, index);
                    _value = _value.Slice(index + 1);
                    return true;
                }
    
                _value = _value.Slice(index + 1);
            }
    
            return false;
        }
    
        public ReadOnlySpan<char> Current { get; private set; }
    }
    
    public static SplitByCharEnumerator Split(this ReadOnlySpan<char> source, char[] separator, StringSplitOptions options) => new SplitByCharEnumerator(source, separator, options);
    
    public static SplitByCharEnumerator Split(this ReadOnlySpan<char> source, params char[] separator) => new SplitByCharEnumerator(source, separator, StringSplitOptions.None);
}
Единственная сложная часть заключается в том, что итератор должен быть один ref struct[^], так что он не может реализовать IEnumerable<T> Но вы все равно можете использовать его в foreach петля:
foreach (string line in File.ReadLines(path))
{
    foreach (ReadOnlySpan<char> part in line.AsSpan().Split(' '))
    {
        ...
    }
}
К сожалению, .NET Framework, похоже, не имеет никакого способа проанализировать число из a ReadOnlySpan<char> без преобразования его в string во-первых; .NET Core 2.2 или более поздняя версия позволит вам анализировать непосредственно без каких-либо дополнительных выделений.


Member 14318223

Я думал об использовании функций Trim,IndexOf,Substring и TryParse
но тогда мы должны проверить, все ли ожидаемые данные успешно считаны
Я думал о таких функциях , как Pos, Copy в Pascal
но если ваше решение имеет лучшую пространственную сложность, возможно, мне следует рассмотреть его