Llenar lineas

Tengo un pequeño programa que lee las lineas de un archivo .txt y me manda la longitud de cada linea, ¿Como puedo hacer para que el mismo programa me acomplete todas las lineas a la misma longitud, en este caso que sea la maxima?

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de Marce

Es una muy buena pregunta,

Es una muy buena pregunta, aquí el reto no es cómo obtener la longitud máxima, sino más bien cómo obtener esa longitud sin consumir tantos recursos para posteriormente rellenar cada línea a la longitud máxima. Imaginen un archivo con 10,000 líneas y cada una obviamente con diferente longitud.

Es un buen reto, claro quizá alguién ya lo tenga y estaría genial que lo compartiera, pero creo este es otro de los problemas que se pueden resolver por multiples caminos.

Por lo pronto como algo introductorio sería obtener la longitud de cáda línea del archivo y compararla con las anteriores algo así como un método de búsqueda, quizá me este llendo a lo más básico pero sería algo más optimizable es solo la primer idea. Otra pregunta sería si desde que se escribe en el archivo se pueden rellenar las líneas con la longitud máxima. Creo que manos a la obra :p jeje

Imagen de ezamudio

dos pasada

Forzosamente hay que leer el archivo dos veces, es imposible hacerlo en una sola pasada.

Pero puedes optimizar la primera lectura, aunque se complica la programación; en vez de leer Strings con un BufferedReader usando el método readLine, tendrías que leer el archivo como stream de bytes, leyendo fragmentos del archivo a un mismo arreglo siempre, y analizando esos bytes que leíste para determinar la longitud de cada línea (para lo cual tienes que identificar el cambio de línea). De esa manera puedes leer un archivo de 100mil líneas de texto que mida varios megabytes, usando sólo unos pocos KB de memoria (el tamaño del arreglo de bytes que uses para leerlo; entre más pequeño sea, más lecturas harás a disco y por lo tanto será más lento, por lo que es importante escoger un tamaño lo suficientemente grande pero no demasiado).

Y pues en la segunda pasada ya podrías leer linea por linea con un BufferedReader... pero si ya hiciste todo el código para leer los fragmentos de archivo e identificar los cambios de línea (con lo que ya pudiste contar la longitud de la última línea y compararla contra la longitud más grande que has encontrado hasta el momento), por qué no reutilizarlo? Puedes volver a leer el archivo de la misma forma, la diferencia es que ahora vas escribiendo a otro archivo, contando los bytes que has escrito de la última línea, y cuando llegue el salto de línea, le agregas los espacios que falten (porque ya conoces la longitud máxima). De ese modo puedes generar el archivo con las líneas de la misma longitud, usando muy poca memoria.

Es uno de esos casos en que para hacer más eficaz el proceso, tienes que teclear más código, no menos.

Imagen de Marce

optimiza

Lo que comenta @ezamudio es muy cierto aquí el chiste es optimizar el algoritmo que va a leer el archivo ya sea para obtener la longitud máxima como para rellenar las líneas con dicha longitud. Estaba apunto de empezar con una pequeña implementación de esto cuando me tope con esto que es prácticamente lo que menciona @ezamudio, aún no evalúo el código para saber si es verdad lo que comenta en el tiempo de ejecución pero viendo a simple vista el código suena bastante convincente. Pruébalo y sigue el caminito que ya se te indicó, como ves no hay de otra que darle 2 pasadas al archivo el chiste es optimizar la lectura para el caso en el que te topes con un archivo muy pesado.

Imagen de Marce

probado

Bien pues probé el algoritmo que viene en la liga que te pasé y para un archivo de 250 MB tarda 2.018 segundos en leer y contar cuantas líneas hay :) creo ques es buen rendimiento y realmente dudo que llegues a leer 250 MB pero no está de más.

Imagen de ezamudio

1024, tiempos

