2009-09-10 10 views
8

Używam string.split() w moim kodzie C# do odczytu pliku oddzielonego tabulatorami. Mam do czynienia z wyjątkiem OutOfMemory ", jak wspomniano poniżej w przykładowym kodzie.string.split() "Wyjątek braku pamięci" podczas odczytu pliku oddzielonego tabulatorami

Tutaj chciałbym wiedzieć, dlaczego problem dotyczy pliku mającego rozmiar 16 MB?

To właściwe podejście czy nie?

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 
     } 
+4

Eric Lippert ma świetny blog na temat OutOfMemoryExceptions. http://blogs.msdn.com/ericlippert/archive/2009/06/08/out-of-memory-does-not-refer-to-physical-memory.aspx –

+0

Czy to na zwartej strukturze (np. Windows Mobile)? – MusiGenesis

Odpowiedz

12

Podział jest realizowany słabo i mają poważny problem wydajność podczas nałożone na ogromne struny. Proszę odnieść się do this article for details on memory requirements by split function:

Co się dzieje, kiedy zrobić podział na sznurku zawierającej 1355049 oddzielonych przecinkami ciągi 16 znaków każdy, mając całkowitą długość znaków 25745930?

  1. tablicę wskaźników do obiektu String: Sąsiadujące wirtualnej przestrzeni adresowej z 4 (wskaźnik adresu) * 1.355.049 = 5.420.196 (rozmiar tablic) + 16 (dla księgowości) = 5420212.

  2. Non sąsiadująca wirtualna przestrzeń adresowa dla 1355049 ciągów, każdy z 54 bajtów. Nie oznacza to, że wszystkie te 1,3 miliona strun będą rozproszone po całej hałdzie, ale nie zostaną przydzielone na LOH. GC przydzieli je na pęczkach na stercie Gen0.

  3. Split.Function tworzy wewnętrzną tablicę System.Int32 [] wielkości 25745930 zużywających (102983736 bajtów) ~ 98MB LOH, co jest bardzo kosztowne L.

1

Spróbuj podzielić się plikami, zamiast dzielić całą treść.

10

Spróbuj nie czytania całego pliku do tablicy pierwszy „reader.ReadToEnd()” Czytaj z pliku linia po linii bezpośrednio ..

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) 
       { 

       } 
      } 
     } 
+0

Czy to ma wpływ, gdy czytamy wiersz po linii –

+1

Nie działa, jeśli wszystkie moje dane są w jednym wierszu. – Butzke

4

Polecam czytanie linii po linii, jeśli możesz, ale czasami dzielenie przez nowe linie nie jest wymogiem.

Dzięki temu zawsze możesz napisać własną efektywną pamięć. To rozwiązało problem dla mnie.

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; 
    } 
2

Używam własnego. Zostało przetestowane przy 10 testach jednostkowych.

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; 
    } 
} 
Powiązane problemy