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