Espero que hayas medido el tiempo justo desde antes de invocar ese método y luego justo después de la invocación. Si lo mides de lo que se tarda en correr el programa completo, estás midiendo el tiempo que tarda en levantar la JVM y en lo que el JIT compila el código y eso no deberías tomarlo en cuenta (porque no es optimizable por ti).

Si ajustas el tamaño de ese buffer para que por ejemplo en vez de ser 1024 sea 16384 (16KB), o incluso 65536 (64KB) seguramente va a tardar menos de los 2 segundos. Aunque hay que modificar el código para que cuente la longitud de cada línea, en vez de contar el número de líneas... tiene su chiste, porque tienes que considerar el caso de que el buffer se terminó a la mitad de una línea.

Imagen de ezamudio

Kata

Me gustó el ejercicio como para coding kata. Aquí dejo el código para la primera pasada, en donde se determina la longitud de la línea más larga. Para practicar en un lenguaje que no domine, lo hice en Scala, pero pues el algoritmo es tan básico que que no hay mucha diferencia (seguramente alguien podrá dar una versión más scalesca):

 

Imagen de Marce

Uy pues ya le diste una

Uy pues ya le diste una avanzada muy buena de hecho lo que se podría decir era lo más complicado. Y en cuanto al tiempo que calculé efectivamente tomé el tiempo antes de empezar con el algoritmo y después de terminar. Creo que es un buen rendimiento. Aún no se mucho de Scala pero sin duda no se me hizo tan dificil interpretar lo que haces, será un buen momento para probar Scala :p

Listo!!!

Si quedo el programia, llenando las lineas con espacios en blanco, las pruebas las hice con un archivo pequeño, ya mas adelante modificare el programa para que lea un archivo que tengo de 672kb
 

la maxima longitud que ocupe

la maxima longitud que ocupe fue de 200.
Ahora mi duda es, ¿como valido los primeros 2 caracteres de cada linea?
si una linea empieza con HD(ejemplo) que haga algo y si no que haga otra cosa.

Imagen de Marce

La forma en la que rellenas

La forma en la que rellenas cada línea creo que puede mejorar un poco, para mi es mejor que esa condición que tienes en el while la saques y saques la diferencia (resta) entre tu longitud máxima (200) y el length de la línea que leíste algo como;
 
Así evitas tener que ir por el length cada vez que le agregas un espacio y hacer esa comparación, si ya sabes cual es la longitud límite mejor sacar cuantos espacios debes agregarle y meter eso en un ciclo.

Me imagino que estás leyendo un tipo de layout en el que no tienes Caracteres de separación, algo como:
  y solo te basas en el número de posiciones que debe tener cada valor. Para que llenar entonces cada línea del archivo con espacios si ya sabes las posiciones en las que vas a tomar cada valor? Es simple curiosidad probablemente esto sea parte de algo más grande, o yo estoy confundida con lo que hiciste :p jiji

Imagen de ezamudio

optimizaciones

Primero: Para que hagas cosas distintas según el inicio de la línea, pues simplemente  .

En cuanto a la manera en que rellenas las líneas: primero, es mejor usar StringBuilder que StringBuffer, porque StringBuilder no es thread-safe y en este caso no compartes el StringBuffer con ningún otro hilo, de modo que te da mejor rendimiento StringBuilder. Y esto utiliza muchísima memoria:

 

Porque conviertes a String en cada iteración, lo cual es innecesario porque StringBuffer (y StringBuilder) response a   sin necesidad de que le hagas nada. Y si estás agregando un espacio a la vez es mejor que uses ' ' (un char) en vez de " " (un String).

Segundo, ni siquiera es necesario que utilices el StringBuilder. Si lo que necesitas al final es que el archivo generado tenga todas las líneas de la misma longitud, por qué le agregas espacios a la línea en memoria? Simplemente lees la línea como un String (como ya lo haces), escribes ese string al archivo de salida (sin corte de línea), luego escribes a ese mismo archivo de salida tantos espacios como te hagan falta para completar su longitud, y luego escribes el saldo de línea.

