001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.model; 017 018import java.io.InputStream; 019import java.io.FileInputStream; 020import java.io.BufferedReader; // 6.2.2.0 (2015/03/27) 021import java.io.BufferedInputStream; 022import java.io.FileNotFoundException; 023import java.io.File; 024import java.io.IOException; 025import java.nio.file.Files; // 6.2.2.0 (2015/03/27) 026import java.nio.charset.Charset; // 6.2.2.0 (2015/03/27) 027 028// import java.text.DecimalFormat; 029// import java.text.NumberFormat; 030import java.util.Set; // 6.0.2.3 (2014/10/10) 031import java.util.TreeSet; // 6.0.2.3 (2014/10/10) 032 033import org.apache.xmlbeans.XmlException; 034import org.apache.poi.POITextExtractor; 035import org.apache.poi.extractor.ExtractorFactory; 036 037import org.apache.poi.hwpf.HWPFDocument; 038import org.apache.poi.hwpf.usermodel.Range; 039import org.apache.poi.hwpf.usermodel.Paragraph; 040 041import org.apache.poi.xwpf.usermodel.XWPFDocument; // 6.2.0.0 (2015/02/27) 042import org.apache.poi.xwpf.usermodel.XWPFParagraph; // 6.2.0.0 (2015/02/27) 043 044import org.apache.poi.hssf.usermodel.HSSFCellStyle; 045 046import org.apache.poi.hslf.HSLFSlideShow; 047import org.apache.poi.hslf.usermodel.SlideShow; 048import org.apache.poi.hslf.model.Slide; 049import org.apache.poi.hslf.model.TextRun; 050 051import org.apache.poi.xslf.usermodel.XMLSlideShow; // 6.2.0.0 (2015/02/27) 052import org.apache.poi.xslf.extractor.XSLFPowerPointExtractor; // 6.2.0.0 (2015/02/27) 053 054import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 055import org.apache.poi.openxml4j.exceptions.OpenXML4JException ; // 6.1.0.0 (2014/12/26) findBugs 056 057import org.apache.poi.ss.usermodel.WorkbookFactory; 058import org.apache.poi.ss.usermodel.Workbook; 059import org.apache.poi.ss.usermodel.Sheet; 060import org.apache.poi.ss.usermodel.Row; 061import org.apache.poi.ss.usermodel.Cell; 062import org.apache.poi.ss.usermodel.CellStyle; 063import org.apache.poi.ss.usermodel.CreationHelper; 064import org.apache.poi.ss.usermodel.RichTextString; 065import org.apache.poi.ss.usermodel.DateUtil; 066import org.apache.poi.ss.usermodel.FormulaEvaluator; 067import org.apache.poi.ss.usermodel.Name; // 6.0.2.3 (2014/10/10) 068import org.apache.poi.ss.util.SheetUtil; 069 070import org.opengion.fukurou.util.FileInfo; // 6.2.3.0 (2015/05/01) 071import org.opengion.fukurou.util.StringUtil; // 6.2.0.0 (2015/02/27) 072// import org.opengion.fukurou.util.HybsDateUtil; // 6.2.0.0 (2015/02/27) 073import org.opengion.fukurou.util.Closer; // 6.2.0.0 (2015/02/27) 074import static org.opengion.fukurou.util.HybsConst.CR; // 6.1.0.0 (2014/12/26) refactoring 075 076/** 077 * POI による、Excel/Word/PoworPoint等に対する、ユーティリティクラスです。 078 * 079 * 基本的には、ネイティブファイルを読み取り、テキストを取得する機能が主です。 080 * Excel、Word、PowerPoint、Visio、Publisher からのテキスト取得が可能です。 081 * 082 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 083 * @og.rev 6.2.0.0 (2015/02/27) パッケージ変更(util → model) 084 * @og.group その他 085 * 086 * @version 6.0 087 * @author Kazuhiko Hasegawa 088 * @since JDK7.0, 089 */ 090public final class POIUtil { 091 /** このプログラムのVERSION文字列を設定します。 {@value} */ 092 private static final String VERSION = "6.3.1.0 (2015/06/28)" ; 093 094 // 6.2.3.0 (2015/05/01) 095 public static final String POI_SUFIX = "ppt,pptx,doc,docx,xls,xlsx,xlsm" ; 096 097 /** 098 * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。 099 * 100 */ 101 private POIUtil() {} 102 103 /** 104 * 引数ファイルが、POI関連の拡張子ファイルかどうかを判定します。 105 * 106 * Excel、Word、PowerPoint、Visio、Publisher からのテキスト取得が可能です。 107 * 拡張子が、ppt,pptx,doc,docx,xls,xlsx,xlsm のファイルの場合、true を返します。 108 * 109 * @og.rev 6.2.3.0 (2015/05/01) POI関連の拡張子ファイルかどうかを判定 110 * 111 * @param file 判定するファイル 112 * @return POI関連の拡張子の場合、true 113 */ 114 public static boolean isPOI( final File file ) { 115 return POI_SUFIX.contains( FileInfo.getSUFIX( file ) ); 116 } 117 118 /** 119 * 引数ファイルを、POITextExtractor を使用してテキスト化します。 120 * 121 * Excel、Word、PowerPoint、Visio、Publisher からのテキスト取得が可能です。 122 * 拡張子から、ファイルの種類を自動判別します。 123 * 124 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 125 * @og.rev 6.2.0.0 (2015/02/27) getText → extractor に変更 126 * 127 * @param file 入力ファイル名 128 * @return 変換後のテキスト 129 * @og.rtnNotNull 130 */ 131 public static String extractor( final File file ) { 132 // InputStream fis = null; 133 POITextExtractor extractor = null; 134 try { 135 // fis = new BufferedInputStream( new FileInputStream( file ) ); 136 // extractor = ExtractorFactory.createExtractor( fis ); 137 extractor = ExtractorFactory.createExtractor( file ); 138 return extractor.getText(); 139 } 140 catch( FileNotFoundException ex ) { 141 final String errMsg = "ファイルが存在しません[" + file + "]" + CR + ex.getMessage() ; 142 throw new RuntimeException( errMsg,ex ); 143 } 144 catch( IOException ex ) { 145 final String errMsg = "ファイル処理エラー[" + file + "]" + CR + ex.getMessage() ; 146 throw new RuntimeException( errMsg,ex ); 147 } 148 catch( InvalidFormatException ex ) { 149 final String errMsg = "ファイルフォーマットエラー[" + file + "]" + CR + ex.getMessage() ; 150 throw new RuntimeException( errMsg,ex ); 151 } 152 catch( OpenXML4JException ex ) { 153 final String errMsg = "ODF-XML処理エラー[" + file + "]" + CR + ex.getMessage() ; 154 throw new RuntimeException( errMsg,ex ); 155 } 156 catch( XmlException ex ) { 157 final String errMsg = "XML処理エラー[" + file + "]" + CR + ex.getMessage() ; 158 throw new RuntimeException( errMsg,ex ); 159 } 160 finally { 161 Closer.ioClose( extractor ); 162 // Closer.ioClose( fis ); 163 } 164 } 165 166 /** 167 * 引数ファイル(Text)を、テキスト化します。 168 * 169 * ここでは、ファイルとエンコードを指定して、ファイルのテキスト全てを読み取ります。 170 * 171 * @og.rev 6.2.2.0 (2015/03/27) 引数ファイル(Text)を、テキスト化。 172 * @og.rev 6.2.3.0 (2015/05/01) textReader → extractor に変更 173 * 174 * @param file 入力ファイル 175 * @param encode エンコード名 176 * @return ファイルのテキスト 177 */ 178 public static String extractor( final File file , final String encode ) { 179 try { 180 // 指定のファイルをバイト列として読み込む 181 final byte[] bytes = Files.readAllBytes( file.toPath() ); 182 // 読み込んだバイト列を エンコードして文字列にする 183 return new String( bytes, encode ); 184 } 185 // catch ( UnsupportedEncodingException ex ) { 186 // final String errMsg = "エンコードが不正です[" + file + "] , ENCODE=[" + encode + "]" ; 187 // throw new RuntimeException( errMsg,ex ); 188 // } 189 catch ( IOException ex ) { 190 final String errMsg = "ファイル読込みエラー[" + file + "] , ENCODE=[" + encode + "]" ; 191 throw new RuntimeException( errMsg,ex ); 192 } 193 } 194 195 /** 196 * 引数ファイル(Text)を、テキスト化します。 197 * 198 * ここでは、ファイルとエンコードを指定して、ファイルのテキスト全てを読み取ります。 199 * 200 * @og.rev 6.2.2.0 (2015/03/27) 引数ファイル(Text)を、テキスト化。 201 * 202 * @param file 入力ファイル 203// * @param helper イベント処理させるI/F 204 * @param conv イベント処理させるI/F 205 * @param encode エンコード名 206 */ 207// public static void textReader( final File file , final TableModelHelper helper , final String encode ) { 208 public static void textReader( final File file , final TextConverter<String,String> conv , final String encode ) { 209 BufferedReader reader = null ; 210 211 int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 212 try { 213 reader = Files.newBufferedReader( file.toPath() , Charset.forName( encode ) ); 214 215 String line ; 216 while((line = reader.readLine()) != null) { 217// helper.value( line,rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 218 conv.change( line,String.valueOf( rowNo++ ) ); 219 } 220 } 221 catch ( IOException ex ) { 222 final String errMsg = "ファイル読込みエラー[" + file + "] , ENCODE=[" + encode + "] , ROW=[" + rowNo + "]" ; 223 throw new RuntimeException( errMsg,ex ); 224 } 225 finally { 226 Closer.ioClose( reader ); 227// helper.endFile( file ); // 6.2.0.0 (2015/02/27) 228 } 229 } 230 231 /** 232 * 引数ファイル(Word,PoworPoint,Excel)を、TableModelHelper を使用してテキスト化します。 233 * 234 * ここでは、ファイル名の拡張子で、処理するメソッドを選別します。 235 * 拡張子が、対象かどうかは、#isPOI( File ) メソッドで判定できます。 236 * 237 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 238 * 表形式オブジェクトの形で処理されます。 239 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 240 * スキップされます。 241 * 242 * @og.rev 6.2.3.0 (2015/05/01) 新規作成 243 * @og.rev 6.2.5.0 (2015/06/05) xls,xlsxは、それぞれ excelReader1,excelReader2 で処理します。 244 * 245 * @param file 入力ファイル 246// * @param helper イベント処理させるI/F 247 * @param conv イベント処理させるI/F 248 */ 249// public static void textReader( final File file , final TableModelHelper helper ) { 250 public static void textReader( final File file , final TextConverter<String,String> conv ) { 251 final String SUFIX = FileInfo.getSUFIX( file ); 252 253 if( "doc".equalsIgnoreCase( SUFIX ) ) { 254// wordReader1( file,helper ); 255 wordReader1( file,conv ); 256 } 257 else if( "docx".equalsIgnoreCase( SUFIX ) ) { 258// wordReader2( file,helper ); 259 wordReader2( file,conv ); 260 } 261 else if( "ppt".equalsIgnoreCase( SUFIX ) ) { 262// pptReader1( file,helper ); 263 pptReader1( file,conv ); 264 } 265 else if( "pptx".equalsIgnoreCase( SUFIX ) ) { 266// pptReader2( file,helper ); 267 pptReader2( file,conv ); 268 } 269 else if( "xls".equalsIgnoreCase( SUFIX ) ) { 270// excelReader1( file,helper ); // 6.2.5.0 (2015/06/05) 271 excelReader1( file,conv ); // 6.2.5.0 (2015/06/05) 272 } 273 else if( "xlsx".equalsIgnoreCase( SUFIX ) || "xlsm".equalsIgnoreCase( SUFIX ) ) { 274// excelReader2( file,helper ); // 6.2.5.0 (2015/06/05) 275 excelReader2( file,conv ); // 6.2.5.0 (2015/06/05) 276 } 277 else { 278 final String errMsg = "拡張子は、" + POI_SUFIX + " にしてください。[" + file + "]" ; 279 throw new RuntimeException( errMsg ); 280 } 281 } 282 283 /** 284 * 引数ファイル(Word)を、HWPFDocument を使用してテキスト化します。 285 * 286 * 拡張子(.doc)のファイルを処理します。 287 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 288 * 表形式オブジェクトの形で処理されます。 289 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 290 * スキップされます。 291 * 292 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 293 * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更 294 * @og.rev 6.2.4.2 (2015/05/29) 改行以外に、「。」で分割します。 295 * 296 * @param file 入力ファイル名 297// * @param helper イベント処理させるI/F 298 * @param conv イベント処理させるI/F 299 */ 300// private static void wordReader1( final File file , final TableModelHelper helper ) { 301 private static void wordReader1( final File file , final TextConverter<String,String> conv ) { 302 InputStream fis = null; 303 304 try { 305 // 6.2.0.0 (2015/02/27) TableModelHelper 変更に伴う修正 306// helper.startFile( file ); 307 308 fis = new BufferedInputStream( new FileInputStream( file ) ); // 6.2.0.0 (2015/02/27) 309 final HWPFDocument doc = new HWPFDocument( fis ); 310 311 int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 312 313 // // WordExtractor を使ったサンプル 314 // WordExtractor we = new WordExtractor( doc ); 315 // for( String txt : we.getParagraphText() ) { 316 // String text = WordExtractor.stripFields( txt ) 317 // .replaceAll( "\\x13[^\\x01]+\\x01\\x14" , "" ) 318 // .replaceAll( "\\x0b" , "\n" ).trim(); 319 // helper.value( text.trim(),rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 320 // } 321 322 // Range,Paragraph を使ったサンプル 323 final Range rng = doc.getRange(); 324 for (int pno = 0; pno < rng.numParagraphs(); pno++) { 325 final Paragraph para = rng.getParagraph(pno); 326 final String text = Range.stripFields( para.text() ) 327 .replaceAll( "\\x13[^\\x01]+\\x01\\x14" , "" ) 328 .replaceAll( "\\x0b" , "\n" ).trim(); 329// helper.value( text,rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 330 conv.change( text, String.valueOf( rowNo++ ) ); 331 } 332 333 // Range,Paragraph,CharacterRun を使ったサンプル(変な個所で文字が分断される) 334 // final Range rng = doc.getRange(); 335 // for (int pno = 0; pno < rng.numParagraphs(); pno++) { 336 // final Paragraph para = rng.getParagraph(pno); 337 // for (int cno = 0; cno < para.numCharacterRuns(); cno++) { 338 // final CharacterRun crun = para.getCharacterRun(cno); 339 // String text = Range.stripFields( crun.text() ) 340 // .replaceAll( "\\x13[^\\x01]+\\x01\\x14" , "" ) 341 // .replaceAll( "\\x0b" , "\n" ).trim(); 342 // helper.value( text,rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 343 // } 344 // } 345 } 346 catch( IOException ex ) { 347 final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 348 throw new RuntimeException( errMsg,ex ); 349 } 350 finally { 351 Closer.ioClose( fis ); 352// helper.endFile( file ); // 6.2.0.0 (2015/02/27) 353 } 354 } 355 356 /** 357 * 引数ファイル(Word)を、XWPFDocument を使用してテキスト化します。 358 * 359 * 拡張子(.docx)のファイルを処理します。 360 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 361 * 表形式オブジェクトの形で処理されます。 362 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 363 * スキップされます。 364 * 365 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 366 * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更 367 * @og.rev 6.2.4.2 (2015/05/29) 改行以外に、「。」で分割します。 368 * 369 * @param file 入力ファイル 370// * @param helper イベント処理させるI/F 371 * @param conv イベント処理させるI/F 372 */ 373// private static void wordReader2( final File file , final TableModelHelper helper ) { 374 private static void wordReader2( final File file , final TextConverter<String,String> conv ) { 375 InputStream fis = null; 376 377 try { 378 // 6.2.0.0 (2015/02/27) TableModelHelper 変更に伴う修正 379// helper.startFile( file ); 380 381 fis = new BufferedInputStream( new FileInputStream( file ) ); // 6.2.0.0 (2015/02/27) 382 final XWPFDocument doc = new XWPFDocument( fis ); 383 384 int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 385 for( final XWPFParagraph para : doc.getParagraphs() ) { 386 // for( final XWPFRun run : para.getRuns() ) { 387 // helper.value( run.toString(),rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 388 // } 389 final String text = para.getParagraphText().trim(); 390// helper.value( text,rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 391 conv.change( text, String.valueOf( rowNo++ ) ); 392 } 393 } 394 catch( IOException ex ) { 395 final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 396 throw new RuntimeException( errMsg,ex ); 397 } 398 finally { 399 Closer.ioClose( fis ); 400// helper.endFile( file ); // 6.2.0.0 (2015/02/27) 401 } 402 } 403 404 /** 405 * 引数ファイル(PoworPoint)を、HSLFSlideShow を使用してテキスト化します。 406 * 407 * 拡張子(.ppt)のファイルを処理します。 408 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 409 * 表形式オブジェクトの形で処理されます。 410 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 411 * スキップされます。 412 * 413 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 414 * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更 415 * 416 * @param file 入力ファイル 417// * @param helper イベント処理させるI/F 418 * @param conv イベント処理させるI/F 419 */ 420// private static void pptReader1( final File file , final TableModelHelper helper ) { 421 private static void pptReader1( final File file , final TextConverter<String,String> conv ) { 422 InputStream fis = null; 423 424 try { 425 // 6.2.0.0 (2015/02/27) TableModelHelper 変更に伴う修正 426// helper.startFile( file ); 427 428 fis = new BufferedInputStream( new FileInputStream( file ) ); // 6.2.0.0 (2015/02/27) 429 final SlideShow ss = new SlideShow( new HSLFSlideShow( fis ) ); 430 final Slide[] slides = ss.getSlides(); 431 int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 432 for (int sno = 0; sno < slides.length; sno++) { 433 // final String title = slides[sno].getTitle(); // title は、あまり当てにならない。 434 final TextRun[] textRun = slides[sno].getTextRuns(); 435 for (int tno = 0; tno < textRun.length; tno++) { 436 final String text = textRun[tno].getText(); 437 // データとして設定されているレコードのみイベントを発生させる。 438 if( text.length() > 0 ) { 439// helper.value( text,rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 440 conv.change( text, String.valueOf( rowNo++ ) ); 441 } 442 } 443 } 444 } 445 catch( IOException ex ) { 446 final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 447 throw new RuntimeException( errMsg,ex ); 448 } 449 finally { 450 Closer.ioClose( fis ); 451// helper.endFile( file ); // 6.2.0.0 (2015/02/27) 452 } 453 } 454 455 /** 456 * 引数ファイル(PoworPoint)を、XMLSlideShow を使用してテキスト化します。 457 * 458 * 拡張子(.pptx)のファイルを処理します。 459 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 460 * 表形式オブジェクトの形で処理されます。 461 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 462 * スキップされます。 463 * 464 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 465 * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更 466 * @og.rev 6.2.4.2 (2015/05/29) 行単位に、取り込むようにします。 467 * 468 * @param file 入力ファイル 469// * @param helper イベント処理させるI/F 470 * @param conv イベント処理させるI/F 471 */ 472// private static void pptReader2( final File file , final TableModelHelper helper ) { 473 private static void pptReader2( final File file , final TextConverter<String,String> conv ) { 474 InputStream fis = null; 475 476 try { 477 // 6.2.0.0 (2015/02/27) TableModelEvent 変更に伴う修正 478// helper.startFile( file ); 479 480 fis = new BufferedInputStream( new FileInputStream( file ) ); // 6.2.0.0 (2015/02/27) 481 final XMLSlideShow ss = new XMLSlideShow( fis ); 482 final XSLFPowerPointExtractor ext = new XSLFPowerPointExtractor( ss ); 483 final String[] vals = ext.getText().split( "\\n" ); // 6.2.4.2 (2015/05/29) 行単位に、取り込むようにします。 484 for( int row=0; row<vals.length; row++ ) { 485// helper.value( vals[row],row,0 ); 486 conv.change( vals[row], String.valueOf( row ) ); 487 } 488 489 // final XSLFSlide[] slides = ss.getSlides(); 490 // final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 491 // int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 492 // for (int sno = 0; sno < slides.length; sno++) { 493 // buf.setLength(0); // Clearの事 494 // 495 // // final XSLFTextShape[] shp = slides[sno].getPlaceholders(); 496 // final XSLFShape[] shp = slides[sno].getShapes(); 497 // for (int tno = 0; tno < shp.length; tno++) { 498 // // buf.append( shp[tno].getText() ); 499 // buf.append( shp[tno].toString() ); 500 // } 501 // // String text = buf.toString().trim(); 502 // // event.value( text,rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 503 // helper.value( buf.toString(),rowNo++,0 ); // 6.2.4.2 (2015/05/29) trim() しません。 504 // } 505 } 506 catch( IOException ex ) { 507 final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 508 throw new RuntimeException( errMsg,ex ); 509 } 510 finally { 511 Closer.ioClose( fis ); 512// helper.endFile( file ); // 6.2.0.0 (2015/02/27) 513 } 514 } 515 516 /** 517 * 引数ファイル(Excel)を、テキスト化します。 518 * 519 * TableModelHelper を与えることで、EXCELデータをテキスト化できます。 520 * ここでは、HSSF(.xls)形式を処理します。 521 * シート名、セル、テキストオブジェクトをテキスト化します。 522 * 523 * @og.rev 6.2.5.0 (2015/06/05) 新規作成 524 * 525 * @param file 入力ファイル 526// * @param helper イベントオブジェクト(TableModelHelper) 527 * @param conv イベント処理させるI/F 528 * @see org.opengion.fukurou.model.ExcelModel 529 */ 530// public static void excelReader1( final File file , final TableModelHelper helper ) { 531 public static void excelReader1( final File file , final TextConverter<String,String> conv ) { 532// excelReader2( file , helper ); 533 excelReader2( file , conv ); 534 } 535 536 /** 537 * 引数ファイル(Excel)を、テキスト化します。 538 * 539 * TableModelHelper を与えることで、EXCELデータをテキスト化できます。 540 * ここでは、ExcelModelを使用して、(.xlsx , .xlsm)形式を処理します。 541 * シート名、セル、テキストオブジェクトをテキスト化します。 542 * 543 * @og.rev 6.2.5.0 (2015/06/05) 新規作成 544 * @og.rev 6.3.1.0 (2015/06/28) TextConverterに、引数(cmnt)を追加 545 * 546 * @param file 入力ファイル 547// * @param helper イベントオブジェクト(TableModelHelper) 548 * @param conv イベント処理させるI/F 549 * @see org.opengion.fukurou.model.ExcelModel 550 */ 551// public static void excelReader2( final File file , final TableModelHelper helper ) { 552 public static void excelReader2( final File file , final TextConverter<String,String> conv ) { 553 final ExcelModel excel = new ExcelModel( file, true ); 554 555 // textConverter を使いますが、テキストを読み込むだけで、変換しません。 556 excel.textConverter( 557 new TextConverter<String,String>() { 558 private int row ; 559 /** 560 * 入力文字列を、変換します。 561 * 562 * @param val 入力文字列 563 * @param cmnt コメント 564 * @return 変換文字列(変換されない場合は、null) 565 */ 566 @Override 567// public String change( final String val ) { // すべてのテキストを読み取る。 568 public String change( final String val , final String cmnt ) { // すべてのテキストを読み取る。 569// helper.value( val , row , 0 ); 570// helper.value( cmnt , row++ , 1 ); 571 conv.change( val,cmnt ); 572 return null; // nullを返せば、変換しません。 573 } 574 } 575 ); 576 } 577 578 /** 579 * Excelの行列記号を、行番号と列番号に分解します。 580 * 581 * Excelの行列記号とは、A1 , B5 , AA23 などの形式を指します。 582 * これを、行番号と列番号に分解します。例えば、A1→0行0列、B5→4行1列、AA23→22行26列 となります。 583 * 分解した結果は、内部変数の、rowNo と colNo にセットされます。 584 * これらは、0 から始まる int型の数字で表します。 585 * 586 * @行-列形式 587 * 行列は、EXCELオブジェクトに準拠するため、0から始まる整数です。 588 * 0-0 ⇒ A1 , 1-0 ⇒ A2 , 0-1 ⇒ B1 になります。 589 * AEXCEL表記 590 * EXCEL表記に準拠した、A1,A2,B1 の記述も処理できるように対応します。 591 * なお、A1,A2,B1 の記述は、必ず、英字1文字+数字 にしてください。(A〜Zまで) 592 * BEXCELシート名をキーに割り当てるために、"SHEET" という記号に対応します。 593 * rowNo = -1 をセットします。 594 * 595 * @og.rev 6.0.3.0 (2014/11/13) 新規作成 596 * @og.rev 6.2.6.0 (2015/06/19) 行-列形式と、SHEET文字列判定を採用。 597 * 598 * @param kigo Excelの行列記号( A1 , B5 , AA23 など ) 599 * @return 行と列の番号を持った配列([0]=行=ROW , [1]=列=COL) 600 * @og.rtnNotNull 601 */ 602 public static int[] kigo2rowCol( final String kigo ) { 603 int rowNo = 0; 604 int colNo = -1; // +1 して、26 かける処理をしているので、辻褄合わせ 605 606 // 6.2.6.0 (2015/06/19) 行-列形式と、SHEET文字列判定を採用。 607 if( "SHEET".equalsIgnoreCase( kigo ) ) { 608 rowNo = -1; 609 } 610 else { 611 final int adrs = kigo.indexOf( '-' ); 612 if( adrs > 0 ) { 613 rowNo = Integer.parseInt( kigo.substring( 0,adrs ) ); 614 colNo = Integer.parseInt( kigo.substring( adrs+1 ) ); 615 } 616 else { 617 for( int i=0; i<kigo.length(); i++ ) { 618 final char ch = kigo.charAt(i); 619 if( 'A' <= ch && ch <= 'Z' ) { colNo = (colNo+1)*26 + ch-'A'; } 620 else { 621 // アルファベットでなくなったら、残りは 行番号(ただし、-1する) 622 rowNo = Integer.parseInt( kigo.substring( i ) ) -1; 623 break; 624 } 625 } 626 } 627 } 628 return new int[] { rowNo,colNo }; 629 } 630 631 /** 632 * セルオブジェクト(Cell)から値を取り出します。 633 * 634 * セルオブジェクトが存在しない場合は、null を返します。 635 * それ以外で、うまく値を取得できなかった場合は、ゼロ文字列を返します。 636 * 637 * @og.rev 3.8.5.3 (2006/08/07) 取り出し方法を少し修正 638 * @og.rev 5.5.1.2 (2012/04/06) フォーマットセルを実行して、その結果を再帰的に処理する。 639 * @og.rev 6.0.3.0 (2014/11/13) セルフォーマットエラー時に、RuntimeException を throw しない。 640 * 641 * @param oCell EXCELのセルオブジェクト 642 * 643 * @return セルの値 644 */ 645 public static String getValue( final Cell oCell ) { 646 if( oCell == null ) { return null; } 647 String strText = ""; 648 final int nCellType = oCell.getCellType(); 649 switch(nCellType) { 650 case Cell.CELL_TYPE_NUMERIC: 651 strText = getNumericTypeString( oCell ); 652 break; 653 case Cell.CELL_TYPE_STRING: 654 // POI3.0 strText = oCell.getStringCellValue(); 655 final RichTextString richText = oCell.getRichStringCellValue(); 656 if( richText != null ) { 657 strText = richText.getString(); 658 } 659 break; 660 case Cell.CELL_TYPE_FORMULA: 661 // POI3.0 strText = oCell.getStringCellValue(); 662 // 5.5.1.2 (2012/04/06) フォーマットセルを実行して、その結果を再帰的に処理する。 663 final Workbook wb = oCell.getSheet().getWorkbook(); 664 final CreationHelper crateHelper = wb.getCreationHelper(); 665 final FormulaEvaluator evaluator = crateHelper.createFormulaEvaluator(); 666 667 try { 668 strText = getValue(evaluator.evaluateInCell(oCell)); 669 } 670 catch ( Throwable th ) { 671 final String errMsg = "セルフォーマットが解析できません。Formula=[" + oCell.getCellFormula() + "]" 672 + CR + getCellMsg( oCell ); 673 // throw new RuntimeException( errMsg,th ); 674 System.err.println( errMsg ); // 6.0.3.0 (2014/11/13) 675 System.err.println( StringUtil.ogStackTrace( th ) ); 676 } 677 break; 678 case Cell.CELL_TYPE_BOOLEAN: 679 strText = String.valueOf(oCell.getBooleanCellValue()); 680 break; 681 case Cell.CELL_TYPE_BLANK : 682 break; 683 case Cell.CELL_TYPE_ERROR: 684 break; 685 default : 686 break; 687 } 688 return strText ; 689 } 690 691 /** 692 * セル値が数字の場合に、数字か日付かを判断して、対応する文字列を返します。 693 * 694 * @og.rev 3.8.5.3 (2006/08/07) 新規追加 695 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。 696 * @og.rev 6.3.1.0 (2015/06/28) ExcelStyleFormat を使用します。 697 * 698 * @param oCell EXCELのセルオブジェクト 699 * 700 * @return 数字の場合は、文字列に変換した結果を、日付の場合は、"yyyyMMddHHmmss" 形式で返します。 701 */ 702 public static String getNumericTypeString( final Cell oCell ) { 703 final String strText ; 704 705 final double dd = oCell.getNumericCellValue() ; 706 if( DateUtil.isCellDateFormatted( oCell ) ) { 707 // strText = HybsDateUtil.getDate( DateUtil.getJavaDate( dd ).getTime() , "yyyyMMddHHmmss" ); // 5.5.7.2 (2012/10/09) HybsDateUtil を利用 708 strText = ExcelStyleFormat.dateFormat( dd ); 709 } 710 else { 711 // final NumberFormat numFormat = NumberFormat.getInstance(); 712 // if( numFormat instanceof DecimalFormat ) { 713 // ((DecimalFormat)numFormat).applyPattern( "#.####" ); 714 // } 715 // strText = numFormat.format( dd ); 716 final String fmrs = oCell.getCellStyle().getDataFormatString(); 717 strText = ExcelStyleFormat.getNumberValue( fmrs,dd ); 718 } 719 return strText ; 720 } 721 722 /** 723 * 全てのSheetに対して、autoSizeColumn設定を行います。 724 * 725 * 重たい処理なので、ファイルの書き出し直前に一度だけ実行するのがよいでしょう。 726 * autoSize設定で、カラム幅が大きすぎる場合、現状では、 727 * 初期カラム幅のmaxColCount倍を限度に設定します。 728 * ただし、maxColCount がマイナスの場合は、無制限になります。 729 * 730 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 731 * 732 * @param wkbook 処理対象のWorkbook 733 * @param maxColCount 最大幅を標準セル幅の何倍にするかを指定。マイナスの場合は、無制限 734 * @param dataStRow データ行の開始位置。未設定時は、-1 735 */ 736 public static void autoCellSize( final Workbook wkbook , final int maxColCount , final int dataStRow ) { 737 final int shCnt = wkbook.getNumberOfSheets(); 738 739 for( int shNo=0; shNo<shCnt; shNo++ ) { 740 final Sheet sht = wkbook.getSheetAt( shNo ); 741 final int defW = sht.getDefaultColumnWidth(); // 標準カラムの文字数 742 final int maxWidth = defW*256*maxColCount ; // Widthは、文字数(文字幅)*256*最大セル数 743 744 int stR = sht.getFirstRowNum(); 745 final int edR = sht.getLastRowNum(); 746 747 final Row rowObj = sht.getRow( stR ); 748 final int stC = rowObj.getFirstCellNum(); 749 final int edC = rowObj.getLastCellNum(); // 含まない 750 751 // SheetUtil を使用して、計算範囲を指定します。 752 if( stR < dataStRow ) { stR = dataStRow; } // 計算範囲 753 for( int colNo=stC; colNo<edC; colNo++ ) { 754 final double wpx = SheetUtil.getColumnWidth( sht,colNo,true,stR,edR ); 755 if( wpx >= 0.0d ) { // Cellがないと、マイナス値が戻る。 756 int wd = (int)Math.ceil(wpx * 256) ; 757 if( maxWidth >= 0 && wd > maxWidth ) { wd = maxWidth; } // 最大値が有効な場合は、置き換える 758 sht.setColumnWidth( colNo,wd ); 759 } 760 } 761 762 // Sheet#autoSizeColumn(int) を使用して、自動計算させる場合。 763 // for( int colNo=stC; colNo<edC; colNo++ ) { 764 // sht.autoSizeColumn( colNo ); 765 // if( maxWidth >= 0 ) { // 最大値が有効な場合は、置き換える 766 // int wd = sht.getColumnWidth( colNo ); 767 // if( wd > maxWidth ) { sht.setColumnWidth( colNo,maxWidth ); } 768 // } 769 // } 770 } 771 } 772 773 /** 774 * 指定の Workbook の全Sheetを対象に、空行を取り除き、全体をシュリンクします。 775 * 776 * この処理は、#saveFile( String ) の直前に行うのがよいでしょう。 777 * 778 * ここでは、Row を逆順にスキャンし、Cellが 存在しない間は、行を削除します。 779 * 途中の空行の削除ではなく、最終行かららの連続した空行の削除です。 780 * 781 * isCellDel=true を指定すると、Cellの末尾削除を行います。 782 * 有効行の最後のCellから空セルを削除していきます。 783 * 表形式などの場合は、Cellのあるなしで、レイアウトが崩れる場合がありますので 784 * 処理が不要な場合は、isCellDel=false を指定してください。 785 * 786 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 787 * @og.rev 6.0.2.3 (2014/10/10) CellStyle の有無も判定基準に含めます。 788 * @og.rev 6.0.2.5 (2014/10/31) Cellの開始、終了番号が、マイナスのケースの対応 789 * 790 * @param wkbook 処理対象のWorkbook 791 * @param isCellDel Cellの末尾削除を行うかどうか(true:行う/false:行わない) 792 */ 793 public static void activeWorkbook( final Workbook wkbook , final boolean isCellDel ) { 794 final int shCnt = wkbook.getNumberOfSheets(); 795 for( int shNo=0; shNo<shCnt; shNo++ ) { 796 final Sheet sht = wkbook.getSheetAt( shNo ); 797 798 final int stR = sht.getFirstRowNum(); 799 final int edR = sht.getLastRowNum(); 800 801 boolean isRowDel = true; // 行の削除は、Cellが見つかるまで。 802 for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) { // 逆順に処理します。 803 final Row rowObj = sht.getRow( rowNo ); 804 if( rowObj != null ) { 805 final int stC = rowObj.getFirstCellNum(); 806 final int edC = rowObj.getLastCellNum(); 807 for( int colNo=edC; colNo>=stC && colNo>=0; colNo-- ) { // 6.0.2.5 (2014/10/31) stC,edC が、マイナスのケースがある。 808 final Cell colObj = rowObj.getCell( colNo ); 809 if( colObj != null ) { 810 final String val = getValue( colObj ); 811 if( colObj.getCellType() != Cell.CELL_TYPE_BLANK && val != null && val.length() > 0 ) { 812 isRowDel = false; // 一つでも現れれば、行の削除は中止 813 break; 814 } 815 // 6.0.2.3 (2014/10/10) CellStyle の有無も判定基準に含めます。 816 else if( colObj.getCellStyle() != null ) { 817 isRowDel = false; // 一つでも現れれば、行の削除は中止 818 break; 819 } 820 else if( isCellDel ) { 821 rowObj.removeCell( colObj ); // CELL_TYPE_BLANK の場合は、削除 822 } 823 } 824 } 825 if( isRowDel ) { sht.removeRow( rowObj ); } 826 else if( !isCellDel ) { break; } // Cell の末尾削除を行わない場合は、break すればよい。 827 } 828 } 829 } 830 } 831 832 /** 833 * ファイルから、Workbookオブジェクトを新規に作成します。 834 * 835 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 836 * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更 837 * 838 * @param file 入力ファイル 839 * @return Workbookオブジェクト 840 * @og.rtnNotNull 841 */ 842 public static Workbook createWorkbook( final File file ) { 843 InputStream fis = null; 844 try { 845 // File オブジェクトでcreate すると、ファイルがオープンされたままになってしまう。 846 fis = new BufferedInputStream( new FileInputStream( file ) ); 847 return WorkbookFactory.create( fis ); 848 } 849 catch( IOException ex ) { 850 final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 851 throw new RuntimeException( errMsg,ex ); 852 } 853 catch( InvalidFormatException ex ) { 854 final String errMsg = "ファイル形式エラー[" + file + "]" + CR + ex.getMessage() ; 855 throw new RuntimeException( errMsg,ex ); 856 } 857 finally { 858 Closer.ioClose( fis ); 859 } 860 } 861 862 /** 863 * シート一覧を、Workbook から取得します。 864 * 865 * 取得元が、Workbook なので、xls , xlsx どちらの形式でも取り出せます。 866 * 867 * EXCEL上のシート名を、配列で返します。 868 * 869 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 870 * 871 * @param wkbook Workbookオブジェクト 872 * @return シート名の配列 873 */ 874 public static String[] getSheetNames( final Workbook wkbook ) { 875 final int shCnt = wkbook.getNumberOfSheets(); 876 877 String[] shtNms = new String[shCnt]; 878 879 for( int i=0; i<shCnt; i++ ) { 880 final Sheet sht = wkbook.getSheetAt( i ); 881 shtNms[i] = sht.getSheetName(); 882 } 883 884 return shtNms; 885 } 886 887 /** 888 * 名前定義一覧を取得します。 889 * 890 * EXCEL上に定義された名前を、配列で返します。 891 * ここでは、名前とFormulaをタブで連結した文字列を配列で返します。 892 * Name オブジェクトを削除すると、EXCELが開かなくなったりするので、 893 * 取りあえず一覧を作成して、手動で削除してください。 894 * なお、名前定義には、非表示というのがありますので、ご注意ください。 895 * 896 * ◆ 非表示になっている名前の定義を表示にする EXCEL VBA マクロ 897 * http://dev.classmethod.jp/tool/excel-delete-name/ 898 * Sub VisibleNames() 899 * Dim name 900 * For Each name In ActiveWorkbook.Names 901 * If name.Visible = False Then 902 * name.Visible = True 903 * End If 904 * Next 905 * MsgBox "すべての名前の定義を表示しました。", vbOKOnly 906 * End Sub 907 * 908 * ※ EXCEL2010 数式タブ→名前の管理 で、複数選択で、削除できます。 909 * ただし、非表示の名前定義は、先に表示しないと、削除できません。 910 * ◆ 名前の一括削除 EXCEL VBA マクロ 911 * http://komitsudo.blog70.fc2.com/blog-entry-104.html 912 * Sub DeleteNames() 913 * Dim name 914 * On Error Resume Next 915 * For Each name In ActiveWorkbook.Names 916 * If Not name.BuiltIn Then 917 * name.Delete 918 * End If 919 * Next 920 * MsgBox "すべての名前の定義を削除しました。", vbOKOnly 921 * End Sub 922 * 923 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 924 * 925 * @param wkbook Workbookオブジェクト 926 * @return 名前定義(名前+TAB+Formula)の配列 927 * @og.rtnNotNull 928 */ 929 public static String[] getNames( final Workbook wkbook ) { 930 final int cnt = wkbook.getNumberOfNames(); 931 932 final Set<String> nmSet = new TreeSet(); 933 934 for( int i=0; i<cnt; i++ ) { 935 String name = null; 936 String ref = null; 937 938 final Name nm = wkbook.getNameAt(i); 939 try { 940 name = nm.getNameName(); 941 ref = nm.getRefersToFormula(); 942 } 943 // catch( Exception ex ) { // 6.1.0.0 (2014/12/26) refactoring 944 catch( RuntimeException ex ) { 945 final String errMsg = "POIUtil:RefersToFormula Error! name=[" + name + "]" + ex.getMessage() ; 946 System.out.println( errMsg ); 947 // Excel97形式の場合、getRefersToFormula() でエラーが発生することがある。 948 } 949 950 nmSet.add( name + "\t" + ref ); 951 952 // 削除するとEXCELが壊れる? なお、削除時には逆順で廻さないとアドレスがずれます。 953 // if( nm.isDeleted() ) { wkbook.removeName(i); } 954 } 955 956 return nmSet.toArray( new String[nmSet.size()] ); 957 } 958 959 /** 960 * 書式のスタイル一覧を取得します。 961 * 962 * EXCEL上に定義された書式のスタイルを、配列で返します。 963 * 書式のスタイルの名称は、CellStyle にメソッドが定義されていません。 964 * 実クラスである HSSFCellStyle にキャストして使用する 965 * 必要があります。(XSSFCellStyle にも名称を取得するメソッドがありません。) 966 * 967 * ※ EXCEL2010 ホームタブ→セルのスタイル は、一つづつしか削除できません。 968 * マクロは、開発タブ→Visual Basic で、挿入→標準モジュール を開き 969 * テキストを張り付けてください。 970 * 実行は、開発タブ→マクロ で、マクロ名を選択して、実行します。 971 * 最後は、削除してください。 972 * 973 * ◆ スタイルの一括削除 EXCEL VBA マクロ 974 * http://komitsudo.blog70.fc2.com/blog-entry-104.html 975 * Sub DeleteStyle() 976 * Dim styl 977 * On Error Resume Next 978 * For Each styl In ActiveWorkbook.Styles 979 * If Not styl.BuiltIn Then 980 * styl.Delete 981 * End If 982 * Next 983 * MsgBox "すべての追加スタイルを削除しました。", vbOKOnly 984 * End Sub 985 * 986 * ◆ 名前の表示、削除、スタイルの削除の一括実行 EXCEL VBA マクロ 987 * Sub AllDelete() 988 * Call VisibleNames 989 * Call DeleteNames 990 * Call DeleteStyle 991 * MsgBox "すべての処理を完了しました。", vbOKOnly 992 * End Sub 993 * 994 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 995 * 996 * @param wkbook Workbookオブジェクト 997 * @return 書式のスタイル一覧 998 * @og.rtnNotNull 999 */ 1000 public static String[] getStyleNames( final Workbook wkbook ) { 1001 final int cnt = wkbook.getNumCellStyles(); // return 値は、short 1002 1003 final Set<String> nmSet = new TreeSet(); 1004 1005 for( int s=0; s<cnt; s++ ) { 1006 final CellStyle cs = wkbook.getCellStyleAt( (short)s ); 1007 if( cs instanceof HSSFCellStyle ) { 1008 final HSSFCellStyle hcs = (HSSFCellStyle)cs; 1009 final String name = hcs.getUserStyleName(); 1010 if( name != null ) { nmSet.add( name ); } 1011 else { // この処理は不要かも。 1012 final HSSFCellStyle pst = hcs.getParentStyle(); 1013 if( pst != null ) { 1014 final String pname = pst.getUserStyleName(); 1015 if( pname != null ) { nmSet.add( pname ); } 1016 } 1017 } 1018 } 1019 } 1020 1021 return nmSet.toArray( new String[nmSet.size()] ); 1022 } 1023 1024 /** 1025 * セル情報を返します。 1026 * 1027 * エラー発生時に、どのセルでエラーが発生したかの情報を取得できるようにします。 1028 * 1029 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1030 * @og.rev 6.0.3.0 (2014/11/13) セル情報を作成する時に、値もセットします。 1031 * @og.rev 6.2.2.0 (2015/03/27) celKigo を求めるロジック変更 1032 * @og.rev 6.3.1.0 (2015/06/28) rowNo(行番号)も引数に取るようにします。 1033 * 1034 * @param oCell EXCELのセルオブジェクト 1035 * @return セル情報の文字列 1036 */ 1037 public static String getCellMsg( final Cell oCell ) { 1038 String lastMsg = null; 1039 1040 if( oCell != null ) { 1041 final String shtNm = oCell.getSheet().getSheetName(); 1042 final int rowNo = oCell.getRowIndex(); 1043 final int celNo = oCell.getColumnIndex(); 1044 1045 // 6.0.3.0 (2014/11/13) セル情報を作成する時に、値もセットします。 1046 lastMsg = " Sheet=" + shtNm + ", Row=" + rowNo + ", Cel=" + celNo 1047// + "(" + getCelKigo(celNo) + ") , Val=" + oCell.toString() ; 1048 + "(" + getCelKigo(rowNo,celNo) + ") , Val=" + oCell.toString() ; 1049 } 1050 1051 return lastMsg; 1052 } 1053 1054 /** 1055 * Excelの行番号,列番号より、セル記号を求めます。 1056 * 1057 * 行番号は、0から始まる数字ですが、記号化する場合は、1から始まります。 1058 * Excelの列記号とは、A,B,C,…,Z,AA,AB,…,ZZ,AAA,AAB,… と続きます。 1059 * つまり、アルファベットだけの、26進数になります。(ゼロの扱いが少し特殊です) 1060 * 列番号は、0から始まる数字で、0=A,1=B,2=C,…,25=Z,26=AA,27=AB,…,701=ZZ,702=AAA,703=AAB,… 1061 * EXCELの行列記号にする場合は、この列記号に、行番号を、+1して付ければよいだけです。 1062 * (※ 列番号に+1するのは、内部では0から始まる列番号ですが、表示上は1から始まります) 1063 * 1064 * @og.rev 6.2.2.0 (2015/03/27) celKigo を求めるロジック変更 1065 * @og.rev 6.3.1.0 (2015/06/28) rowNo(行番号)も引数に取るようにします。 1066 * 1067 * @param rowNo 行番号(0,1,2,…) 1068 * @param colNo 列番号(0,1,2,…) 1069 * @return Excelの列記号(A1,B2,C3,…) 1070 */ 1071// public static String getCelKigo( final int colNo ) { 1072 public static String getCelKigo( final int rowNo,final int colNo ) { 1073 final StringBuilder buf = new StringBuilder(); 1074 int cnt = colNo; 1075 while( cnt >= 26 ) { 1076 buf.append( (char)('A'+cnt%26) ); 1077 cnt = cnt/26-1; 1078 } 1079 buf.append( (char)('A'+cnt%26) ) 1080 .reverse() // append で逆順に付けているので、反転して返します。 1081 .append( rowNo+1 ); 1082 1083// return buf.reverse().toString(); // append で逆順に付けているので、反転して返します。 1084 return buf.toString(); 1085 } 1086 1087 /** 1088 * アプリケーションのサンプルです。 1089 * 1090 * 入力ファイル名 は必須で、第一引数固定です。 1091 * 第二引数は、処理方法で、-ALL か、-LINE を指定します。何も指定しなければ、-ALL です。 1092 * 第三引数を指定した場合は、Encode を指定します。 1093 * 1094 * Usage: java org.opengion.fukurou.model.POIUtil 入力ファイル名 [処理方式] [エンコード] 1095 * -A(LL) ・・・ ALL 一括処理(初期値) 1096 * -L(INE) ・・・ LINE 行単位処理 1097 * -S(heet) ・・・ Sheet名一覧 1098 * -N(AME) ・・・ NAME:名前定義 1099 * -C(ellStyle) ・・・ CellStyle:書式のスタイル 1100 * 1101 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1102 * @og.rev 6.2.3.0 (2015/05/01) パラメータ変更、textReader → extractor に変更 1103 * @og.rev 6.2.4.2 (2015/05/29) 引数判定の true/false の処理が逆でした。 1104 * 1105 * @param args コマンド引数配列 1106 */ 1107 public static void main( final String[] args ) { 1108 final String usageMsg = "Usage: java org.opengion.fukurou.model.POIUtil 入力ファイル名 [処理方式] [エンコード]" + "\n" + 1109 "\t -A(LL) ・・・ ALL 一括処理(初期値) \n" + 1110 "\t -L(INE) ・・・ LINE 行単位処理 \n" + 1111 "\t -S(heet) ・・・ Sheet名一覧 \n" + 1112 "\t -N(AME) ・・・ NAME:名前定義 \n" + 1113 "\t -C(ellStyle) ・・・ CellStyle:書式のスタイル \n" ; 1114 if( args.length == 0 ) { 1115 System.err.println( usageMsg ); 1116 return ; 1117 } 1118 1119 final File file = new File( args[0] ); 1120 final char type = (args.length >= 2) ? args[1].charAt(1) : 'A' ; 1121 final String encode = (args.length >= 3) ? args[2] : null ; // 6.2.4.2 (2015/05/29) true/false の処理が逆でした。 1122 1123 switch( type ) { 1124 case 'A' : if( encode == null ) { 1125 System.out.println( POIUtil.extractor( file ) ); 1126 } 1127 else { 1128 System.out.println( POIUtil.extractor( file,encode ) ); 1129 } 1130 break; 1131// case 'L' : final TableModelHelper helper = 1132// new TableModelHelper() { 1133// /** 1134// * 読み取り状態の時に、rowNo,colNo にあるセルの値を引数にイベントが発生します。 1135// * 1136// * @param val 文字列値 1137// * @param rowNo 行番号(0〜) 1138// * @param colNo 列番号(0〜) 1139// * @return 読み取りするかどうか(true:読み取りする/false:読み取りしない) 1140// */ 1141// protected boolean value( final String val,final int rowNo,final int colNo ) { 1142// System.out.println( "RC[" + rowNo + "," + colNo + "]=" + val ); 1143// return true; 1144// } 1145// } ; 1146 case 'L' : final TextConverter<String,String> conv = 1147 new TextConverter<String,String>() { 1148 /** 1149 * 入力文字列を、変換します。 1150 * 1151 * @param val 入力文字列 1152 * @param cmnt コメント 1153 * @return 変換文字列(変換されない場合は、null) 1154 */ 1155 @Override 1156 public String change( final String val , final String cmnt ) { 1157 System.out.println( "val=" + val + " , cmnt=" + cmnt ); 1158 return null; 1159 } 1160 }; 1161 1162 if( encode == null ) { 1163// POIUtil.textReader( file,helper ); 1164 POIUtil.textReader( file,conv ); 1165 } 1166 else { 1167// POIUtil.textReader( file,helper,encode ); 1168 POIUtil.textReader( file,conv,encode ); 1169 } 1170 break; 1171 case 'S' : final String[] shts = POIUtil.getSheetNames( POIUtil.createWorkbook(file) ); 1172 System.out.println( "No:\tSheetName" ); 1173 for( int i=0; i<shts.length; i++ ) { 1174 System.out.println( i + "\t" + shts[i] ); 1175 } 1176 break; 1177 case 'N' : final String[] nms = POIUtil.getNames( POIUtil.createWorkbook(file) ); 1178 System.out.println( "No:\tName\tFormula" ); 1179 for( int i=0; i<nms.length; i++ ) { 1180 System.out.println( i + "\t" + nms[i] ); 1181 } 1182 break; 1183 case 'C' : final String[] sns = POIUtil.getStyleNames( POIUtil.createWorkbook(file) ); 1184 System.out.println( "No:\tStyleName" ); 1185 for( int i=0; i<sns.length; i++ ) { 1186 System.out.println( i + "\t" + sns[i] ); 1187 } 1188 break; 1189 default : System.err.println( usageMsg ); 1190 break; 1191 } 1192 } 1193}