none
error de 'System.OutOfMemoryException'. al crear un archivo plano RRS feed

  • Pregunta

  • buen dia!

    Estoy armando un archivo plano donde tengo 34 campos, en los campos 24 hay que dejar 255 espacios en blanco y en el campo 26 se colocan 2000 espacios en blanco, la consulta que realizo es de 148316 registros se llevan a un DataTable para recorrer cada fila en un foreach.

    En el registro 81312 sale el siguiente error 

    No se controló System.OutOfMemoryException
      Message="Se produjo una excepción de tipo 'System.OutOfMemoryException'."
      Source="mscorlib"
      StackTrace:
           en System.String.GetStringForStringBuilder(String value, Int32 startIndex, Int32 length, Int32 capacity)
           en System.Text.StringBuilder.GetNewString(String currentString, Int32 requiredLength)
           en System.Text.StringBuilder.Append(String value)
           en WindowsApplication1.FrmOrdenCompraPlano.btnExportar_Click(Object sender, EventArgs e) en C:\Mis Documentos\PRACTICANTE_INFORMATICA\JOHAN\AM Carga Pedidos Universal\Aplicativo Sip\FrmOrdenCompraPlano.cs:línea 254
           en System.Windows.Forms.Control.OnClick(EventArgs e)
           en System.Windows.Forms.Button.OnClick(EventArgs e)
           en System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
           en System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
           en System.Windows.Forms.Control.WndProc(Message& m)
           en System.Windows.Forms.ButtonBase.WndProc(Message& m)
           en System.Windows.Forms.Button.WndProc(Message& m)
           en System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
           en System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
           en System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
           en System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
           en System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
           en System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
           en System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
           en System.Windows.Forms.Application.Run(Form mainForm)
           en WindowsApplication1.Program.Main() en C:\Mis Documentos\PRACTICANTE_INFORMATICA\JOHAN\AM Carga Pedidos Universal\Aplicativo Sip\Program.cs:línea 17
           en System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
           en System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
           en Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
           en System.Threading.ThreadHelper.ThreadStart_Context(Object state)
           en System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
           en System.Threading.ThreadHelper.ThreadStart()

    que posible solución podrían ayudar con este error??


    viernes, 3 de junio de 2016 13:34

Respuestas

  • Para escribir en el archivo:

    using (StreamWriter sw = new StreamWriter(@"c:\ruta\fichero.txt"))
    {
        foreach (fila in ditioDondeEstenLasFilas)
        {
            sw.WriteLine(fila["campo1"]+","+fila["campo2"]+etc);
        }
    }
    

    Observa que no se necesita el StringBuilder, aunque si prefieres usarlo en lugar de concatenar los campos, entonces para inicializarlo a un tamaño dado se pone StringBuilder sb = newStringBuilder(tamaño).

    • Marcado como respuesta greg_dorian viernes, 3 de junio de 2016 22:24
    viernes, 3 de junio de 2016 17:21

