プログラム設計書はたいていExcelで書かれる。Excel文書にJavaでアクセスしてプログラムのひな形(インターフェース)を作成してコンパイルすると整合性チェックに使えるなど便利なことが多いです。概要設計においても設計項目の単項目チェック、項目関連チェック、マスターとの一致チェック、誤字脱字チェックなどを自動で行うことは非常に有益です。この手の作業に一番使われるVBAは文書数が多くなると数時間もかかるので、JavaやC#など本格的な言語で作成したいところです。
現在の業務で帳票といえばExcelかPDFです。PDFのテンプレートを作るのは一般的にExcelに比べて難しいので、Excelのテンプレートに値をJavaで流しこんで帳票を作成できるとうれしいです。
eclipseがあれば必要ないかもしれませんが、ファイルの一覧を作るときなどに便利です。
http://sourceforge.net/project/showfiles.php?group_id=79926
jexcelapi_2_6_3.zipを任意のディレクトリに解凍します。
jexcelapi\jxl.jarをC:\cygwin\home\ユーザ名\javaなどJavaを開発するディレクトリにコピーします。jexcelapi\srcにサンプルコードがあるので参考にします。
JxlWriteTest.javaをコピーします。
JxlReadTest.javaをコピーします。
javac -cp jxl.jar JxlWriteTest.java
java JxlWriteText
実行したディレクトリにoutput.xlsができているので内容を確認します。
javac -cp jxl.jar JxlReadTest.java
Java JxlReadTest
output.xlsの中のセルの値がコマンドラインに表示されるはずです。
実際の仕事ではシート、行、列のループを使い、例外をその都度catchしてbreakかcontinueで制御します。
JxlReadTest.javaにコメントにも書きましたが、ファイルリストからExcelファイル名を1行ずつ読み込んで、各エクセルファイルから値を読んだ方が、毎回Javaコマンドを起動するよりはるかに効率が良いです。また統計をとるには全ファイルをアクセスし終わるまで終了できないので、mainでファイルリストから1行ずつExcelファイル名を読んでその中を分析していきます。
UnixやCygwinで
find パス名 -name "*.xls" -fprint リストファイル名
でリストを作ると良いです。
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は侮蔑的な名前の通り、真剣に作っているとは思えません。
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()); } } } |
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()); } } } |
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 // 物理列名 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(); } } } } |
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()); } } } |