Otra: Es mejor simplemente   que  .

Algo así, escribiendo a STDOUT las lineas convertidas:

 

Lleno todas las lineas con

Lleno todas las lineas con espacios en blanco, para que queden de un mismo tamaño, por que despues voy a ocupar una herramienta para transformar ese archivo en otro formato.

Imagen de Marce

Muy Bien

Bien pues ya te contestaron de manera completa yo me fijé más en como rellenabas las líneas y realmente es muy cierto lo de StringBuffer y la solución dada es mucho más optima ;)
Por cierto @ezamudio noté que hiciste el ciclo para llenar las líneas con espacios con un while y te ahorraste el contador por así decirlo, me pareció muy buen detalle, supongo que siempre va a ser más rápido un while que un for y sobre todo en la forma en que lo implementaste.

Imagen de ezamudio

while

Si te refieres a esto:

 

Lo que pasa es que ahí incremento la variable   y la comparo contra el máximo. De hecho creo que está mal ese código porque si faltara un solo caracter, creo que no entra en la condición y podría haber líneas con un espacio menos de lo necesario. Son las broncas de hacer código al aventón y de que el campo de comentarios no tenga compilador integrado.

Gracias por sus comentarios y

Gracias por sus comentarios y propuestas, ya quedo listo con modificaciones y observaciones que lei aqui.

Imagen de Marce

Bueno, como todo sabemos que

Bueno, como todo sabemos que lo que se expresan son ideas, si realmente quisieramos poner código 100% funcional compilado y en producción (jajaja en producción) sería distinto, aunque si es bueno aclarar que todos son ejemplos que se deben pulir. A final de cuentas el que lo lee debe saber que se le dice el camino y no el código tal cual para copiar y pegar, sería un muy grave error si haces eso, es mejor siempre revisar ;) y pues de nada, aunque @ezamudio se lleva los créditos xD

Imagen de rodrigo salado anaya

Re-LLenar Lineas...

La verdad es que solo le di un vistazo a los otro comentarios de Marce y Ezamudio, pero me gusto el ejercicio, modifique lo que pides y queda de la siguiente manera:
La longitud maxima de una linea la deje como 20 caracteres; sin contar las '|' inicial y final.
El txt de entrada es :
 
El de salida es:
 

Código:
 
Saludos, y ojala te sirba de algo : D.

Seguramente alguien podrá dar

Seguramente alguien podrá dar una versión más scalesca...

Challenge accepted...

uhmm.... "tick, tick, tack, tick, tack..." mmmhhh...

( varios minutos después )

No pues la verdad no me sale pero sé de cierto que esto se puede escribir escribiendo en un estilo funcional ( ya ven como aquello de OOP en Scala es solo un gancho? ) .

Así que pregunté y aquí va la respuesta

 

  •   regresa un  
  •   ( Alguien ha oído aquello de map/reduce? ) Se puede pensar como una función que recibe otra función que va a aplicar a todo un conjunto y va a regresar otro conjunto.
  •   después se regresa el máximo.

Lo único que hay que notar aquí claro, es que este código es menos eficiente que el original, porque hace más pasadas para aplicar el map y luego el máximo

Otra opción fue:

 

Que luce más eficiente, pero ahí les dejo de tarea averiguar que tanto es más o menos eficiente.

update

Según agregaron en los comentarios, estos dos métodos iteran el archivo una sola vez, ¿será?

Imagen de ezamudio

performance

Sé que existen esas funciones de conveniencia, pero no las usé a propósito. Tan es así que ni siquiera usé BufferedReader.readLine que hubiera sido lo más fácil. Groovy y Scala ambos tienen algunos métodos y clases de conveniencia para leer todo el archivo de un golpe y separarlo en líneas. La discusión al principio era cómo hacer el código para que funcionara con archivos de 10 líneas de 10 caracteres y también con archivos de un millón de líneas de miles de caracteres cada una... al menos algo así mencioné antes de empezar a postear código.