2009-09-10 17 views
8

Estoy usando string.split() en mi código C# para leer el archivo separado por pestañas. Me enfrento a la "excepción OutOfMemory" como se menciona a continuación en la muestra del código.string.split() "Excepción de falta de memoria" al leer el archivo separado por tabulaciones

Aquí me gustaría saber por qué viene el problema de que el archivo tenga un tamaño de 16 MB?

¿Este es el enfoque correcto o no?

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

Como un lado, Eric Lippert tiene un gran blog en OutOfMemoryExceptions. http://blogs.msdn.com/ericlippert/archive/2009/06/08/out-of-memory-does-not-refer-to-physical-memory.aspx –

+0

Esto está en el marco compacto (es decir, Windows Mobile)? – MusiGenesis

Respuesta

12

Split se ejecute de forma incorrecta, y tienen problema grave el rendimiento cuando aplicado en grandes cadenas. Por favor refiérase a this article for details on memory requirements by split function:

¿Qué pasa cuando se hace una división en una cadena que contiene 1.355.049 comas cadenas separadas de 16 caracteres cada una, que tiene una longitud total de 25.745.930 carácter?

  1. una matriz de punteros a objeto de cadena: Contigua espacio de direcciones virtuales de 4 (puntero de dirección) * 1.355.049 = 5.420.196 (tamaño de matrices) + 16 (para la contabilidad) = 5420212.

  2. No- espacio contiguo de direcciones virtuales para cadenas 1355049, cada una de 54 bytes. No significa que todas esas 1,3 millones de cadenas se distribuirán por todo el montón, pero no se asignarán en LOH. GC los asignará en racimos en el montón Gen0.

  3. Split.Function creará matriz interna de System.Int32 [] del tamaño de 25.745.930, consumiendo (102983736 bytes) ~ 98 MB de LOH, que es muy caro L.

1

Intente leer el archivo linewise en lugar de dividir todo el contenido.

10

Trate no leer todo el archivo en una matriz primero "reader.ReadToEnd()" Lea el archivo línea por línea directa ..

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

¿Tiene un impacto cuando leemos línea por línea –

+1

No funciona si todos mis datos están en una sola línea. – Butzke

4

Recomendaría leyendo línea por línea si puedes, pero a veces dividir por nuevas líneas no es el requisito.

Para que siempre pueda escribir su propia división de memoria eficiente. Esto resolvió mi problema.

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

Uso el mío. Ha sido probado con 10 pruebas unitarias.

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; 
    } 
} 
Cuestiones relacionadas