#author("2023-01-06T00:20:46+00:00","","")
// 下階層用テンプレート
#topicpath
----
//ここにコンテンツを記述します。
#contents


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

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


**ダウンロード [#o48fbf97]
[[opencsv - an open source csv parser for Java>http://opencsv.sourceforge.net/]] より、ダウンロード可能です。


**使ってみる。 [#fc391ad0]
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にマッピングする [#l241c4a1]
さて先のように、簡単に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にマッピングする [#v0910948]
実際にやってみます。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を作成するばあいに使用します。


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

[[au.com.bytecode.opencsv.bean.HeaderColumnNameAutoTranslateMappingStrategy>http://www.masatom.in/viewvc/trunk/CSVReaderSamples/source/au/com/bytecode/opencsv/bean/?root=Examples]]というクラスを作成したのですが、このクラスは

 姓=last_name
 名=first_name
 年齢=age
というテキストファイル((該当のJavaBeansと同じディレクトリに[Bean名].txtって名前で置いておく))からマッピングを読み取り、そのマッピングにしたがって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集 [#oa4d3768]

以下書き途中。
***CSVの順番とJavaBeansのフィールドをマッピングする方法 [#m634fc39]
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のヘッダ行のヘッダ名称とフィールドをマッピングする方法 [#a69969de]
HeaderColumnNameTranslateMappingStrategyをつかう


***CSVのヘッダ行のヘッダ名称がそのままフィールド名の場合 [#g114b09c]
HeaderColumnNameMappingStrategyをつかう




**関連リンク [#p5fcd061]
-[[opencsv - an open source csv parser for Java>http://opencsv.sourceforge.net/]]

----
この記事は
#vote(おもしろかった[19],そうでもない[0])
#vote(おもしろかった[100],そうでもない[26])

#comment
#topicpath


SIZE(10){現在のアクセス:&counter;}


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS