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