web-dev-qa-db-ja.com

Javaで固定幅フォーマットのファイルを解析する最良の方法は何ですか?

1行に115個の固定幅フィールドがあるベンダーからのファイルがあります。そのファイルを115フィールドに解析して、コードで使用できるようにするための最良の方法は何ですか?

私の最初の考えは、NAME_START_POSITIONNAME_LENGTHのような各フィールドの定数を作成し、substringを使用することです。それは醜いように見えるので、これを行うための他の推奨される方法があるかどうか興味があります。グーグル検索で見つかったいくつかの図書館のどれも、これ以上良いとは思えなかった。ありがとう

16
MattGrommes

ホイールを再発明する代わりに、 flatworm のようなフラットファイルパーサーを使用します。クリーンなAPIを備え、使いやすく、適切なエラー処理と単純なファイル形式記述子を備えています。別のオプションは jFFP ですが、私は最初のオプションを好みます。

21
Pascal Thivent

fixedformat4j で遊んだことがありますが、とてもいいです。コンバーター等の構成が容易。

7
p3t0r

niVocity-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<String[]> allRows = parser.parseAll(new File("path/to/fixed.txt")));

ここにいくつかあります 解析の例 あらゆる種類の固定幅入力。

そして、ここにいくつかの他の 一般的な書き込みの例 および他の 固定幅の例 固定幅フォーマットに固有のものがあります。

開示:私はこのライブラリの作成者です。オープンソースで無料です(Apache 2.0ライセンス)

5
Jeronimo Backes

Scalaに最適ですが、おそらくJavaで使用できます

自分で作成した固定長フォーマット用の適切なライブラリがないことにうんざりしていました。 ここで確認できます: https://github.com/atais/Fixed-Length

基本的な使用法は、ケースクラスを作成し、それを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)
1
Atais

これが私が使用する基本的な実装です:

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

固定幅ファイルを読み取るためのプレーンな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);
            }
        }

    }

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

}
0
Amit Prasad

固定幅のテキストソースを解析するために使用できる別のライブラリ: https://github.com/org-tigris-jsapar/jsapar

スキーマをxmlまたはコードで定義し、固定幅のテキストをJava Beanに解析するか、内部形式から値をフェッチすることができます。

0
stenix

Apache Commons CSV プロジェクトは、ファイルで修正されたものを処理できます。

固定幅機能はサンドボックスからの昇格に耐えられなかったようです。

0
Jherico

文字列の名前がinStrの場合は、文字列をchar配列に変換し、String(char[], start, length)コンストラクターを使用します。

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

\t+を区切り文字として使用できます。

次のようなものを試してください

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