Todas las respuestas

  • hola

    aconsejaria uses librerias como ser

    http://www.filehelpers.net/

    para procesar los datos del archivo y volcarlos a una lista, en este caso usarias una clase en donde indicarias las posiciones de cada dato

    no creo que usas ado.net con un datatable sea buena idea

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    viernes, 3 de junio de 2016 14:33
  • Hola leandro

    y en la clase como indico que posiciones estaria por que tengo entendido que solo se indica el tamaño del dato

    tienes algun ejemplo para guiarme con eso???

    viernes, 3 de junio de 2016 14:46
  • Estoy armando un archivo plano donde tengo 34 campos, en los campos 24 hay que dejar 255 espacios en blanco y en el campo 26 se colocan 2000 espacios en blanco, la consulta que realizo es de 148316 registros se llevan a un DataTable para recorrer cada fila en un foreach.

    ¿Has hecho las cuentas de cuánto suma todo eso? Son algo más de 8000 caracteres por registro, que multiplicado por el número de registros sale más de un gigabyte. Si lo cargas como Strings en .Net, y dado que cada carácter ocupa dos bytes, te saldrían más de dos gigabytes. Y si lo cargas en un DataTable, hay que añadir el "overhead" de los distintos objetos internos que va creando. En suma, consumirás bastante más de 2 gigabytes. En un sistema operativo de 64 bits sería factible (suponiendo que tu memoria virtual sea suficientemente grande), pero en 32 bits te pasas de la memoria direccionable por el programa y da el error de "Out of Memory".

    Yo recomendaría que NO lo cargues en un DataTable para recorrerlo con un foreach, sino que sustituyas el foreach por un while que vaya procesando las filas sobre la marcha según se leen del fichero, sin dejarlas guardadas en memoria. Si esto no es factible, y realmente necesitas el DataTable (por ejemplo, si hay que pegarle múltiples pasadas y se vuelve demasiado lento en caso de que las pasadas se las pegues al archivo en disco), entonces por lo menos suprime todos los espacios en blanco y carga solo la información "útil".

    viernes, 3 de junio de 2016 14:57
  • hola alberto

    si NO recomiendas usar un DataTable ... como? o que podría utilizar llevar el contenido del Datatable a un List<>?? y luego armar mi archivo con StringBuilder???

    StringBuilder TextPlano = new StringBuilder("");

    los espacios en blanco son necesarios porque asi los requiere la empresa que toma el archivo plano, no es un archivo plano separado con caracter especial, si no que el programa se encarga de leer linea y posicion del campo segun la linea.

    viernes, 3 de junio de 2016 15:42
  • Esto último que dices me despista. Al principio entendí que querías leer un archivo existente, pero ahora dices que los caracteres los requiere la empresa, así que se infiere que estás grabando un archivo, en lugar de leerlo. Y luego hablas de convertir el DataTable a un List<>. ¿Para qué? ¿Cuál es el objetivo? Es decir, no tiene sentido cargar por duplicado la información en memoria, en un datatable y también en un List. Se necesitará uno o el otro pero no ambos. Y en cualquier caso, ¿para qué se necesitan EN MEMORIA los espacios en blanco? Si estás grabando el archivo, el bucle que lo graba linea por linea va generando los espacios en la linea, no se necesita que estén cargados en una estructura en memoria. Y si se está leyendo, al leer se desprecian al leer cada linea y no se cargan en memoria. Y luego hablas de un StringBuilder. ¿Para qué? ¿No querrás cargar el archivo entero en un StringBuilder, verdad? Lo lógico, dado lo grande que es, es tratarlo linea por linea, no concatenar todas las lineas juntas en un StringBuilder.
    viernes, 3 de junio de 2016 15:58
  • hola alberto

    es leer de una base de datos y escribirlo a un archivo plano, asi como walter describe creo que es lo que me esta faltando limpiar el sb

    viernes, 3 de junio de 2016 16:07
  • Desde acá, el problema parece estar en el uso del stringbuilder no en el datatable.

    exacto!! walter  dos preguntas como escribo directamente al archivo ??? o como cargo cada 100 registros en el sb copiarlos y luego limpiar y reusar el sb??? 

    la segunda opcion me parece como la mas acertada Pero Como reasigno memoria Al StringBuilder?? 

    Tienes algun codigo de ejemplo?? o algun link de guia?

    viernes, 3 de junio de 2016 16:17
  • es leer de una base de datos y escribirlo a un archivo plano

    No intentes cargar primero toda la base de datos en un DataTable y luego volcar el DataTable al archivo. Tienes demasiados registros para eso.

    En su lugar, prescinde del DataTable y usa un DataReader. Esto es un pelín más costoso de programar que si se usa un datatable con un dataadapter, pero tiene la ventaja de que puedes procesar los registros uno por uno sin cargarlos todos a la vez en memoria.

    Según vayas iterando por los registros que te devuelve el SqlDataReader, simplemente ensambla lo que quieras escribir en el fichero a partir de los datos del registro, y escríbelo con un WriteLine en un StreamWriter. Puedes prescindir del StringBuilder, no se necesita, ya que el StreamWriter tiene su propio buffer interno que optimiza los accesos al disco, así que no pasa nada si lo llamas una vez por cada registro.

    viernes, 3 de junio de 2016 16:20
  • Para escribir en el archivo:

    using (StreamWriter sw = new StreamWriter(@"c:\ruta\fichero.txt"))
    {
        foreach (fila in ditioDondeEstenLasFilas)
        {
            sw.WriteLine(fila["campo1"]+","+fila["campo2"]+etc);
        }
    }
    

    Observa que no se necesita el StringBuilder, aunque si prefieres usarlo en lugar de concatenar los campos, entonces para inicializarlo a un tamaño dado se pone StringBuilder sb = newStringBuilder(tamaño).

    • Marcado como respuesta greg_dorian viernes, 3 de junio de 2016 22:24
    viernes, 3 de junio de 2016 17:21