¿Cuál es la mejor manera de analizar un archivo con formato de ancho fijo en Java?

Tengo un archivo de un proveedor que tiene 115 campos de ancho fijo por línea. ¿Cuál es la mejor manera de analizar ese archivo en los 115 campos para poder usarlos en mi código?

Mi primer pensamiento es solo hacer constantes para cada campo como NAME_START_POSITION y NAME_LENGTH y usar NAME_LENGTH . Eso simplemente parece feo, así que tengo curiosidad si hay otras formas recomendadas de hacerlo. Ninguna de las dos bibliotecas que aparecieron en la búsqueda de Google pareció mejor. Gracias

Usaría un analizador de archivos sin formato como el gusano plano en lugar de reinventar la rueda: tiene una API limpia, es fácil de usar, tiene un manejo de errores decente y un descriptor de formato de archivo simple. Otra opción es jFFP, pero prefiero la primera.

He jugado arround con fixedformat4j y es bastante agradable. Conversores fáciles de configurar y similares.

UniVocity-parsers viene con FixedWidthParser y FixedWidthWriter , que admite formatos complicados de ancho fijo, incluidas líneas con diferentes campos, paddings, etc.

 // creates the sequence of field lengths in the file to be parsed FixedWidthFields fields = new FixedWidthFields(4, 5, 40, 40, 8); // creates the default settings for a fixed width parser FixedWidthParserSettings settings = new FixedWidthParserSettings(fields); // many settings here, check the tutorial. //sets the character used for padding unwritten spaces in the file settings.getFormat().setPadding('_'); // creates a fixed-width parser with the given settings FixedWidthParser parser = new FixedWidthParser(settings); // parses all rows in one go. List allRows = parser.parseAll(new File("path/to/fixed.txt"))); 

Aquí hay algunos ejemplos para analizar todo tipo de entradas de ancho fijo.

Y aquí hay algunos otros ejemplos para escribir en general y otros ejemplos de ancho fijo específicos para el formato de ancho fijo.

Divulgación: soy el autor de esta biblioteca, es de código abierto y gratuito (licencia Apache 2.0)

Aquí hay una implementación básica que uso:

 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; public class FlatFileParser { public static void main(String[] args) { File inputFile = new File("data.in"); File outputFile = new File("data.out"); int columnLengths[] = {7, 4, 10, 1}; String charset = "ISO-8859-1"; String delimiter = "~"; System.out.println( convertFixedWidthFile(inputFile, outputFile, columnLengths, delimiter, charset) + " lines written to " + outputFile.getAbsolutePath()); } /** * Converts a fixed width file to a delimited file. * 

* This method ignores (consumes) newline and carriage return * characters. Lines returned is based strictly on the aggregated * lengths of the columns. * * A RuntimeException is thrown if run-off characters are detected * at eof. * * @param inputFile the fixed width file * @param outputFile the generated delimited file * @param columnLengths the array of column lengths * @param delimiter the delimiter used to split the columns * @param charsetName the charset name of the supplied files * @return the number of completed lines */ public static final long convertFixedWidthFile( File inputFile, File outputFile, int columnLengths[], String delimiter, String charsetName) { InputStream inputStream = null; Reader inputStreamReader = null; OutputStream outputStream = null; Writer outputStreamWriter = null; String newline = System.getProperty("line.separator"); String separator; int data; int currentIndex = 0; int currentLength = columnLengths[currentIndex]; int currentPosition = 0; long lines = 0; try { inputStream = new FileInputStream(inputFile); inputStreamReader = new InputStreamReader(inputStream, charsetName); outputStream = new FileOutputStream(outputFile); outputStreamWriter = new OutputStreamWriter(outputStream, charsetName); while((data = inputStreamReader.read()) != -1) { if(data != 13 && data != 10) { outputStreamWriter.write(data); if(++currentPosition > (currentLength - 1)) { currentIndex++; separator = delimiter; if(currentIndex > columnLengths.length - 1) { currentIndex = 0; separator = newline; lines++; } outputStreamWriter.write(separator); currentLength = columnLengths[currentIndex]; currentPosition = 0; } } } if(currentIndex > 0 || currentPosition > 0) { String line = "Line " + ((int)lines + 1); String column = ", Column " + ((int)currentIndex + 1); String position = ", Position " + ((int)currentPosition); throw new RuntimeException("Incomplete record detected. " + line + column + position); } return lines; } catch (Throwable e) { throw new RuntimeException(e); } finally { try { inputStreamReader.close(); outputStreamWriter.close(); } catch (Throwable e) { throw new RuntimeException(e); } } } }

