строка.split () "исключение из памяти" при чтении файла, разделенного вкладкой

Я использую строку.split () в моем коде C# для чтения разделенного табуляцией файла. Я сталкиваюсь с "исключением OutOfMemory", как указано ниже в примере кода.

здесь я хотел бы знать, почему проблема возникает для файла с размером 16 МБ?

Это правильный подход или нет?

using (StreamReader reader = new StreamReader(_path))
{
  //...........Load the first line of the file................
  string headerLine = reader.ReadLine();

  MeterDataIPValueList objMeterDataList = new MeterDataIPValueList();
  string[] seperator = new string[1];   //used to sepreate lines of file

  seperator[0] = "rn";
  //.............Load Records of file into string array and remove all empty lines of file.................
  string[] line = reader.ReadToEnd().Split(seperator, StringSplitOptions.RemoveEmptyEntries);
  int noOfLines = line.Count();
  if (noOfLines == 0)
  {
    mFileValidationErrors.Append(ConstMsgStrings.headerOnly + Environment.NewLine);
  }
  //...............If file contains records also with header line..............
  else
  {
    string[] headers = headerLine.Split('t');
    int noOfColumns = headers.Count();

    //.........Create table structure.............
    objValidateRecordsTable.Columns.Add("SerialNo");
    objValidateRecordsTable.Columns.Add("SurveyDate");
    objValidateRecordsTable.Columns.Add("Interval");
    objValidateRecordsTable.Columns.Add("Status");
    objValidateRecordsTable.Columns.Add("Consumption");

    //........Fill objValidateRecordsTable table by string array contents ............

    int recordNumber;  // used for log
    #region ..............Fill objValidateRecordsTable.....................
    seperator[0] = "t";
    for (int lineNo = 0; lineNo < noOfLines; lineNo++)
    {
      recordNumber = lineNo + 1;
      **string[] recordFields = line[lineNo].Split(seperator, StringSplitOptions.RemoveEmptyEntries);** // Showing me error when we  split columns
      if (recordFields.Count() == noOfColumns)
      {
        //Do processing
      }

5 ответов


Split реализован плохо и имеет серьезную проблему с производительностью при применении к огромным строкам. Пожалуйста, обратитесь к эта статья для получения подробной информации о требованиях к памяти с помощью функции split:

Что происходит, когда вы делаете разбиение на строку, содержащую 1355049 разделенных запятыми строк по 16 символов каждый, имеющих общую длину символов 25745930 ?

  1. массив указателей на строковый объект: непрерывное виртуальное адресное пространство 4 (указатель адреса)*1355049 = 5420196 (размер массивов) + 16 (для учета) = 5420212.

  2. несмежное виртуальное адресное пространство для 1355049 строк, каждая из 54 байт. Это не означает, что все эти 1,3 миллиона строк будут разбросаны по всей куче, но они не будут выделены на LOH. GC распределит их по гроздьям на куче Gen0.

  3. разделить.Функция создаст внутренний массив системы.Int32 [] размера 25745930, потребляя (102983736 байт) ~98MB LOH, что очень дорого L.


попробовать не чтение всего файла в массив сначала " reader.ReadToEnd () " прочитайте файл строка за строкой напрямую ..

  using (StreamReader sr = new StreamReader(this._path))
        {
            string line = "";
            while(( line= sr.ReadLine()) != null)
            {
                string[] cells = line.Split(new string[] { "\t" }, StringSplitOptions.None);
                if (cells.Length > 0)
                {

                }
            }
        }

Я бы рекомендовал читать по строкам, если вы можете, но иногда разделение на новые строки не является требованием.

таким образом, вы всегда можете написать свой собственный эффективный сплит памяти. Это решило проблему для меня.

    private static IEnumerable<string> CustomSplit(string newtext, char splitChar)
    {
        var result = new List<string>();
        var sb = new StringBuilder();
        foreach (var c in newtext)
        {
            if (c == splitChar)
            {
                if (sb.Length > 0)
                {
                    result.Add(sb.ToString());
                    sb.Clear();
                }
                continue;
            }
            sb.Append(c);
        }
        if (sb.Length > 0)
        {
            result.Add(sb.ToString());
        }
        return result;
    }

Я использую свой собственный. Он был испытан с 10 модульными испытаниями..

public static class StringExtensions
{

    // the string.Split() method from .NET tend to run out of memory on 80 Mb strings. 
    // this has been reported several places online. 
    // This version is fast and memory efficient and return no empty lines. 
    public static List<string> LowMemSplit(this string s, string seperator)
    {
        List<string> list = new List<string>();
        int lastPos = 0;
        int pos = s.IndexOf(seperator);
        while (pos > -1)
        {
            while(pos == lastPos)
            {
                lastPos += seperator.Length;
                pos = s.IndexOf(seperator, lastPos);
                if (pos == -1)
                    return list;
            }

            string tmp = s.Substring(lastPos, pos - lastPos);
            if(tmp.Trim().Length > 0)
                list.Add(tmp);
            lastPos = pos + seperator.Length;
            pos = s.IndexOf(seperator, lastPos);
        }

        if (lastPos < s.Length)
        {
            string tmp = s.Substring(lastPos, s.Length - lastPos);
            if (tmp.Trim().Length > 0)
                list.Add(tmp);
        }

        return list;
    }
}

попробуйте прочитать файл линейно вместо разделения всего содержимого.