none
Optimizar proceso de recorte cadena de texto RRS feed

  • Pregunta

  • Estimados,

      Tengan muy buenos días (Al menos acá en Chile), les comento que tenemos un proceso que hasta hace poco funcionaba super, el cual toma un archivo y lee una única línea de texto que contiene 30.000 registros y 350 caracteres por cada registro, se demora aproximadamente 5 minutos.

       Todo bien, hasta hace una semana que comenzamos a procesar cerca de 50.000 registros y se demoro cerca de 10 minutos, mi problema radica que hicimos pruebas con 80.000 registros(los esperados con este volumen para el dia de cierre de mes) y el proceso se demorará como 30 minutos, lo que es demasiado, imagino que el tema debe estar en el manejo de memoria y proceso de validación, además por si fuera poco se espera que a mediados de año se procesen 80.000 registros diarios con pic de 150.000 lo que tendría como 2 horas para el cierre.

    el código es el siguiente:

                Dim objReader As New System.IO.StreamReader(txtruta.text)
                Dim Archivo As New System.Text.StringBuilder
                dim LineaCompleta as string=objReader.ReadLine()
                objReader.Close()
                objReader.Dispose()
                objReader = Nothing

    //validaciones... rápidas de cantidad de caracteres y que no existan mas lineas, etc

    dim NewArchivo as New System.IO.StreamWriter(txtDestino.text)

    dim linea as string, nuevalinea as string 

    dim posAct as integer=1

    dim Largo as Integer=350

    dim siguiente as boolean=true

    DO

       nuevalinea=""

       linea=mid(LineaCompleta, posAct, Largo)

       posAct=posAct + Largo

       for carac as integer=0 to linea.length-1

            pasoCaracter=mid(Linea,carac,1)

           if ValidaCaracter(pasoCaracter)=true then

                nuevalinea=nuevalinea & pasoCaracter

          else

                nuevalinea=nuevalinea + " "

           endif

       Next

        NewArchivo.writeline(Nuevalinea)

    UNTIL (siguiente=false)

    newArchivo.close()

    algo así es el código, funciona pero al parecer con el aumento de registros el tiempo es exponencial y me preocupa.

    De antemano muchas gracias :)

    martes, 10 de enero de 2017 15:16

Respuestas

  • Ricardo Peña Diaz,

    Hice una pequeña prueba con un archivo conteniendo 5 millones de caracteres (algo de 15000 registros) y el proceso duro algo menos de un segundo, claro que mucho dependerá del equipo donde se realice la operación, te dejo el código que he escrito:

    Private Function PartirCadena(Cadena As String, Longitud As Integer) As IEnumerable(Of String)
    
    	Return Enumerable.Range(0, Cadena.Length / Longitud).
    		Select(Function(i) Cadena.Substring(i * Longitud, Longitud))
    
    End Function
    
    Private Sub btnPartirCadena_Click(sender As Object, e As EventArgs)
    
    	Dim Linea As String() = File.ReadAllLines("D:\ArchivoLargo.txt", Encoding.[Default])
    
    	File.WriteAllLines("D:\NuevoArchivoLargo.txt", PartirCadena(Linea(0), 350).ToArray())
    
    End Sub

     

    Donde 'D:\ArchivoLargo.txt' es la ruta del archivo que contiene el texto a partir y 'D:\NuevoArchivoLargo.txt' es el archivo que se creará con las subcadenas. En caso -y entiendo por un tema de recursos de hardware- experimentes un menor rendimiento en caso el tamaño de la cadena incremente puedes modificar el código para que la operación se realice por partes, por ejemplo cada 30000 registros.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    martes, 10 de enero de 2017 17:09

Todas las respuestas

  • Ricardo Peña Diaz,

    Una duda, ¿la línea contiene algún separador entre "registros" como por ejemplo una coma, un espacio en blanco?

    ¿Qué haces con los registros recuperados? ¿los contienes en algún medio?


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    martes, 10 de enero de 2017 15:27
  • Ricardo Peña Diaz,

    Una duda, ¿la línea contiene algún separador entre "registros" como por ejemplo una coma, un espacio en blanco?

    ¿Qué haces con los registros recuperados? ¿los contienes en algún medio?


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    Williams, muchas gracias por tu interés, lamentablemente no existe caracter delimitador, terminan los 350 y parte altiro el siguiente registro  y pueden ser alfanuméricos. sobre lo que se hace con los registros recuperados, se dejan en un archivo lineal, el cual después lo toma una malla que hace el cruce con la data que tenemos en nuestro SQL Server. 
    martes, 10 de enero de 2017 15:44
  • Hola.

    Te recomiendo que trabajes con  System.Text.StringBuilder

    Dim linea As New System.Text.StringBuilder
    Dim nuevalinea As New System.Text.StringBuilder
    nuevalinea.Append(pasoCaracter)

    mejoras en rendimiento


    Juan,

       Yo creo que el problema no esta en esa lectura, mas bien se presenta al recorrer y procesar los cortes de registro y generar el nuevo archivo de N lineas. además imagino que no debe ser muy óptimo el convertir el StringBuilder a texto con el .tostring para obtener cada linea, me cuentas tus apreciaciones por favor.

    martes, 10 de enero de 2017 15:46
  • Ricardo Peña Diaz,

    Hice una pequeña prueba con un archivo conteniendo 5 millones de caracteres (algo de 15000 registros) y el proceso duro algo menos de un segundo, claro que mucho dependerá del equipo donde se realice la operación, te dejo el código que he escrito:

    Private Function PartirCadena(Cadena As String, Longitud As Integer) As IEnumerable(Of String)
    
    	Return Enumerable.Range(0, Cadena.Length / Longitud).
    		Select(Function(i) Cadena.Substring(i * Longitud, Longitud))
    
    End Function
    
    Private Sub btnPartirCadena_Click(sender As Object, e As EventArgs)
    
    	Dim Linea As String() = File.ReadAllLines("D:\ArchivoLargo.txt", Encoding.[Default])
    
    	File.WriteAllLines("D:\NuevoArchivoLargo.txt", PartirCadena(Linea(0), 350).ToArray())
    
    End Sub

     

    Donde 'D:\ArchivoLargo.txt' es la ruta del archivo que contiene el texto a partir y 'D:\NuevoArchivoLargo.txt' es el archivo que se creará con las subcadenas. En caso -y entiendo por un tema de recursos de hardware- experimentes un menor rendimiento en caso el tamaño de la cadena incremente puedes modificar el código para que la operación se realice por partes, por ejemplo cada 30000 registros.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    martes, 10 de enero de 2017 17:09
  • Ricardo Peña Diaz,

    Hice una pequeña prueba con un archivo conteniendo 5 millones de caracteres (algo de 15000 registros) y el proceso duro algo menos de un segundo, claro que mucho dependerá del equipo donde se realice la operación, te dejo el código que he escrito:

    Private Function PartirCadena(Cadena As String, Longitud As Integer) As IEnumerable(Of String)
    
    	Return Enumerable.Range(0, Cadena.Length / Longitud).
    		Select(Function(i) Cadena.Substring(i * Longitud, Longitud))
    
    End Function
    
    Private Sub btnPartirCadena_Click(sender As Object, e As EventArgs)
    
    	Dim Linea As String() = File.ReadAllLines("D:\ArchivoLargo.txt", Encoding.[Default])
    
    	File.WriteAllLines("D:\NuevoArchivoLargo.txt", PartirCadena(Linea(0), 350).ToArray())
    
    End Sub

     

    Donde 'D:\ArchivoLargo.txt' es la ruta del archivo que contiene el texto a partir y 'D:\NuevoArchivoLargo.txt' es el archivo que se creará con las subcadenas. En caso -y entiendo por un tema de recursos de hardware- experimentes un menor rendimiento en caso el tamaño de la cadena incremente puedes modificar el código para que la operación se realice por partes, por ejemplo cada 30000 registros.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.

    Genial, acabo de hacer las pruebas y los 75.000 registros los proceso en 2 segundos... es lo que buscaba y no un proceso cíclico, ahora lo único que me queda es saber como puedo validar los caracteres? alguna idea para realizar la validación solo debe permitir una lista de caracteres ascii, que son números, letras y algún otro carácter.

    de verdad muchas gracias.

    martes, 10 de enero de 2017 20:06
  • Ricardo Peña Diaz,

    No tengo claro la forma de validación que quieres implementar, ¿es para filtrar los registros?, ¿es sobre la línea?.

    Te agradeceré puedas poner un ejemplo claro de lo quieres realizar para tener una idea mas amplia y hacerte llegar una propuesta.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    martes, 10 de enero de 2017 20:52
  • Ricardo Peña Diaz,

    No tengo claro la forma de validación que quieres implementar, ¿es para filtrar los registros?, ¿es sobre la línea?.

    Te agradeceré puedas poner un ejemplo claro de lo quieres realizar para tener una idea mas amplia y hacerte llegar una propuesta.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.

    William,

      Gracias nuevamente por el interes, te comento que implemente una ruta de validación en esta llamada

    Return Enumerable.Range(0, Cadena.Length / Longitud).
    		Select(Function(i) Cadena.Substring(i * Longitud, Longitud))
    

    y quedo

    Return Enumerable.Range(0, Cadena.Length / Longitud).
    		Select(Function(i) ReemplazarCaracteres(Cadena.Substring(i * Longitud, Longitud)))

    la rutina reemplazarCaracteres tomaba cada carácter y valida que sea uno válido, letras, números y un par de signos, los demás los reemplaza por " ", las pruebas me dieron solo 4 segundos en el proceso completo, por lo que vamos a realizar unas últimas pruebas y debería quedar así, de verdad muchas gracias, me diste una gran ayuda!!

    miércoles, 11 de enero de 2017 13:55