Top / Java / CSVを取り扱う

CSVを読み込むJavaライブラリ

仕事ではよくCSVファイルを読み込んで云々といった業務処理が結構必要になったりしますが、Javaのライブラリを探していたところ、よさげなライブラリが見つかりました。opencsv - an open source csv parser for Java です。

ちょっと触ったところ非常にシンプルで使いやすかったのでメモっておきます。

ダウンロード

opencsv - an open source csv parser for Java より、ダウンロード可能です。

使ってみる。

Eclipseのプロジェクトとしてアップしました。 http://www.masatom.in/viewvc/trunk/CSVReaderSamples/?root=Examplesよりダウンロード可能です。

サンプルは以下のような感じです

  • sample.csv
    姓,名,年齢
    とうきょう,たろう,33
    東京,太郎,15
  • プログラム
    public class Main {
      public static void main(String[] args) throws IOException {
        CSVReader reader = new CSVReader(new FileReader("sample.csv"));
        String[] nextLine;
        while ((nextLine = reader.readNext()) != null) {
          // nextLine[] is an array of values from the line
          System.out.println(nextLine[0] + " " + nextLine[1] + " etc...");
        }
      }
    }

実行結果は以下の通り。

姓 名 etc...
とうきょう たろう etc...
東京 太郎 etc...

csvファイルをパースして、適切に分割してくれています。

サンプルは単純でしたが、ダブルクォート("")で囲まれたCSVやタブ区切りのファイル、""内のカンマの取り扱い、途中に改行が入るCSV、たとえば

姓,名,年齢
とうきょう,"た
ろう",33
東京,太郎,15

こんなたち悪いCSVなども、正しく取り扱うことができます。すごいです。。

JavaBeans?にマッピングする

さて先のように、簡単にCSVをパースすることができましたが、このライブラリでとても便利なのは、CSVデータを指定したJavaBeans?に直接マッピングできるところです。たとえばさっきの、

  • sample.csv
    姓,名,年齢
    とうきょう,たろう,33
    東京,太郎,15
    を、
  • JavaBeans?
    public class CSVSampleBean implements Serializable {
      private String last_name;
      private String first_name;
      private String age;
      // Getter/Setterは省略
      @Override
      public String toString() {
        return new ToStringBuilder(this).append("姓", last_name).append("名",
            first_name).append("年齢", age).toString();
      }
    }

にマッピングする事ができるんですね。CSVをJavaBeans?にマッピングするには、CSVの列とJavaBeans?のフィールドを対応づける必要がありますが、

  • CSVの1列目はフィールド名○○、2列目は△△ と指定する
  • CSVの1行目はヘッダ行と見なして、1行目の文字列群と、JavaBeans?のフィールドを対応づける

などいろいろな方法で対応付けを指定することができます。

JavaBeans?にマッピングする

実際にやってみます。Mappingの指定は、

  • sample.csv
    姓,名,年齢
    とうきょう,たろう,33
    東京,太郎,15
    ...
    の「姓,名,年齢」とJavaBeans?のフィールド「last_name,first_name,age」をjava.util.Mapをつかって対応づける方法でやってみます。実際のプログラムは以下の通りです
public class HeaderColumnNameTranslateMain {
  private static final String CSV_FILE = "sample.csv";

  public static void main(String[] args) throws IOException {
    HeaderColumnNameTranslateMappingStrategy strat = new HeaderColumnNameTranslateMappingStrategy();
    strat.setType(CSVSampleBean.class);

    Map<String, String> map = createMapping();

    strat.setColumnMapping(map);
    CsvToBean csv = new CsvToBean();
    List<CSVSampleBean> list = csv.parse(strat, new FileReader(CSV_FILE));
    for (CSVSampleBean bean : list) {
      System.out.println(bean);
    }
  }
  
  // CSVのヘッダ名が、どのフィールドにマッピングすればいいかを指定する。
  // map.put("[ヘッダ名]", "[フィールド名]");
  // この場合は、ヘッダ行が無視される。
  private static Map<String, String> createMapping() {
    Map<String, String> map = new HashMap<String, String>();
    map.put("姓", "last_name");
    map.put("名", "first_name");
    map.put("年齢", "age");
    return map;
  }
}

