JavaでExcel文書を操作する一例

 by 城風敏彦 http://shirokaz.com
  • まえがき
  • MinGWまたはCygwinをインストール
  • JDKをインストール
  • JExcelAPI(Java Excel API)のインストール
  • javaソースを作成する
  • コンパイル&実行する
  • Javaソースを改造する
  • DBのテーブル定義Excelをチェックするサンプル
  • CSV化するサンプル
  • JakartaPOIとJExcelAPIの比較
  • 参考文献

まえがき †

プログラム設計書はたいていExcelで書かれる。Excel文書にJavaでアクセスしてプログラムのひな形(インターフェース)を作成してコンパイルすると整合性チェックに使えるなど便利なことが多いです。概要設計においても設計項目の単項目チェック、項目関連チェック、マスターとの一致チェック、誤字脱字チェックなどを自動で行うことは非常に有益です。この手の作業に一番使われるVBAは文書数が多くなると数時間もかかるので、JavaやC#など本格的な言語で作成したいところです。

現在の業務で帳票といえばExcelかPDFです。PDFのテンプレートを作るのは一般的にExcelに比べて難しいので、Excelのテンプレートに値をJavaで流しこんで帳票を作成できるとうれしいです。

MinGWまたはCygwinをインストール †

eclipseがあれば必要ないかもしれませんが、ファイルの一覧を作るときなどに便利です。

  • http://www.kkaneko.com/rinkou/cygwin/mingw.html
  • http://www.okisoft.co.jp/esc/cygwin.html
  • http://203.141.155.99/home/shiromoto/setupcygwin.htm

JDKをインストール †

  • http://www.geocities.jp/shirokaz99/java/jdk-install.html

JExcelAPI(Java Excel API)のインストール †

http://sourceforge.net/project/showfiles.php?group_id=79926
jexcelapi_2_6_3.zipを任意のディレクトリに解凍します。
jexcelapi\jxl.jarをC:\cygwin\home\ユーザ名\javaなどJavaを開発するディレクトリにコピーします。jexcelapi\srcにサンプルコードがあるので参考にします。

javaソースを作成する †

JxlWriteTest.javaをコピーします。
JxlReadTest.javaをコピーします。

コンパイル&実行する †

javac -cp jxl.jar JxlWriteTest.java
java JxlWriteText
実行したディレクトリにoutput.xlsができているので内容を確認します。

javac -cp jxl.jar JxlReadTest.java
Java JxlReadTest

output.xlsの中のセルの値がコマンドラインに表示されるはずです。

Javaソースを改造する †

実際の仕事ではシート、行、列のループを使い、例外をその都度catchしてbreakかcontinueで制御します。
JxlReadTest.javaにコメントにも書きましたが、ファイルリストからExcelファイル名を1行ずつ読み込んで、各エクセルファイルから値を読んだ方が、毎回Javaコマンドを起動するよりはるかに効率が良いです。また統計をとるには全ファイルをアクセスし終わるまで終了できないので、mainでファイルリストから1行ずつExcelファイル名を読んでその中を分析していきます。
UnixやCygwinで 
find パス名 -name "*.xls" -fprint リストファイル名
でリストを作ると良いです。

DBのテーブル定義Excelをチェックするサンプル †

  • DDLCheck.java

CSV化するサンプル †

  • JxlCSVOut.java

JakartaPOIとJExcelAPIの比較 †

JavaでExcel文書にアクセスするのに、 JakartaPOI, OpenOffice.org も良く知られていますが、筆者の経験ではJExcelAPI がマクロやオートシェイプの入った文書でもエラーなくアクセスできるなど、一番安定している実感があるのでここに紹介しました。ただしJExcelAPIもセルの罫線のチェックなど細かいところが弱いので、MicrosoftのPIA (Primary Interop Assemblies)とC#の組みあわせの方がその手のチェックには強いかもしれないです。興味のある方はC#でExcelを参照してください。

jakarta-poiとJExcelAPIの大まかな比較を書いてみました。 Webではpoiの方がテストコードがあるとか、頻繁に更新されているとか評判が良いようですが、私の何度かの経験では結局poiは脆弱でJExeclAPIを使って目的を達成できました。バージョンによって違う結果もあると思いますが、傾向はこんな感じです。

  jakarta-poi JExcelAPI
