txt texto separado segunda programa por para modificar lineas linea leer como comas codigo archivos archivo java nio java-io

texto - modificar archivos txt en java



Leer líneas de caracteres y obtener la posición del archivo (7)

El caso parece ser resuelto por VTD-XML, una biblioteca capaz de analizar rápidamente grandes archivos XML:

La última implementación de Java VTD-XML ximpleware, actualmente 2.13 http://sourceforge.net/projects/vtd-xml/files/vtd-xml/ proporciona algún código que mantiene un desplazamiento de bytes después de cada llamada al método getChar () de su IReader implementaciones

Las implementaciones de IReader para varias codificaciones de caracteres están disponibles dentro de VTDGen.java y VTDGenHuge.java

Las implementaciones de IReader se proporcionan para las siguientes codificaciones

ASCII; ISO_8859_1 ISO_8859_10 ISO_8859_11 ISO_8859_12 ISO_8859_13 ISO_8859_14 ISO_8859_15 ISO_8859_16 ISO_8859_2 ISO_8859_3 ISO_8859_4.
WIN_1250 WIN_1251 WIN_1252 WIN_1253 WIN_1254 WIN_1255 WIN_1256 WIN_1257 WIN_1258

Estoy leyendo líneas secuenciales de caracteres de un archivo de texto. La codificación de los caracteres en el archivo podría no ser de un solo byte.

En ciertos puntos, me gustaría obtener la posición del archivo en la que comienza la siguiente línea, para poder volver a abrir el archivo más tarde y regresar a esa posición rápidamente .

Preguntas

¿Hay una manera fácil de hacer ambas cosas, preferiblemente utilizando bibliotecas estándar de Java?

Si no, ¿cuál es una solución razonable?

Atributos de una solución ideal.

Una solución ideal podría manejar múltiples codificaciones de caracteres. Esto incluye UTF-8, en el que diferentes caracteres pueden representarse por diferentes números de bytes. Una solución ideal dependería principalmente de una biblioteca confiable y bien soportada. Lo más ideal sería la biblioteca estándar de Java. El segundo mejor sería una biblioteca de Apache o Google. La solución debe ser escalable. Leer todo el archivo en la memoria no es una solución. Regresar a una posición no debe requerir leer todos los caracteres anteriores en tiempo lineal.

Detalles

Para el primer requisito, BufferedReader.readLine() es atractivo. Pero el almacenamiento en búfer interfiere claramente con la obtención de una posición de archivo significativa.

Menos obvio, InputStreamReader también puede leer por adelantado, lo que interfiere con la obtención de la posición del archivo. De la documentación de InputStreamReader :

Para habilitar la conversión eficiente de bytes a caracteres, se pueden leer más bytes del flujo subyacente que los necesarios para satisfacer la operación de lectura actual.

El método RandomAccessFile.readLine() lee un solo byte por carácter .

Cada byte se convierte en un carácter tomando el valor del byte para los ocho bits más bajos del carácter y estableciendo los ocho bits más altos del carácter en cero. Por lo tanto, este método no admite el conjunto completo de caracteres Unicode.


Esta solución parcial se dirige solo a los archivos codificados con ASCII de 7 bits o UTF-8. Una respuesta con una solución general sigue siendo deseable (como lo es la crítica de esta solución).

En UTF-8:

  • Todos los caracteres de un solo byte se pueden distinguir de todos los bytes en caracteres de múltiples bytes. Todos los bytes en un carácter de múltiples bytes tienen un ''1'' en la posición de orden superior. En particular, los bytes que representan LF y CR no pueden formar parte de un carácter de múltiples bytes.
  • Todos los caracteres de un solo byte están en ASCII de 7 bits. Así que podemos decodificar un archivo que contiene solo caracteres ASCII de 7 bits con un decodificador UTF-8.

En conjunto, esos dos puntos significan que podemos leer una línea con algo que lee bytes, en lugar de caracteres, y luego descodificar la línea.

Para evitar problemas con el almacenamiento en búfer, podemos usar RandomAccessFile . Esa clase proporciona métodos para leer una línea y obtener / establecer la posición del archivo.

Aquí hay un bosquejo del código para leer la siguiente línea como UTF-8 usando RandomAccessFile.

