プログラマメモ2 - programmer no memo2

いまだにでてくる文字コードがらみの問題のための 2015/01/18

いまだにでてくる文字コードがらみの問題

import java.io.UnsupportedEncodingException; public class TestMojicode { public static void main(String[] args) throws Exception { a(); } static void a() throws UnsupportedEncodingException { int[] cps = {0x2012,0x2013,0x2014,0x2015,0x2212}; for (int cp : cps) { System.out.println("============"); String hex = Integer.toHexString(cp); System.out.println("hex:" + hex); final String s = toS(cp); { int cp2 = cp(s.charAt(0)); System.out.println(s + ":" + cp2); } { String s1 = to(s, "MS932"); int cp2 = cp(s1.charAt(0)); System.out.println(s1 + ":" + cp2); } { String s2 = to(s, "SJIS"); int cp2 = cp(s2.charAt(0)); System.out.println(s2 + ":" + cp2); } } } static String to(String s, String charasetName) throws UnsupportedEncodingException { return new String(s.getBytes(charasetName), charasetName); // 一旦変換してもどす } static String toS(int cp) { char[] ch = Character.toChars(cp); return new String(ch); } static int cp(char c) { return Character.codePointAt(new char[] { c }, 0, 1); } }



import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; import javax.imageio.stream.FileImageInputStream; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 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 TestPoiExcel extends TestMojicode { static final String[][] sss = { { "全角ハイフンマイナス", "-" }, { "半角ハイフンマイナス", "-" }, { "全角ハイフン", "‐" }, { "全角マイナス", "−" }, { "フィギュアダッシュ", "‒" }, { "全角ダッシュ", "—" }, { "二分ダッシュ", "–" }, { "ホリゾンタルバー", "―" }, { "全角長音", "ー" }, { "半角長音", "ー" }, { "罫線", "─" }, { "罫線", "━" }, { "いち", "一" }, }; public static void main(String[] args) throws Exception { c("/Users/nakawakashigeto/export/eclipse/workspace/workspace_closure/zzz_2015/src/doc-files/b.xls"); } static void c(String file) throws InvalidFormatException, IOException, URISyntaxException { File f = new File(file); InputStream inputStream = new FileInputStream(f); Workbook workbook = workbook(inputStream); Sheet sheet = workbook.getSheetAt(0); a(sheet); workbook.write(new FileOutputStream(f)); } static void a(Sheet sheet) throws InvalidFormatException, IOException, URISyntaxException { int pos = 0; for (String[] ss : sss) { set(sheet, pos, 0, ss[0]); set(sheet, pos, 1, ss[1]); set(sheet, pos, 2, to(ss[1], "MS932")); set(sheet, pos, 3, to(ss[1], "SJIS")); pos += 1; } } public static void set(Sheet sheet, int r, int c, String s) { Row row = sheet.getRow(r); if (row == null) { row = sheet.createRow(r); } Cell cell = row.getCell(c); if (cell == null) { cell = row.createCell(c); } cell.setCellValue(s); } public static Workbook workbook(InputStream inputStream) throws InvalidFormatException, IOException { return WorkbookFactory.create(inputStream); } }

そのアドレスから、さらにシート名だけをとりだしたい。 2015/12/29

javaです。
poiでエクセル操作です。
セルにハイパーリンクでドキュメント内にあるリンクをはり、それをpoiを使って取得するのですが、その参照の形式は、シート名!A1という感じのものになります。
これがエクセル内での特殊文字?とか使われると'シート名!!'!A1という感じになります(ここではシート名に!をあえて含めています)。

参照(アドレス)の取得の仕方は、
Cell::getHyperlink()でHyperlinkをとりだして、さらにそこからHyperlink::getAddress()で、文字列表現をとりだします。

そのアドレスから、さらにシート名だけをとりだしたい。
poiのコードをのぞいていますと、
org.apache.poi.ss.util.CellReferenceにあるparseSheetNameでやっているようです。
ちなみにバージョンは、バージョン3.10.1です。
現時点でのコードをみるとTODOがコメントに書いてあって、どうもこの似たような処理を、あちらこちらでやっているようで、それをリファクタリングしたいようです。

簡単にシート名をとりだすためのユーティリティメソッドを書いてみました。


以下、コード

public static String parseSheetName(String reference) { final char QUOTE = '\''; StringBuilder sb = new StringBuilder(); int startPos = 0; if (reference.charAt(0) == QUOTE) { startPos = 1; } for (int i = startPos; i < reference.length(); i++) { char c = reference.charAt(i); if (QUOTE == c) { break; } if (1 != startPos && '!' == c) { break; } sb.append(c); } return sb.toString(); }







WorkbookFactoryはどこ? - poi-ooxmlを忘れずに 2014/12/27

poiです。
ひさびさにpoiですが、
WorkbookFactoryを探してpoiのjarにない。。。
それで、poi-ooxmlにあることにあとから気がつくわけです。
poi-ooxmlはpoiからみるとdependentな関係じゃないので、ビルドツール(maven)とかであわせてダウンロードしてくれないぽい(poi)。


シリアル値の計算 その3 エクセル - java POI頼み 2014/02/16

前回の記事の検証
実際に、org.apache.poi.ss.usermodel.DateUtilを使ってみる。


 今回試したコード

package poi; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import org.apache.poi.ss.usermodel.DateUtil; public class TestSerial2 { public static void main(String[] args) { a(); } static void a() { SimpleDateFormat format1 = new SimpleDateFormat("yyyy/MM/dd"); SimpleDateFormat format2 = new SimpleDateFormat("yyyy年MM月dd"); Calendar c = Calendar.getInstance(); c.set(1900, 0, 1);// 月は0スタート // とりあえず9000日 for (int i = 0; i < 9000; i++) { Date date = c.getTime(); System.out.printf("%s\t%s\t%f%n", format1.format(date), format2.format(date), toExcelSerial(c)); c.add(Calendar.DATE, 1); } } /** * excelで使用するシリアル値に変換します。 * * @param c * @return */ static double toExcelSerial(Calendar c) { return DateUtil.getExcelDate(c.getTime()); } }

出力された結果をエクセルシートにはりつけてみると
右の列が、書式設定している



シリアル値の計算、その2 エクセル - java POI頼み 2014/02/15

エクセルで使われるシリアル値の計算についてです。
と、実は、自前で計算しようとすると、とても面倒な気が....

これ素直に、ライブラリまかせのほういいと思う。
「下手に手出すと帰ってこれない」いつものパターン



poiに渡すまえに計算して、渡すのではなく、Dateかカレンダーにして渡すのがよいと思う。自前で、計算すると、正しいかどうか、ちょっと検証するのつらそうなのと、実績あるpoiのほうが、3.8888倍(当社比)信頼性が高いと思うのであった。

HSSFCellに実装みると、シリアル値にしてから設定しているのがわかる。
3.10-finalのコード参照

public void setCellValue(Calendar value) { setCellValue( HSSFDateUtil.getExcelDate(value, _book.getWorkbook().isUsing1904DateWindowing()) ); }

public void setCellValue(Date value) { setCellValue(HSSFDateUtil.getExcelDate(value, _book.getWorkbook().isUsing1904DateWindowing())); }

使うエクセルの設定によっては、1900 年日付システムと 1904 年日付システムを区別する必要があるのが上記のコードからもみてとれる。自前で、算出する場合、これも考慮しないといけないので、素直にpoiまかせで。

お試しとして、シリアル値が必要なら、HSSFDateUtilを使うのよいと思う。

変換自体のロジックはorg.apache.poi.ss.usermodel.DateUtilあるようです。
以下、poiのソースコードから
org.apache.poi.ss.usermodel.DateUtil
private static double internalGetExcelDate(Calendar date, boolean use1904windowing) { if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) || (use1904windowing && date.get(Calendar.YEAR) < 1904)) { return BAD_DATE; } // Because of daylight time saving we cannot use // date.getTime() - calStart.getTimeInMillis() // as the difference in milliseconds between 00:00 and 04:00 // can be 3, 4 or 5 hours but Excel expects it to always // be 4 hours. // E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours // and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60 + date.get(Calendar.MINUTE) ) * 60 + date.get(Calendar.SECOND) ) * 1000 + date.get(Calendar.MILLISECOND) ) / ( double ) DAY_MILLISECONDS; Calendar calStart = dayStart(date); double value = fraction + absoluteDay(calStart, use1904windowing); if (!use1904windowing && value >= 60) { value++; } else if (use1904windowing) { value--; } return value; }

時刻形式のための、シリアル値の計算、その1 エクセル - java 2014/02/14

Javaです。エクセル操作をおこなうためにpoiを使ってたりします。
時刻形式のフォーマットをエクセルファイルの書式で設定して、poiを使ってシリアル値を渡すための計算を模索してたりします。
 できてるっぽいです。

結果




参考


以下、コード

 
package poi; public class TestSerial { public static void main(String[] args) { a(); } static void a() { for (int i = 0; i <= 23; i++) { for (int j = 0; j <= 59; j++) { String sHH = "" + i; String sMM = "" + j; System.out.printf("%d:%d\t%f%n", i, j, toExcelSerial(sHH, sMM)); } } } /** * 参考<br/> * http://oshiete.goo.ne.jp/qa/4947261.html <br/> * 00:00 - 99:99<br/> * まで計算できるが時間の妥当性と数値チェックはおこなっていません。<br/> * * @param sHH * @param sMM * @return */ static public double toExcelSerial(String sHH, String sMM) { if (sHH == null || sMM == null) return 0.; if (!(sHH.length() <= 2 || sMM.length() <= 2)) return 0.; double hh = Double.parseDouble(sHH) * (1.0 / 24.0); double mm = Double.parseDouble(sMM) * (1.0 / 24.0 / 60.0); return (hh + mm); } }

エクセルの最大列数をもとめる 2013/12/14

javaです。
excelは列をアルファベットで表現しています。
Aなら1とかZなら26とかです。

POIではcolの位置を、0スタートの数値であらわすので、プログラムにおとすとき僕は混乱してしまいます。

この辺の感覚が違う人は、こういったことを気にしないし、苦にもしないようですが、僕の場合、デザインのときの位置決めと、コード上での指定を一致させたいです。

というわけで、POI を使う場合は、いつもユーティリティーメソッドのposメソッドを用意したりします。

ちなみにエクセルのバージョンによって、16384まで列が使えるようです。

以下コード
poiの操作で使う場合は、-1して使います。

public class Test26shinsuu { public static void main(String[] args) { System.out.println(pos("A")); System.out.println(pos("B")); System.out.println(pos("Z")); System.out.println(pos("AA")); // Excel 2003の最大列数 256 System.out.println(pos("IV")); // Excel 2007以降の最大列数 16384 System.out.println(pos("XFD")); } static int pos(String s){ String s1 = s.toUpperCase(); char[] cs = s1.toCharArray(); int p = 0; for(int i=0;i<cs.length;i++){ int c = (int)cs[i] - 64; p += Math.pow(26, cs.length - (i + 1)) * c; } return p; } }





エクセル2007の最大列数は2の14乗=16384らしいのですが、列記号XFD=... - Yahoo!知恵袋

ワークブックからシートを読み込むのよ その2 - groovy 2013/10/12

groovyです。
前回からのつづき。
プログラマメモ2: ワークブックからシートを読み込むのよ - groovy
forを使わずやってみた。工夫したつもりだけど。 あとはlogicの中身を実装する感じ。
今回やってて学習したこと、メソッドをクロージャに変換できること。このやり方使えばxxx.yyyが減るから、すっきりするかな。
しかし、はまったのは、比較時に()をつけないと、クロージャーそのままとして比較してしまい、エラーになった。

いろいろためしたけど、この案はぼつにしてforまわし使うんだろうな。

// wbはpoiのワークブックのインスタンス // ワークブックのメソッドをクロージャにする def numberOfSheets = wb.&getNumberOfSheets def getSheetAt = wb.&getSheetAt // ルール定義シート読み飛ばし // @含んでいるシート名のみ対象とする def ruleSkipSheet = { name -> if( name.contains('@') ) return false true } // ルール定義シート読み込み終了 def ruleStop = { pos -> if( numberOfSheets() <= pos ) return false true } // main loop処理 def loop = { pos, logic -> if ( ruleStop(pos) ){ def sheet = getSheetAt(pos) // シート処理 logic(sheet) // 次のシートへ pos += 1 // 再帰呼び出し call(pos, logic) } return } // sheetごとの処理を行う def logic = { sheet -> if( ruleSkipSheet( sheet.getSheetName() ) ) return println " *****OK logic ${ sheet }" } // 処理実行 loop(0, logic)

ワークブックからシートを読み込むのよ - groovy 2013/10/11
2013/10/11

grailsをさわりはじめて、groovyいいなーと最近ふたたび思っているところです。

POIを使って、エクセルのシートを読み込む際に、indexを指定しないととれないのが面倒です。
groovy使うならどういうふうにするかなーというわけで、こんな感じ。

// workbookからシートをある数分読み込む
wb.getNumberOfSheets().times { i ->
    def sheet = wb.getSheetAt(i)
    println "*** ${ i } ${ sheet.getSheetName() } "
}

times使ってます。
いろいろloopのやり方があってこまります。。。

プログラマメモ2: groovy 基本ループとか
each系クロージャの中断はできない - marsのメモ
が、ここまでやっていてわかったのはgroovyのloopでクロージャなのは、break,とかcontinueが使えない

なので、やはりforを使う....

def ruleSkipSheet = { name -> if(name.contains('@')) return false true } // workbookからシートをある数分読み込む for (int i in 0..wb.getNumberOfSheets() -1 ) { def sheet = wb.getSheetAt(i) def name = sheet.getSheetName() if( ruleSkipSheet( name ) ) continue println "*** ${ i } ${ sheet.getSheetName() } " }

これで読み込むシートを名前で決定できるうえのコードだと@が入っているシートのみOKというルールにしている。

Not enough data (1) to read requested (2) bytes - ふう 2012/08/31

Not enough data (1) to read requested (2) bytes
appliを申請してもいっこうに承認される気がしないので、べつのことにチャレンジということで、Javaです。POIです。 excelをPOIで処理をしようとして上記のエラーにでくわしました。やったことは最新のPOIにする現時点では3.8かな。あと読み込むファイルをxlsからxlsxにする。
こんなんで、うまくいったのでそれもどうかなと思いつつ、もう探求する気力がないので、うまくいったのでよしということで。
このエラー何度も遭遇してるんだけどね。
dom4j探すのに手間取ったので、リンク
dom4j - Introduction