El más adecuado para Scala, pero probablemente podrías usarlo en Java

Estaba tan harto de que no haya una biblioteca adecuada para el formato de longitud fija que haya creado la mía. Puede consultarlo aquí: https://github.com/atais/Fixed-Length

Un uso básico es que creas una clase de caso y se describe como HList (Shapeless):

 case class Employee(name: String, number: Option[Int], manager: Boolean) object Employee { import com.github.atais.util.Read._ import cats.implicits._ import com.github.atais.util.Write._ import Codec._ implicit val employeeCodec: Codec[Employee] = { fixed[String](0, 10) <<: fixed[Option[Int]](10, 13, Alignment.Right) <<: fixed[Boolean](13, 18) }.as[Employee] } 

Y ahora puede decodificar fácilmente sus líneas o codificar su objeto:

 import Employee._ Parser.decode[Employee](exampleString) Parser.encode(exampleObject) 

El proyecto Apache Commons CSV puede manejar archivos fijos.

Parece que la funcionalidad de ancho fijo no sobrevivió a la promoción del sandbox.

Aquí está el código Java simple para leer el archivo de ancho fijo:

 import java.io.File; import java.io.FileNotFoundException; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.Arrays; import java.util.List; public class FixedWidth { public static void main(String[] args) throws FileNotFoundException, IOException { // String S1="NHJAMES TURNER M123-45-67890004224345"; String FixedLengths = "2,15,15,1,11,10"; List items = Arrays.asList(FixedLengths.split("\\s*,\\s*")); File file = new File("src/sample.txt"); try (BufferedReader br = new BufferedReader(new FileReader(file))) { String line1; while ((line1 = br.readLine()) != null) { // process the line. int n = 0; String line = ""; for (String i : items) { // System.out.println("Before"+n); if (i == items.get(items.size() - 1)) { line = line + line1.substring(n, n + Integer.parseInt(i)).trim(); } else { line = line + line1.substring(n, n + Integer.parseInt(i)).trim() + ","; } // System.out.println( // S1.substring(n,n+Integer.parseInt(i))); n = n + Integer.parseInt(i); // System.out.println("After"+n); } System.out.println(line); } } } } 
 /*The method takes three parameters, fixed length record , length of record which will come from schema , say 10 columns and third parameter is delimiter*/ public class Testing { public static void main(String as[]) throws InterruptedException { fixedLengthRecordProcessor("1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10", 10, ","); } public static void fixedLengthRecordProcessor(String input, int reclength, String dilimiter) { String[] values = input.split(dilimiter); String record = ""; int recCounter = 0; for (Object O : values) { if (recCounter == reclength) { System.out.println(record.substring(0, record.length() - 1));// process // your // record record = ""; record = record + O.toString() + ","; recCounter = 1; } else { record = record + O.toString() + ","; recCounter++; } } System.out.println(record.substring(0, record.length() - 1)); // process // your // record } } 

Puede usar \t+ como su delimitador.

Pruebe algo como

 String fields[] = line.split("\t+"); 

Si su cadena se llama inStr , inStr a una matriz char y use el constructor String(char[], start, length)

 char[] intStrChar = inStr.toCharArray(); String charfirst10 = new String(intStrChar,0,9); String char10to20 = new String(intStrChar,10,19);