計算式
マクロ
日付 ×
行削除 ×
オートシェイプ × ×
ブックのコピー ×

※マクロはプログラムが中断しない意味。 ※オートシェイプはテンプレートから出力ブックにコピーされる意味。

私のおすすめはJExcelAPIでDateTime(時刻)を使わず、Label(文字列)とNumber(数値)だけを使ってコーディングすることです。Labelで計算式が動かなければNumber(とDouble.parseDouble())を使うとたいてい計算できるようになります。 [Java]JExcelApiを使ってみる がセルのフォーマットを取得して再代入するところなど良い参考になります。

Jxlでテンプレートのxlsから帳票を作るには最初に以下のようにコーディングします。

Workbook w1 = Workbook.getWorkbook(new File("template.xls"));

WritableWorkbook w2 = Workbook.createWorkbook(new File("clone.xls"), w1);

このようにReadOnlyのオリジナルのWritableなコピーを作ってそれを編集するのがまず気に入りました。POIはコピー機能がないので自分でコピーを作ってからそれを編集しなければテンプレートを破壊してしまいます。jakarta-commonsのファイルコピー機能を使えばよいのでしょうが、どうもPOIは侮蔑的な名前の通り、真剣に作っているとは思えません。

参考文献 †

  • JExcelAPI
  • JExcelApiを使ってみた
  • [Java]JExcelApiを使ってみる <- テンプレートをコピーして値をセットして帳票を作るのに良い例
  • JExcelApiの使い方 <- 一からプログラムでスタイルを指定して帳票を作るのに良い例
  • JExcelApi (Java Excel API) を用いたExcelファイルを新規作成するサンプル
  • JakartaPOI
  • OpenOffice.org
  • C#でExcel
  • Blancofw-jexcelapi ML

 

JxlWriteTest.java

import java.io.File;

import java.io.IOException;

import java.util.Date;

import java.text.SimpleDateFormat;

import jxl.Workbook;

import jxl.write.Label;

import jxl.write.Number;

import jxl.write.WritableSheet;

import jxl.write.WritableWorkbook;

import jxl.write.WriteException;

/*
* JExcelApi でExcelファイルの新規作成テスト
* http://jexcelapi.sourceforge.net/がJXLの大元
* http://www.andykhan.com/jexcelapi/tutorial.html#writing をテスト
* http://homepage2.nifty.com/igat/igapyon/diary/2005/ig050221.htmlを参考
* @auther 城風敏彦
* 作成日: 2007/05/11
*/

public class JxlWriteTest {

	/** Excelブックを新規作成してファイル保存します. */
	public static void main(String[] args) {
		String outputFileName = "output.xls";
		String firstSheetName = "First Sheet";
		WritableWorkbook workbook = null;
		try {
			System.out.println(outputFileName + "ブックを新規作成します.");
			workbook = Workbook.createWorkbook(new File(outputFileName));
			if (workbook == null) {
				System.err.println(outputFileName + "ブック作成に失敗しました.");
				return;
			}
		} catch (IOException ex) {
			System.err.println(ex.toString());
			return;
		}
		System.out.println(firstSheetName + "を新規作成します.");
		WritableSheet sheet = workbook.createSheet(firstSheetName, 0);
		if (sheet == null) {
			System.err.println(outputFileName + "シート作成に失敗しました.");
			return;
		}
		System.out.println("A列3行に文字列セルを新規作成します.");
		Label label = new Label(0, 2, "A ラベル レコード");
		try {
			sheet.addCell(label);
		} catch (WriteException ex) {
			System.err.println("A3セルの書き込みに失敗しました.:" + ex.toString());
		}
		System.out.println("D列5行に数値セルを新規作成します.");
		Number number = new Number(3, 4, 3.1459);
		try {
			sheet.addCell(number);
		} catch (WriteException ex) {
			System.err.println("D5セルの書き込みに失敗しました(2).:" + ex.toString());
		}
		System.out.println("B列1行に日付セルを新規作成します.");
		// DateTimeを使うと1900/1/0になってしまうので、Labelを使って日付セルを作成
		// DateTime datetime = new DateTime(1, 0, new Date());
		SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
		String dateStr = format.format(new Date());
		Label label2 = new Label(1, 0, dateStr);
		try {
			sheet.addCell(label2);
		} catch (WriteException ex) {
			System.err.println("B1セルの書き込みに失敗しました.:" + ex.toString());
		}
		try {
			workbook.write();
		} catch (IOException ex) {
			System.err.println(outputFileName + "ブックの書き込みに失敗しました.:"
					+ ex.toString());
		}
		System.out.println(outputFileName + "ブックのファイル保存に成功しました.");
		try {
			if (workbook != null) {
				workbook.close();
			}
		} catch (Exception ex) {
			System.err.println(outputFileName + "ブックのクローズに失敗しました.:"
					+ ex.toString());
		}
	}

}


 

 

JxlReadTest.java

import java.io.File;

import jxl.Cell;

import jxl.Workbook;

import jxl.Sheet;

/*
* JExcelApi でExcelファイルの読込テスト
* http://jexcelapi.sourceforge.net/がJXLの大元
* http://www.andykhan.com/jexcelapi/tutorial.html#writing をテスト
* http://homepage2.nifty.com/igat/igapyon/diary/2005/ig050221.htmlを参考
* @auther 城風敏彦
* 作成日: 2007/05/11
* 改造方向:プロジェクトでは何百ものエクセルファイルにアクセスしてレポートを出力したり
*  統計をとることも多いので、ファイルリストをからファイル名を読み込んで動作させる。
*  UnixやCygwinで 
*  find パス名 -name "*.xls" -fprint リストファイル名
*  でリストを作ると良い。
*/

public class JxlReadTest {

	/** Excelブックを開きます. */
	public static void main(String[] args) {
		String inputFileName = "output.xls";
		Workbook workbook = null;
		try {
			System.out.println(inputFileName + "ブックをオープンします.");
			workbook = Workbook.getWorkbook(new File(inputFileName)); 
			if (workbook == null) {
				System.err.println(inputFileName + "ブックオープンに失敗しました.");
				return;
			}
		} catch (Exception ex) {
			System.err.println(ex.toString());
			return;
		}
		System.out.println("1番目のシートにアクセスします.");
		Sheet sheet = workbook.getSheet(0);
		if (sheet == null) {
			System.err.println(inputFileName + "シート作成に失敗しました.");
			return;
		}
		Cell cell = null;
		String cellStr = "";
		try {
			cell = sheet.getCell(0,2);
			cellStr = cell.getContents();
			System.out.println("A3セルの内容は「"+cellStr+"」です");
		} catch (RuntimeException ex) {
			System.err.println("A3セルの読込に失敗しました.:" + ex.toString());
		}
		try {
			cell = sheet.getCell(3,4);
			cellStr = cell.getContents();
			System.out.println("D5セルの内容は「"+cellStr+"」です");
		} catch (RuntimeException ex) {
			System.err.println("D5セルの読込に失敗しました.:" + ex.toString());
		}
		try {
			cell = sheet.getCell(1,0);
			cellStr = cell.getContents();
			System.out.println("B1セルの内容は「"+cellStr+"」です");
		} catch (RuntimeException ex) {
			System.err.println("B1セルの読込に失敗しました.:" + ex.toString());
		}
		try {
			if (workbook != null) {
				workbook.close();
			}
		} catch (Exception ex) {
			System.err.println(inputFileName + "ブックのクローズに失敗しました.:"
					+ ex.toString());
		}
	}

}


 

DDLCheck.java

import java.io.File; import java.util.*;

import jxl.*;

