在Java中解析固定宽度格式文件的最佳方法是什么?

我有一个来自供应商的文件,每行有115个固定宽度的字段。 将该文件解析为115个字段的最佳方法是什么,以便我可以在我的代码中使用它们?

我的第一个想法是为NAME_START_POSITIONNAME_LENGTH等每个字段创建常量并使用substring 。 这看起来很难看,所以我很好奇是否还有其他推荐方法可以做到这一点。 谷歌搜索出现的几个图书馆似乎都没有。 谢谢

我会使用扁平文件解析器,如扁虫,而不是重新发明轮子:它有一个干净的API,使用简单,具有良好的error handling和简单的文件格式描述符。 另一种选择是jFFP,但我更喜欢第一种。

我已经使用fixedformat4j玩了它,它非常好。 易于配置转换器等。

uniVocity-parsers附带一个FixedWidthParserFixedWidthWriter ,它可以支持棘手的固定宽度格式,包括具有不同字段,填充等的行。

 // 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"))); 

以下是解析各种固定宽度输入的一些示例 。

以下是一些其他用于写入的示例以及特定于固定宽度格式的其他固定宽度示例 。

披露:我是这个库的作者,它是开源和免费的(Apache 2.0许可证)

这是我使用的基本实现:

 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); } } } }

最适合Scala,但可能你可以在Java中使用它

我已经厌倦了这样一个事实:我没有创建自己的固定长度格式的适当库。 你可以在这里查看: https : //github.com/atais/Fixed-Length

一个基本用法是创建一个case类,它被描述为一个HList (无形):

 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] } 

您现在可以轻松解码您的线条或编码您的对象:

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

Apache Commons CSV项目可以处理已修复的文件。

看起来固定宽度function无法从沙箱中升级。

这是读取fixedwidth文件的普通java代码:

 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 } } 

您可以使用\t+作为分隔符。

尝试类似的东西

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

如果您的字符串被称为inStr ,将其转换为char数组并使用String(char[], start, length)构造函数

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