protected static String readNextLineAsUTF8( RandomAccessFile in ) throws IOException { String rv = null; String lineBytes = in.readLine(); if ( null != lineBytes ) { rv = new String( lineBytes.getBytes(), StandardCharsets.UTF_8 ); } return rv; }

Luego, la posición del archivo se puede obtener del RandomAccessFile inmediatamente antes de llamar a ese método. Dado un RandomAccessFile referenciado por in :

long startPos = in.getFilePointer(); String line = readNextLineAsUTF8( in );


Inicialmente, encontré el enfoque sugerido por Andy Thomas ( https://.com/a/30850145/556460 ) el más apropiado.

Pero desafortunadamente no pude convertir la matriz de bytes (tomada de RandomAccessFile.readLine ) en una cadena correcta en los casos en que la línea del archivo contenga caracteres no latinos.

Así que volví a trabajar el enfoque al escribir una función similar a RandomAccessFile.readLine que recopila datos de la línea no a una cadena, sino a una matriz de bytes directamente, y luego construyo la cadena deseada a partir de la matriz de bytes. Así que el siguiente código a continuación satisfizo completamente mis necesidades (en Kotlin).

Después de llamar a la función, file.channel.position() devolverá la posición exacta de la siguiente línea (si existe):

fun RandomAccessFile.readEncodedLine(charset: Charset = Charsets.UTF_8): String? { val lineBytes = ByteArrayOutputStream() var c = -1 var eol = false while (!eol) { c = read() when (c) { -1, 10 -> eol = true // /n 13 -> { // /r eol = true val cur = filePointer if (read() != ''/n''.toInt()) { seek(cur) } } else -> lineBytes.write(c) } } return if (c == -1 && lineBytes.size() == 0) null else java.lang.String(lineBytes.toByteArray(), charset) as String }


Si construyes un BufferedReader desde un FileReader y mantienes una instancia de FileReader accesible a tu código, deberías poder obtener la posición de la siguiente línea llamando al:

fileReader.getChannel().position();

después de una llamada a bufferedReader.readLine() .

El BufferedReader podría construirse con un búfer de entrada de tamaño 1 si está dispuesto a intercambiar ganancias de rendimiento por precisión posicional.

Solución alternativa ¿Cuál sería el error de realizar un seguimiento de los bytes usted mismo?

long startingPoint = 0; // or starting position if this file has been previously processed while (readingLines) { String line = bufferedReader.readLine(); startingPoint += line.getBytes().length; }

esto le daría un recuento de bytes preciso a lo que ya ha procesado, independientemente de la marca o el almacenamiento en búfer subyacente. Tendrías que tener en cuenta los finales de línea en tu cuenta, ya que se eliminan.


Yo sugeriría java.io.LineNumberReader . Puede establecer y obtener el número de línea y, por lo tanto, continuar en un determinado índice de línea.

Ya que es un BufferedReader , también es capaz de manejar UTF-8.


RandomAccessFile tiene una función: buscar (pos larga) Establece el desplazamiento del puntero del archivo, medido desde el principio de este archivo, en el que se produce la siguiente lectura o escritura.


Solución A

  1. Use RandomAccessFile.readChar() o RandomAccessFile.readByte() en un bucle.
  2. Verifique los caracteres de su EOL, luego procese esa línea.

El problema con cualquier otra cosa es que deberías asegurarte de no leer nunca más allá del carácter EOL.

readChar () devuelve un carácter no un byte. Así que no tienes que preocuparte por el ancho del personaje.

Lee un carácter de este archivo. Este método lee dos bytes del archivo, comenzando en el puntero del archivo actual.

[...]

Este método bloquea hasta que se leen los dos bytes, se detecta el final de la secuencia o se lanza una excepción.

Al usar un RandomAccessFile y no un Reader, está renunciando a la capacidad de Java para decodificar el conjunto de caracteres en el archivo por usted. Un BufferedReader lo haría automáticamente.

Hay varias maneras de superar esto. Una es detectar la codificación usted mismo y luego usar el método de lectura * () correcto. La otra forma sería usar un flujo de BoundedInput.

Hay uno en esta pregunta, Java: leer cadenas de un archivo de acceso aleatorio con entrada en buffer.

Por ejemplo, https://.com/a/4305478/16549