string.split () & # 8220; Исключение из-за недостатка памяти & # 8221; при чтении файла с разделителями табуляции

StackOverflow https://stackoverflow.com/questions/1404435

  •  05-07-2019
  •  | 
  •  

Вопрос

Я использую string.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] = "\r\n";
  //.............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
      }
Это было полезно?

Решение

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

  

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

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

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

  •   
  • Функция Split.Function создаст внутренний массив System.Int32 [] размером 25745930, потребляющий (102983736 байт) ~ 98 МБ 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;
        }
    }
    

    Попробуйте прочитать файл, а не разбивать весь контент.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top