実行結果は以下の通り

nu.mine.kino.csv.CSVSampleBean@833955[姓=とうきょう,名=たろう,年齢=33]
nu.mine.kino.csv.CSVSampleBean@13582d[姓=東京,名=太郎,年齢=15]

このようにマッピングを指定するだけで、自動的にJavaBeans?を生成しフィールドに値をセットしてくれます。これは便利ですね。

ちなみに

List<CSVSampleBean> list = csv.parse(strat, new FileReader(CSV_FILE));

この処理ですが、

CSVReader reader = new CSVReader(new FileReader(CSV_FILE));
List<CSVSampleBean> list = csv.parse(strat, reader);

というように自分でCSVReaderを生成して渡すこともできます。こちらのメソッドはCSVファイルがタブ区切りだったばあいなどに

CSVReader reader = new CSVReader(new FileReader(CSV_FILE),'\t'); <-セパレータをタブにしてる

として自分でCSVReaderを作成するばあいに使用します。

プログラム中でなく、設定ファイルとかでマッピングを指定したい

さてプログラム中にMapを用いてCSVヘッダ名とフィールド名を対応づけることで、JavaBeans?にCSVをマッピングすることができました。ではそのマッピングを外から指定できないかですが、どうも標準の機能ではできないっぽいです。、、、のでちょっとコードを追加して、できるようにしました。

au.com.bytecode.opencsv.bean.HeaderColumnNameAutoTranslateMappingStrategyというクラスを作成したのですが、このクラスは

姓=last_name
名=first_name
年齢=age

というテキストファイル*1からマッピングを読み取り、そのマッピングにしたがってJavaBeans?のインスタンスを生成してくれます。

具体的なコードは以下の通り。

public class HeaderColumnNameTranslateMain3 {
  private static final String CSV_FILE = "sample.csv";

  public static void main(String[] args) throws IOException {
    HeaderColumnNameAutoTranslateMappingStrategy strat = new HeaderColumnNameAutoTranslateMappingStrategy();
    // FileInputStream in = new FileInputStream(new File("hogehoge.txt"));
    // strat.setInputStream(in);
    strat.setType(CSVSampleBean.class);
    CsvToBean csv = new CsvToBean();
    List<CSVSampleBean> list = csv.parse(strat, new FileReader(CSV_FILE));
    for (CSVSampleBean bean : list) {
      System.out.println(bean);
    }
  }
}

コメントアウトしてますが、設定ファイルはプログラムで指定することもできます。指定しない場合のデフォルト値は、JavaBeans?のクラスと同じディレクトリ上の[JavaBean?クラス名].txtです。

TIPS集

以下書き途中。

CSVの順番とJavaBeans?のフィールドをマッピングする方法

CSVの順番(何列目のデータ)とJavaBeans?のフィールドをマッピングすることで、JavaBeans?のインスタンスを生成する事もできます。具体的には下記のように、 ColumnPositionMappingStrategy?をつかえばOKです。

ColumnPositionMappingStrategy strat = new ColumnPositionMappingStrategy();
strat.setType(CSVSampleBean.class);
// CSVの順番で、どのフィールドにマッピングすればいいかを指定する。
String[] columns = new String[] { "last_name", "first_name", "age" };//フィールド名
strat.setColumnMapping(columns);

どのフィールドにCSVのどの列のデータをマッピングするかを指定するって感じですね。

CSVのヘッダ行のヘッダ名称とフィールドをマッピングする方法

HeaderColumnNameTranslateMappingStrategy?をつかう

CSVのヘッダ行のヘッダ名称がそのままフィールド名の場合

HeaderColumnNameMappingStrategy?をつかう

関連リンク


この記事は

選択肢 投票
おもしろかった 64  
そうでもない 24  

Top / Java / CSVを取り扱う

現在のアクセス:75728


*1 該当のJavaBeans?と同じディレクトリに[Bean名].txtって名前で置いておく

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-07-19 (水) 15:12:15 (6d)