仕事ではよく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よりダウンロード可能です。
サンプルは以下のような感じです
姓,名,年齢 とうきょう,たろう,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なども、正しく取り扱うことができます。すごいです。。
さて先のように、簡単にCSVをパースすることができましたが、このライブラリでとても便利なのは、CSVデータを指定したJavaBeans?に直接マッピングできるところです。たとえばさっきの、
姓,名,年齢 とうきょう,たろう,33 東京,太郎,15を、
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?のフィールドを対応づける必要がありますが、
などいろいろな方法で対応付けを指定することができます。
実際にやってみます。Mappingの指定は、
姓,名,年齢 とうきょう,たろう,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?を生成しフィールドに値をセットしてくれます。これは便利ですね。
さてプログラム中に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です。
以下書き途中。
ColumnPositionMappingStrategy?をつかう
HeaderColumnNameTranslateMappingStrategy?をつかう
HeaderColumnNameMappingStrategy?をつかう
この記事は
現在のアクセス:98361