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