[[ノート>ノート/ノート]]

*JavaからExcelファイルをいじるApache POI [#e50a3c01]

ポイント:
-JavaのパッケージPOIで、Excelのファイル ***.xlsx を操作できる~
POIはMicrosoftのOfficeドキュメント(Word, Excel, ...)を操作できるライブラリで、今使うのはExcelの部分だけ。
-対象は、Office2007以降で使われているxlsxファイルのみ。古いxlsファイルは対象外。~
古いxlsファイルの場合、多少APIの使い方が違うらしいので、結構混乱の元のような気がする。
-APIドキュメントはだ完備しているが、参考ページは少ない

参照ページ


-[[POIプロジェクトのトップページ:http://poi.apache.org/index.html]]
-[[POI-HSSF and POI-XSSF - Java API To Access Microsoft Excel Format Files:http://poi.apache.org/spreadsheet/index.html]]
-[[Busy Developers' Guide to HSSF and XSSF Features:http://poi.apache.org/spreadsheet/quick-guide.html]]
-[[APIドキュメント:http://poi.apache.org/apidocs/index.html]]

サンプルを含むページ
-[[Javaでexcelファイル(.xlsx)を読む:POIサンプル:http://k-hiura.cocolog-nifty.com/blog/2012/09/javaexcelxlsxpo.html]]
-[[PoiBuilder で Quick Guide (10) : 色のカスタマイズ:http://waman.hatenablog.com/entry/20110707/1310059776]]
-[[Read / Write Excel File In Java Using Apache POI:http://viralpatel.net/blogs/java-read-write-excel-file-apache-poi/]]
-[[Reading/writing excel files in java : POI tutorial:http://howtodoinjava.com/2013/06/19/readingwriting-excel-files-in-java-poi-tutorial/]]
-[[JavaとApache POIを使ってExcelでうさぎ観察日記を作ろう:http://itpro.nikkeibp.co.jp/article/COLUMN/20110405/359101/]]
-[[前景色と背景色:http://www.javadrive.jp/poi/style/index2.html]]

**インストール [#r29db558]
パッケージをダウンロード・解凍し、クラスパスの通っているディレクトリへ置く
-[[Apache POI 3.6のインストール:http://www.javadrive.jp/poi/install/index2.html]]

面倒なので、/usr/share/java/の下に置いた。

**使い方のサンプル [#f9c7c85d]
注: UTF-8で漢字もOKだった。

上記のブログにもサンプルがある。

Excelファイルの新しい(2007以降?).xlsxのファイルに対しては、ライブラリXSSFを使う。
古い.xlsファイルに対してはHSSFを使う。一緒に(混ぜて)対応はできるのかは不明。ここでは、XSSFのみ対応。

***例 ファイルを読んで、セルを取り出したり、書き込んだり、加工したりする [#v36d38a8]

 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.awt.Color;
 import org.apache.poi.xssf.usermodel.XSSFCell;
 import org.apache.poi.xssf.usermodel.XSSFCellStyle;
 import org.apache.poi.xssf.usermodel.XSSFColor;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
 public class ReadWriteSample {
   public static void main(String[] args) throws Exception {
     FileInputStream is = new FileInputStream("/home/yamanouc/src/poi/cali/bunshi.xlsx");  // ファイル読み込み
     XSSFWorkbook wb = new XSSFWorkbook(is);    // ワークブックに取り込む
     XSSFSheet sheet = wb.getSheetAt(0);    // シート0番を取りだす
     XSSFCell cell = sheet.getRow(0).getCell(0);  // セル(0,0)=A1を指定
     System.out.println(cell.getStringCellValue());  // セルA1の内容を、文字列として読出し
     // System.out.println(cell.getNumericCellValue()); //数値セルの場合
 
     // cell.setCellValue(cell.getNumericCellValue() +5); //セルを書き変える場合
     // cell.setCellValue('あいう');    // 文字を書きこみたいとき
     XSSFCellStyle cellstyle = wb.createCellStyle();   // CellStyleを作っておく
      cellstyle.setFillPattern(XSSFCellStyle.BIG_SPOTS);  //FINE_DOTSとかいろいろ選択できる
     cellstyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(128,0,128)));   // RGBで色(Foregroud)を指定
     cell.setCellStyle(cellstyle);   // これでcellに対してスタイルを設定
     is.close();
 
     FileOutputStream os = new FileOutputStream("/home/yamanouc/src/poi/cali/newversion.xlsx");
     wb.write(os);    // ファイルに書き出し
   }
 }




セルのスタイルプロパティ(塗りつぶしとか色とか)を指定するには、
-あらかじめ、HSSFCellstyleのオブジェクトをcreateCellStyleで作っておいて、
-下記のsetFillForegroundColorとかsetFillBackgroundColorとかsetFillPatternで設定を変えておいて、
-cellオブジェクトに対してcell.setCellStyleでスタイルを設定すると、初めて有効になる。

 XSSFCellStyle cellstyle = wb.createCellStyle();   // CellStyleを作っておく
 cellstyle.setFillPattern(XSSFCellStyle.BIG_SPOTS);  //FINE_DOTSとかいろいろ選択できる
 cellstyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(128,0,128)));   // RGBで色(Foregroud)を指定
 cell.setCellStyle(cellstyle);   // これでcellに対してスタイルを設定

塗りつぶしパターンの指定は、setFillPatternで行う。パターンの選択肢は、
 ALT_BARS, BIG_SPOTS, BRICKS, DIAMONDS, FINE_DOTS, LEAST_DOTS, LESS_DOTS, NO_FILL,
 SOLID_FOREGROUND, SPARSE_DOTS, SQUARES, THICK_BACKWARD_DIAG, THICK_FORWARD_DIAG, 
 THICK_HORZ_BANDS, THICK_VERT_BANDS, THIN_BACKWARD_DIAG, THIN_FORWARD_DIAG, 
 THIN_HORZ_BANDS, THIN_VERT_BANDS

色の扱いは、決まった色で指定するか、RGBで指定するかのいずれか。決まった色パターンは、org.apache.poi.ss.usermodel.IndexedColors にあって、
 AQUA               AUTOMATIC          BLACK              BLUE
 BLUE_GREY          BRIGHT_GREEN       BROWN              CORAL
 CORNFLOWER_BLUE    DARK_BLUE          DARK_GREEN         DARK_RED
 DARK_TEAL          DARK_YELLOW        GOLD               GREEN
 GREY_25_PERCENT    GREY_40_PERCENT    GREY_50_PERCENT    GREY_80_PERCENT
 INDIGO             LAVENDER           LEMON_CHIFFON      LIGHT_BLUE
 LIGHT_CORNFLOWER_BLUE                 LIGHT_GREEN        LIGHT_ORANGE
 LIGHT_TURQUOISE    LIGHT_YELLOW       LIME               MAROON
 OLIVE_GREEN        ORANGE             ORCHID             PALE_BLUE
 PINK               PLUM               RED                ROSE
 ROYAL_BLUE         SEA_GREEN          SKY_BLUE           TAN
 TEAL               TURQUOISE          VIOLET             WHITE
 YELLOW 
これらの色を指定したうえで、メソッドgetIndex()で、ここで使えるshortの値を取りだす。
 setFillForegroundColor(IndexedColors.MAROON.getIndex());
 setFillBackgroundColor(IndexedColors.SKY_BLUE.getIndex());

このほか、java.awt.Colorでの色指定が使える。
-まずjava.awt.Colorのオブジェクトを作って、
-それを引数にしてXSSFColorのオブジェクトを作って、
-それを引数にしてsetFillForegroundColorを呼び出す。
 setFillForegroundColor(new XSSFColor(new java.awt.Color(128,0,128)));


**セルをすべてなめるには [#kbfe9b87]
org.apache.poi.ss.usermodelに含まれるRowやCell、org.apahce.poi.ss.utilに含まれるCellReferenceを使って
 for (Row row:sheet) { // 全部の行をなめる
   for (Cell cell:row) {// 全部のセルをなめる
     CellReference cellRef = new CellReference(row.getRowNum(), cell.getColumnIndex());
     System.out.print(cellRef.formatAsString());
     System.out.println(cell.getStringCellValue());
   }
 }
ということができる。

**JavaのHashMapで、value要素に配列を使いたい [#k17b0c28]
やりたいことは、シートの上にあるセルエントリの値(文字列が入っている)を保持しておいて、別ファイルから入力した文字列と比較して(擬)一致した場合には、色を変える処理をすること。~
このとき、どんなセルエントリ文字列があるかをHashMapに<文字列、(セルX, セルY)>の形で保持しておいて、後で一致を引けばいいだろう。(サーチなんてしたくないから)

で、HashMapタイプのvalueエントリに、整数のペア(X, Y)を入れたい。
 import java.lang.reflect.*;
 import java.util.HashMap;
 import java.util.ArrayList;
 
 class testHashMap {
   public static void main(String args[]){
     HashMap<String,ArrayList<Integer>> map = new HashMap<String,ArrayList<Integer>>();
 
     ArrayList<Integer> pair = new ArrayList<Integer>();
     pair.add(1); pair.add(3);
     map.put("りんご", pair);
     ArrayList<Integer> pair2= new ArrayList<Integer>();
     pair2.add(5); pair2.add(6);
     map.put("ぶどう", pair2);
 
     if (map.containsKey("りんご")){
       System.out.print("りんご=>");
       System.out.println(map.get("りんご"));
     }else{
       System.out.println("指定したキーは存在しません");
     }
   }
 }

参照ページは、
-[[HashMap &#8211; Single Key and Multiple Values Example:http://java.dzone.com/articles/hashmap-%E2%80%93-single-key-and]]
-[[ArrayListクラス:http://www.javadrive.jp/start/arraylist/index1.html]]

ここでの失敗~
pair2を作るのが癪だったので、同じインスタンスを使って、内容を消して、ぶどうにも使いまわそうと思ったのだが、
     ArrayList<Integer> pair = new ArrayList<Integer>();
     pair.add(1); pair.add(3);
     map.put("りんご", pair);
     pair.clear();                      <<--- pairを再利用する(リストを空にして)
     pair.add(5); pair.add(6);
     map.put("ぶどう", pair);
これは、当然ダメ。mapの内容はポインタだけで、その先は1つしかないpairのインスタンスに向いているから、りんごの方も値は(5, 6)になって、チャンチャン。あまりに単純な間違い。

というわけで、別のインスタンスpair2が必要だった。(か、コピーしてインスタンスを作り直すか)

じゃあ、newで作ればいいんでしょ、というのは、
    ArrayList<Integer> pair = new ArrayList<Integer>();
    pair.add(1); pair.add(3);
    map.put("りんご", pair);
    ArrayList<Integer> pair = new ArrayList<Integer>();        <<<---作りなおす
    pair.add(5); pair.add(6);
    map.put("ぶどう", pair);
コンパイル時にダメ。
 testHashMap.java:12: エラー: 変数 pairはすでにメソッド main(String[])で定義されています
    ArrayList<Integer> pair= new ArrayList<Integer>();
                       ^

**解決編 (2015-04-06) [#jfc3f0c3]
この手があった。2つのポイント。要するにpairという名前をつけたくないのだから、putの引数としてnewを直接に書いてしまいたい。newで呼ばれるコンストラクタに、リスト要素の値を与えて、ArrayListを作りたい。

こんなページを参照しました。  [[Java言語で固定要素のListを初期化する際のイディオム:http://d.hatena.ne.jp/ryoasai/20101226/1293350856]]。

 import java.lang.reflect.*;
 import java.util.*;
 
 class testHashMap2 {
   public static void main(String args[]){
     HashMap<String,List<Integer>> map = new HashMap<String,List<Integer>>();
 
     map.put("りんご", new ArrayList<Integer>(Arrays.asList(1,3)));
     map.put("ぶどう", new ArrayList<Integer>(Arrays.asList(5,6)));
 
     if (map.containsKey("りんご")){
       System.out.print("りんご=>");
       System.out.println(map.get("りんご"));
     }else{
       System.out.println("指定したキーは存在しません");
     }
   }
 }

**Excelのセルの内容を取り出すプログラムの例 [#i713cbc3]

 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.lang.reflect.*;
 import java.util.*;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.ss.usermodel.WorkbookFactory;
 
 public class ExtractMapEntries {
   public static void main(String[] args) throws Exception {
     if (args.length != 1) {
       System.out.println("引数の個数は1つにしてください");
       System.exit(0);
     }
     InputStream is = new FileInputStream(args[0]);
     Workbook wb = WorkbookFactory.create(is);
     Sheet sheet = wb.getSheetAt(0);
 
     HashMap<String,List<Integer>> map = new HashMap<String,List<Integer>>();
 
     for (Row row:sheet) { // 全部の行をなめる
       for (Cell cell:row) {// 全部のセルをなめる
 //         System.out.print(String.valueOf(cell.getRowIndex()) + '-' + String.valueOf(cell.getColumnIndex()));
 //         System.out.println(cell.getStringCellValue());  //こんな風に印刷できる
 
          Integer R = Integer.valueOf(cell.getRowIndex());  // RとかCに取り出す必要も無いのだが長くなるので
          Integer C = Integer.valueOf(cell.getColumnIndex());
          String V = cell.getStringCellValue();
          map.put(V, new ArrayList<Integer>(Arrays.asList(R,C)));
       }
     }
 
 //mapの内容を全部書き出す。keSetをイテレータのように使って。
     for (String key : map.keySet()) {
        System.out.println("[" + key + "]" + " " + Integer.toString((map.get(key)).get(0)) + " " + Integer.toString((map.get(key)).get(1)) );
        // mapからget(key)した内容は2要素のListなので、その要素(0)と(1)を取り出す。
        // 要素はIntegerなので(Listにする時にintではなくてIntegerにした)toStringでString型にする
     }
   }
 }

処理結果のイメージは、こんな感じになった。
 [] 172 20
 [放射線生物学] 119 14
 [実験動物学実習] 162 16
 [・文理系を超えて普遍的な「知」を構築する能力を] 8 4
 [分析化学] 90 8
 [物理学実験] 148 16
 [・他者に対する関心や社会の動きなど、高い知的] 10 4
 [機器分析化学] 150 16
 [英語A4] 34 8
 [科目群で身につける能力] 83 4
 [英語B4] 34 12
 [英語B3] 33 12
 [分子生物学] 97 10
 [分子生理学] 147 16
 [数理科学特論B2] 25 12
 [植物生理 I] 106 8
 [系統生物学] 106 6
 [有機分析法] 150 14
 [分子遺伝学] 96 16
 [力を身につける。] 59 4
 [海外体験プログラム] 73 6
 [海外体験プログラム] 72 6
 [科学哲学] 18 6
 [基礎生物学] 107 6
 [歴史学] 11 6
 [基礎生理学] 147 8
 [歴史学] 11 8
 [生物分子科学実験 II] 137 10
 [臨床血液学実習] 156 20
 [教養教育科目] 8 0
 [生物無機化学] 87 12
 [1年次] 83 6
 [生化学 II] 96 12
 [生物分子科学特論 III] 130 16
 [生物分子科学実験 IV] 138 10
 [血液学] 156 14
 [多数の化学反応の集積である生命現象について理解を深めるために、初年次では専門分 野の基礎学力やスキルを修得し、2〜3年次では多彩な実験科目と高度な専門科目を修得する。4年次では身につけた専門知識とスキルをもとに卒業研究によって問題解決能力を身につ ける。] 1 0
 [地学] 128 10
 [生物分子科学科] 0 16
 [病原微生物学] 153 16
 [キャリアデザイン] 63 12
 [3年次] 83 14


トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS