Каков наилучший способ разбора форматированного файла с фиксированной шириной на Java?
У меня есть файл от поставщика, который имеет 115 полей фиксированной ширины в строке. Какой лучший способ разбора этого файла в 115 полей, чтобы я мог использовать их в своем коде?
Моя первая мысль - просто создать константы для каждого поля типа NAME_START_POSITION
и NAME_LENGTH
и используя substring
. Это просто кажется уродливым, поэтому мне любопытно, есть ли другие рекомендуемые способы сделать это. Ни одна из двух библиотек, поиск Google не показался лучше. Благодаря
Ответы
Ответ 1
Вместо изобретения колеса я бы использовал анализатор плоских файлов, такой как flatworm: он имеет чистый API, прост в использовании, имеет приличную обработку ошибок и простой дескриптор формата файла. Другой вариант - jFFP, но я предпочитаю первый.
Ответ 2
Я играл в arround с fixedformat4j, и это довольно приятно. Простота настройки преобразователей и т.п.
Ответ 3
uniVocity-parsers поставляется с FixedWidthParser
и FixedWidthWriter
могут поддерживать сложные форматы фиксированной ширины, включая строки с различными полями, отступы и т.д.
// 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<String[]> allRows = parser.parseAll(new File("path/to/fixed.txt")));
Вот несколько примеров для анализа всех видов входных данных фиксированной ширины.
А вот еще несколько примеров для написания в общем и других примеров фиксированной ширины, специфичных для формата фиксированной ширины.
Раскрытие информации: я являюсь автором этой библиотеки, она с открытым исходным кодом и бесплатная (лицензия Apache 2.0)
Ответ 4
Вот базовая реализация, которую я использую:
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.
* <p>
* 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);
}
}
}
}
Ответ 5
Наиболее подходит для Scala, но, вероятно, вы можете использовать его в Java
Мне так надоело, что нет подходящей библиотеки для формата фиксированной длины, который я создал самостоятельно. Вы можете проверить это здесь: https://github.com/atais/Fixed-Length
Основное использование заключается в том, что вы создаете класс case и описывается как 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]
}
И вы можете легко декодировать свои строки сейчас или закодировать свой объект:
import Employee._
Parser.decode[Employee](exampleString)
Parser.encode(exampleObject)
Ответ 6
Проект Apache Commons CSV может обрабатывать файлы с фиксированными файлами.
Похоже, что функция фиксированной ширины не выдержала продвижение по песочнице.
Ответ 7
Вот простой 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<String> 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);
}
}
}
}
Ответ 8
/*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
}
}
Ответ 9
Если ваша строка называется inStr
, преобразуйте ее в массив char и используйте конструктор String(char[], start, length)
char[] intStrChar = inStr.toCharArray();
String charfirst10 = new String(intStrChar,0,9);
String char10to20 = new String(intStrChar,10,19);
Ответ 10
Другая библиотека, которая может использоваться для анализа источника текста фиксированной ширины: https://github.com/org-tigris-jsapar/jsapar
Позволяет вам определять схему в xml или в коде и анализировать текст фиксированной ширины в Java-бины или извлекать значения из внутреннего формата.
Ответ 11
Вы можете использовать \t+
в качестве разделителя.
Попробуйте что-то вроде
String fields[] = line.split("\t+");