/**

* JExcelApi でExcel DDLチェック(列名の重複のみ)
*
* @auther 城風敏彦 作成日: 2010/04/23
*/
public class DDLCheck {
	/** Excelブックを開きます. */
	public static void main(String[] args) {
		String inputFileName = "d:/shiro/xls/DDL.xls";
		Workbook workbook = null;
		try {
			System.out.println(inputFileName + "ブックをオープンします.");
			workbook = Workbook.getWorkbook(new File(inputFileName));
			if (workbook == null) {
				System.err.println(inputFileName + "ブックオープンに失敗しました.");
				return;
			}
			Sheet[] sheets = workbook.getSheets();
			int sheetCount = sheets.length;
			System.out.println("シート数:" + sheetCount);
			for (int i = 1; i < sheetCount; i++) {
				Sheet sheet = workbook.getSheet(i);
				//System.out.println(i+":"+sheet.getName());
				if (sheet == null) {
					System.err.println(inputFileName + "シート読み込みに失敗しました.");
					return;
				}
				Cell cell = null;
				String cellStr = "";
				int j = 0;
				HashMap mapL = new HashMap();
				HashMap mapP = new HashMap();
				int row = sheet.getRows();
				for (j = 6; j < row; j++) {
					// 論理列名
					cell = sheet.getCell(1, j);
					cellStr = cell.getContents();
					if (cellStr == null || cellStr.equals("")) {
						break;
					}
					if (mapL.get(cellStr) != null) {
						System.out.println(sheet.getName() + " B" + j + " 論理列名"
								+ cellStr + "は重複しています。");
					}
					mapL.put(cellStr, cellStr);
					// 物理列名
					cell = sheet.getCell(2, j);
					cellStr = cell.getContents();
					if (cellStr == null || cellStr.equals("")) {
						break;
					}
					if (mapP.get(cellStr) != null) {
						System.out.println(sheet.getName() + " B" + j + " 物理列名"
								+ cellStr + "は重複しています。");
					}
					mapP.put(cellStr, cellStr);
					//System.out.println("B"+j+"セルの内容は「"+cellStr+"」です");
				}
			}
		} catch (Exception ex) {
			System.err.println(ex.toString());
		} finally {
			if (workbook != null) {
				workbook.close();
			}
		}
	}
}

 

JxlCSVOut.java

import java.io.*;

import jxl.*;

/*
 * JExcelApi でExcelファイルの第1シートをCSV形式でファイル出力する

 * @auther 城風敏彦
 * 作成日: 2011/05/18
 */

public class JxlCSVOut {
	private static String inputFileName = "C:\\Temp\\jxl.xls";
	private static String outputFileName = "C:\\Temp\\jxl.csv";

	/** Excelブックを開きます. */
	public static void main(String[] args) {

		Workbook workbook = null;

		try {
			System.out.println(inputFileName + "ブックをオープンします.");
			workbook = Workbook.getWorkbook(new File(inputFileName));

			if (workbook == null) {
				System.err.println(inputFileName + "ブックオープンに失敗しました.");
				return;
			}

		} catch (Exception ex) {
			System.err.println(ex.toString());
			return;
		}

		System.out.println("1番目のシートにアクセスします.");
		Sheet sheet = workbook.getSheet(0);
		if (sheet == null) {
			System.err.println(inputFileName + "シート読込に失敗しました.");
			return;
		}

		// System.out.println(sheet.getName());

		int rowCount = sheet.getRows();
		int colCount = sheet.getColumns();

		// System.out.println("col="+colCount+",row="+rowCount);

		Cell cell = null;
		String cellStr = "";

		OutputStreamWriter bw = null;

		int row = 0;
		int col = 0;
		try {
			//FileIOStream の作成
			FileOutputStream fos = new FileOutputStream(outputFileName);
			//ストリームのラップ

			     bw  = new OutputStreamWriter(fos, "SJIS");



			for (row = 0; row < rowCount; row++) {
				for (col = 0; col < colCount; col++) {

					cell = sheet.getCell(col, row);
					cellStr = cell.getContents();
					cellStr = ","
							+ cellStr.replaceAll("\n", "&br;").replaceAll(",",
									"、");
					bw.write(cellStr);

				}
				System.out.println();
				bw.write("\n");
			}
		} catch (IOException e) {
			System.err.println("FILE出力に失敗しました.");
		} catch (RuntimeException ex) {
			System.err.println(col + ":" + row + "セルの読込に失敗しました.:"
					+ ex.toString());
		}

		try {
			if (workbook != null) {
				workbook.close();
			}
			if (bw!= null) {
				bw.close();
			}
		} catch (IOException e) {
			System.err.println("FILE Closeに失敗しました.");
		} catch (Exception ex) {
			System.err.println(inputFileName + "ブックのクローズに失敗しました.:"
					+ ex.toString());
		}
	}
}

 

你可能感兴趣的:(JAVA)