string.split () & # 8220; Excepción de memoria insuficiente & # 8221; Al leer el archivo separado por pestañas

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

  •  05-07-2019
  •  | 
  •  

Pregunta

Estoy usando string.split () en mi código C # para leer el archivo separado por tabulaciones. Estoy frente a la excepción "OutOfMemory" como se menciona a continuación en el ejemplo de código.

Aquí me gustaría saber por qué el problema viene para un archivo que tiene 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
      }
¿Fue útil?

Solución

La división se implementa de manera deficiente y tiene un grave problema de rendimiento cuando se aplica en cadenas grandes. Consulte este artículo para obtener detalles sobre los requisitos de memoria por función de división :

  

¿Qué sucede cuando haces una división en una cadena que contiene 1355049 cadenas separadas por comas de 16 caracteres cada una, con una longitud total de caracteres de 25745930?

     
      
  1. Una matriz de punteros a objeto de cadena: espacio de direcciones virtuales contiguas de 4 (puntero de dirección) * 1355049 = 5420196 (tamaño de las matrices) + 16 (para contabilidad) = 5420212.

  2.   
  3. Espacio de direcciones virtuales no contiguas para 1355049 cadenas, cada una de 54 bytes. No significa que todas esas 1.3 millones de cadenas se dispersen por todo el montón, pero no se asignarán en LOH. GC los asignará en grupos en el montón Gen0.

  4.   
  5. Split.Function creará una matriz interna de System.Int32 [] de tamaño 25745930, que consume (102983736 bytes) ~ 98MB de LOH, que es muy costoso L.

  6.   

Otros consejos

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

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

                }
            }
        }

Recomendaría leer línea por línea si puede, pero a veces no es necesario dividir por nuevas líneas.

Para que siempre puedas escribir tu propia división de memoria eficiente. Esto solucionó el problema para mí.

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

Yo uso mi propio. Se ha probado con 10 pruebas de unidad.

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

Intente leer el archivo en línea en lugar de dividir todo el contenido.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top