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 oCell.setCellValue( Double.parseDouble( val ) ); // 7.3.0.0 (2021/01/06) SpotBugs 疑わしいプリミティブ値のボクシング 701 break; 702 // case Cell.CELL_TYPE_BOOLEAN: // 6.5.0.0 (2016/09/30) poi-3.12 703 case BOOLEAN: // 6.5.0.0 (2016/09/30) poi-3.15 704 oCell.setCellValue( "true".equalsIgnoreCase( val ) ); 705 break; 706 default : 707 oCell.setCellValue( val ); 708 break; 709 } 710 } 711 712 /** 713 * セル値が数字の場合に、数字か日付かを判断して、対応する文字列を返します。 714 * 715 * @og.rev 3.8.5.3 (2006/08/07) 新規追加 716 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。 717 * @og.rev 6.3.1.0 (2015/06/28) ExcelStyleFormat を使用します。 718 * 719 * @param oCell EXCELのセルオブジェクト 720 * 721 * @return 数字の場合は、文字列に変換した結果を、日付の場合は、"yyyyMMddHHmmss" 形式で返します。 722 */ 723 public static String getNumericTypeString( final Cell oCell ) { 724 final String strText ; 725 726 final double dd = oCell.getNumericCellValue() ; 727 if( DateUtil.isCellDateFormatted( oCell ) ) { 728 // strText = DateSet.getDate( DateUtil.getJavaDate( dd ).getTime() , "yyyyMMddHHmmss" ); // 5.5.7.2 (2012/10/09) HybsDateUtil を利用 729 strText = ExcelStyleFormat.dateFormat( dd ); 730 } 731 else { 732 // final NumberFormat numFormat = NumberFormat.getInstance(); 733 // if( numFormat instanceof DecimalFormat ) { 734 // ((DecimalFormat)numFormat).applyPattern( "#.####" ); 735 // } 736 // strText = numFormat.format( dd ); 737 final String fmrs = oCell.getCellStyle().getDataFormatString(); 738 strText = ExcelStyleFormat.getNumberValue( fmrs,dd ); 739 } 740 return strText ; 741 } 742 743 /** 744 * 全てのSheetに対して、autoSizeColumn設定を行います。 745 * 746 * 重たい処理なので、ファイルの書き出し直前に一度だけ実行するのがよいでしょう。 747 * autoSize設定で、カラム幅が大きすぎる場合、現状では、 748 * 初期カラム幅のmaxColCount倍を限度に設定します。 749 * ただし、maxColCount がマイナスの場合は、無制限になります。 750 * 751 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 752 * @og.rev 6.8.2.4 (2017/11/20) rowObj のnull対策(poi-3.17) 753 * 754 * @param wkbook 処理対象のWorkbook 755 * @param maxColCount 最大幅を標準セル幅の何倍にするかを指定。マイナスの場合は、無制限 756 * @param dataStRow データ行の開始位置。未設定時は、-1 757 */ 758 public static void autoCellSize( final Workbook wkbook , final int maxColCount , final int dataStRow ) { 759 final int shCnt = wkbook.getNumberOfSheets(); 760 761 for( int shNo=0; shNo<shCnt; shNo++ ) { 762 final Sheet sht = wkbook.getSheetAt( shNo ); 763 final int defW = sht.getDefaultColumnWidth(); // 標準カラムの文字数 764 final int maxWidth = defW*256*maxColCount ; // Widthは、文字数(文字幅)*256*最大セル数 765 766 int stR = sht.getFirstRowNum(); 767 final int edR = sht.getLastRowNum(); 768 769 // 6.8.2.4 (2017/11/20) rowObj のnull対策(poi-3.17) 770 // poi-3.15 でも同じ現象が出ていますが、sht.getRow( stR ) で、Rowオブジェクトにnullが返ってきます。 771 // なんとなく、最後の行だけ、返ってきている感じです。 772 // 頻繁には使わないと思いますので、最大カラム数=256 として処理します。 773 774 final Row rowObj = sht.getRow( stR ); 775 // Row rowObj = sht.getRow( stR ); 776 // if( rowObj == null ) { 777 // for( int i=stR+1; i<edR; i++ ) { 778 // rowObj = sht.getRow( i ); 779 // if( rowObj != null ) { break; } 780 // } 781 // } 782 783 final int stC = rowObj == null ? 0 : rowObj.getFirstCellNum(); // 6.8.2.4 (2017/11/20) rowObj のnull対策 784 final int edC = rowObj == null ? 256 : rowObj.getLastCellNum(); // 含まない (xlsxでは、最大 16,384 列 785 786 // SheetUtil を使用して、計算範囲を指定します。 787 if( stR < dataStRow ) { stR = dataStRow; } // 計算範囲 788 for( int colNo=stC; colNo<edC; colNo++ ) { 789 final double wpx = SheetUtil.getColumnWidth( sht,colNo,true,stR,edR ); 790 if( wpx >= 0.0d ) { // Cellがないと、マイナス値が戻る。 791 int wd = (int)Math.ceil(wpx * 256) ; 792 if( maxWidth >= 0 && wd > maxWidth ) { wd = maxWidth; } // 最大値が有効な場合は、置き換える 793 sht.setColumnWidth( colNo,wd ); 794 } 795 } 796 797 // Sheet#autoSizeColumn(int) を使用して、自動計算させる場合。 798 // for( int colNo=stC; colNo<edC; colNo++ ) { 799 // sht.autoSizeColumn( colNo ); 800 // if( maxWidth >= 0 ) { // 最大値が有効な場合は、置き換える 801 // int wd = sht.getColumnWidth( colNo ); 802 // if( wd > maxWidth ) { sht.setColumnWidth( colNo,maxWidth ); } 803 // } 804 // } 805 } 806 } 807 808 /** 809 * 指定の Workbook の全Sheetを対象に、空行を取り除き、全体をシュリンクします。 810 * 811 * この処理は、#saveFile( String ) の直前に行うのがよいでしょう。 812 * 813 * ここでは、Row を逆順にスキャンし、Cellが 存在しない間は、行を削除します。 814 * 途中の空行の削除ではなく、最終行かららの連続した空行の削除です。 815 * 816 * isCellDel=true を指定すると、Cellの末尾削除を行います。 817 * 有効行の最後のCellから空セルを削除していきます。 818 * 表形式などの場合は、Cellのあるなしで、レイアウトが崩れる場合がありますので 819 * 処理が不要な場合は、isCellDel=false を指定してください。 820 * 821 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 822 * @og.rev 6.0.2.3 (2014/10/10) CellStyle の有無も判定基準に含めます。 823 * @og.rev 6.0.2.5 (2014/10/31) Cellの開始、終了番号が、マイナスのケースの対応 824 * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX) 825 * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] CellのgetCellTypeEnum()は推奨されません (POI4.0.0) 826 * 827 * @param wkbook 処理対象のWorkbook 828 * @param isCellDel Cellの末尾削除を行うかどうか(true:行う/false:行わない) 829 */ 830 public static void activeWorkbook( final Workbook wkbook , final boolean isCellDel ) { 831 final int shCnt = wkbook.getNumberOfSheets(); 832 for( int shNo=0; shNo<shCnt; shNo++ ) { 833 final Sheet sht = wkbook.getSheetAt( shNo ); 834 835 final int stR = sht.getFirstRowNum(); 836 final int edR = sht.getLastRowNum(); 837 838 boolean isRowDel = true; // 行の削除は、Cellが見つかるまで。 839 for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) { // 逆順に処理します。 840 final Row rowObj = sht.getRow( rowNo ); 841 if( rowObj != null ) { 842 final int stC = rowObj.getFirstCellNum(); 843 final int edC = rowObj.getLastCellNum(); 844 for( int colNo=edC; colNo>=stC && colNo>=0; colNo-- ) { // 6.0.2.5 (2014/10/31) stC,edC が、マイナスのケースがある。 845 final Cell colObj = rowObj.getCell( colNo ); 846 if( colObj != null ) { 847 final String val = getValue( colObj ); 848 // if( colObj.getCellType() != Cell.CELL_TYPE_BLANK && val != null && val.length() > 0 ) { // 6.5.0.0 (2016/09/30) poi-3.12 849// if( colObj.getCellTypeEnum() != CellType.BLANK && val != null && val.length() > 0 ) { // 6.5.0.0 (2016/09/30) poi-3.15 850 if( colObj.getCellType() != CellType.BLANK && val != null && val.length() > 0 ) { // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated. 851 isRowDel = false; // 一つでも現れれば、行の削除は中止 852 break; 853 } 854 // 6.0.2.3 (2014/10/10) CellStyle の有無も判定基準に含めます。 855 else if( colObj.getCellStyle() != null ) { 856 isRowDel = false; // 一つでも現れれば、行の削除は中止 857 break; 858 } 859 else if( isCellDel ) { 860 rowObj.removeCell( colObj ); // CELL_TYPE_BLANK の場合は、削除 861 } 862 } 863 } 864 if( isRowDel ) { sht.removeRow( rowObj ); } 865 else if( !isCellDel ) { break; } // Cell の末尾削除を行わない場合は、break すればよい。 866 } 867 } 868 } 869 } 870 871 /** 872 * ファイルから、Workbookオブジェクトを新規に作成します。 873 * 874 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 875 * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更 876 * @og.rev 7.0.0.0 (2018/10/01) poi-4.0.0 例外InvalidFormatExceptionは対応するtry文の本体ではスローされません 877 * 878 * @param file 入力ファイル 879 * @return Workbookオブジェクト 880 * @og.rtnNotNull 881 */ 882 public static Workbook createWorkbook( final File file ) { 883 InputStream fis = null; 884 try { 885 // File オブジェクトでcreate すると、ファイルがオープンされたままになってしまう。 886 fis = new BufferedInputStream( new FileInputStream( file ) ); 887 return WorkbookFactory.create( fis ); 888 } 889 catch( final IOException ex ) { 890 final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 891 throw new OgRuntimeException( errMsg,ex ); 892 } 893 // 7.0.0.0 (2018/10/01) poi-4.0.0 対応するtry文の本体ではスローされません 894// catch( final InvalidFormatException ex ) { 895// final String errMsg = "ファイル形式エラー[" + file + "]" + CR + ex.getMessage() ; 896// throw new OgRuntimeException( errMsg,ex ); 897// } 898 finally { 899 Closer.ioClose( fis ); 900 } 901 } 902 903 /** 904 * シート一覧を、Workbook から取得します。 905 * 906 * 取得元が、Workbook なので、xls , xlsx どちらの形式でも取り出せます。 907 * 908 * EXCEL上のシート名を、配列で返します。 909 * 910 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 911 * 912 * @param wkbook Workbookオブジェクト 913 * @return シート名の配列 914 */ 915 public static String[] getSheetNames( final Workbook wkbook ) { 916 final int shCnt = wkbook.getNumberOfSheets(); 917 918 String[] shtNms = new String[shCnt]; 919 920 for( int i=0; i<shCnt; i++ ) { 921 final Sheet sht = wkbook.getSheetAt( i ); 922 shtNms[i] = sht.getSheetName(); 923 } 924 925 return shtNms; 926 } 927 928 /** 929 * 名前定義一覧を取得します。 930 * 931 * EXCEL上に定義された名前を、配列で返します。 932 * ここでは、名前とFormulaをタブで連結した文字列を配列で返します。 933 * Name オブジェクトを削除すると、EXCELが開かなくなったりするので、 934 * 取りあえず一覧を作成して、手動で削除してください。 935 * なお、名前定義には、非表示というのがありますので、ご注意ください。 936 * 937 * ◆ 非表示になっている名前の定義を表示にする EXCEL VBA マクロ 938 * http://dev.classmethod.jp/tool/excel-delete-name/ 939 * Sub VisibleNames() 940 * Dim name 941 * For Each name In ActiveWorkbook.Names 942 * If name.Visible = False Then 943 * name.Visible = True 944 * End If 945 * Next 946 * MsgBox "すべての名前の定義を表示しました。", vbOKOnly 947 * End Sub 948 * 949 * ※ EXCEL2010 数式タブ→名前の管理 で、複数選択で、削除できます。 950 * ただし、非表示の名前定義は、先に表示しないと、削除できません。 951 * ◆ 名前の一括削除 EXCEL VBA マクロ 952 * http://komitsudo.blog70.fc2.com/blog-entry-104.html 953 * Sub DeleteNames() 954 * Dim name 955 * On Error Resume Next 956 * For Each name In ActiveWorkbook.Names 957 * If Not name.BuiltIn Then 958 * name.Delete 959 * End If 960 * Next 961 * MsgBox "すべての名前の定義を削除しました。", vbOKOnly 962 * End Sub 963 * 964 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 965 * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] WorkbookのgetNameAt(int)は推奨されません (POI4.0.0) 966 * 967 * @param wkbook Workbookオブジェクト 968 * @return 名前定義(名前+TAB+Formula)の配列 969 * @og.rtnNotNull 970 */ 971 public static String[] getNames( final Workbook wkbook ) { 972// final int cnt = wkbook.getNumberOfNames(); 973 974 final Set<String> nmSet = new TreeSet<>(); 975 976 // 7.0.0.0 (2018/10/01) 警告:[deprecation] WorkbookのgetNameAt(int)は推奨されません (POI4.0.0) 977// for( int i=0; i<cnt; i++ ) { 978 for( final Name nm : wkbook.getAllNames() ) { 979 String name = null; 980 String ref = null; 981 982// final Name nm = wkbook.getNameAt(i); 983 try { 984 name = nm.getNameName(); 985 ref = nm.getRefersToFormula(); 986 } 987 // catch( final Exception ex ) { // 6.1.0.0 (2014/12/26) refactoring 988 catch( final RuntimeException ex ) { 989 final String errMsg = "POIUtil:RefersToFormula Error! name=[" + name + "]" + ex.getMessage() ; 990 System.out.println( errMsg ); 991 // Excel97形式の場合、getRefersToFormula() でエラーが発生することがある。 992 } 993 994 nmSet.add( name + "\t" + ref ); 995 996 // 削除するとEXCELが壊れる? なお、削除時には逆順で廻さないとアドレスがずれます。 997 // if( nm.isDeleted() ) { wkbook.removeName(i); } 998 } 999 1000 return nmSet.toArray( new String[nmSet.size()] ); 1001 } 1002 1003 /** 1004 * 書式のスタイル一覧を取得します。 1005 * 1006 * EXCEL上に定義された書式のスタイルを、配列で返します。 1007 * 書式のスタイルの名称は、CellStyle にメソッドが定義されていません。 1008 * 実クラスである HSSFCellStyle にキャストして使用する 1009 * 必要があります。(XSSFCellStyle にも名称を取得するメソッドがありません。) 1010 * 1011 * ※ EXCEL2010 ホームタブ→セルのスタイル は、一つづつしか削除できません。 1012 * マクロは、開発タブ→Visual Basic で、挿入→標準モジュール を開き 1013 * テキストを張り付けてください。 1014 * 実行は、開発タブ→マクロ で、マクロ名を選択して、実行します。 1015 * 最後は、削除してください。 1016 * 1017 * ◆ スタイルの一括削除 EXCEL VBA マクロ 1018 * http://komitsudo.blog70.fc2.com/blog-entry-104.html 1019 * Sub DeleteStyle() 1020 * Dim styl 1021 * On Error Resume Next 1022 * For Each styl In ActiveWorkbook.Styles 1023 * If Not styl.BuiltIn Then 1024 * styl.Delete 1025 * End If 1026 * Next 1027 * MsgBox "すべての追加スタイルを削除しました。", vbOKOnly 1028 * End Sub 1029 * 1030 * ◆ 名前の表示、削除、スタイルの削除の一括実行 EXCEL VBA マクロ 1031 * Sub AllDelete() 1032 * Call VisibleNames 1033 * Call DeleteNames 1034 * Call DeleteStyle 1035 * MsgBox "すべての処理を完了しました。", vbOKOnly 1036 * End Sub 1037 * 1038 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 1039 * 1040 * @param wkbook Workbookオブジェクト 1041 * @return 書式のスタイル一覧 1042 * @og.rtnNotNull 1043 */ 1044 public static String[] getStyleNames( final Workbook wkbook ) { 1045 final int cnt = wkbook.getNumCellStyles(); // return 値は、short 1046 1047 final Set<String> nmSet = new TreeSet<>(); 1048 1049 for( int s=0; s<cnt; s++ ) { 1050 final CellStyle cs = wkbook.getCellStyleAt( (short)s ); 1051 if( cs instanceof HSSFCellStyle ) { 1052 final HSSFCellStyle hcs = (HSSFCellStyle)cs; 1053 final String name = hcs.getUserStyleName(); 1054 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 1055 if( name == null ) { // この処理は不要かも。 1056 final HSSFCellStyle pst = hcs.getParentStyle(); 1057 if( pst != null ) { 1058 final String pname = pst.getUserStyleName(); 1059 if( pname != null ) { nmSet.add( pname ); } 1060 } 1061 } 1062 else { 1063 nmSet.add( name ); 1064 } 1065 } 1066 } 1067 1068 return nmSet.toArray( new String[nmSet.size()] ); 1069 } 1070 1071 /** 1072 * セル情報を返します。 1073 * 1074 * エラー発生時に、どのセルでエラーが発生したかの情報を取得できるようにします。 1075 * 1076 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1077 * @og.rev 6.0.3.0 (2014/11/13) セル情報を作成する時に、値もセットします。 1078 * @og.rev 6.2.2.0 (2015/03/27) celKigo を求めるロジック変更 1079 * @og.rev 6.3.1.0 (2015/06/28) rowNo(行番号)も引数に取るようにします。 1080 * 1081 * @param oCell EXCELのセルオブジェクト 1082 * @return セル情報の文字列 1083 */ 1084 public static String getCellMsg( final Cell oCell ) { 1085 String lastMsg = null; 1086 1087 if( oCell != null ) { 1088 final String shtNm = oCell.getSheet().getSheetName(); 1089 final int rowNo = oCell.getRowIndex(); 1090 final int celNo = oCell.getColumnIndex(); 1091 1092 // 6.0.3.0 (2014/11/13) セル情報を作成する時に、値もセットします。 1093 lastMsg = " Sheet=" + shtNm + ", Row=" + rowNo + ", Cel=" + celNo 1094 + "(" + getCelKigo(rowNo,celNo) + ") , Val=" + oCell.toString() ; 1095 } 1096 1097 return lastMsg; 1098 } 1099 1100 /** 1101 * Excelの行番号,列番号より、セル記号を求めます。 1102 * 1103 * 行番号は、0から始まる数字ですが、記号化する場合は、1から始まります。 1104 * Excelの列記号とは、A,B,C,…,Z,AA,AB,…,ZZ,AAA,AAB,… と続きます。 1105 * つまり、アルファベットだけの、26進数になります。(ゼロの扱いが少し特殊です) 1106 * 列番号は、0から始まる数字で、0=A,1=B,2=C,…,25=Z,26=AA,27=AB,…,701=ZZ,702=AAA,703=AAB,… 1107 * EXCELの行列記号にする場合は、この列記号に、行番号を、+1して付ければよいだけです。 1108 * (※ 列番号に+1するのは、内部では0から始まる列番号ですが、表示上は1から始まります) 1109 * 1110 * @og.rev 6.2.2.0 (2015/03/27) celKigo を求めるロジック変更 1111 * @og.rev 6.3.1.0 (2015/06/28) rowNo(行番号)も引数に取るようにします。 1112 * 1113 * @param rowNo 行番号(0,1,2,…) 1114 * @param colNo 列番号(0,1,2,…) 1115 * @return Excelの列記号(A1,B2,C3,…) 1116 */ 1117 public static String getCelKigo( final int rowNo,final int colNo ) { 1118 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 1119 int cnt = colNo; 1120 while( cnt >= 26 ) { 1121 buf.append( (char)('A'+cnt%26) ); 1122 cnt = cnt/26-1; 1123 } 1124 buf.append( (char)('A'+cnt%26) ) 1125 .reverse() // append で逆順に付けているので、反転して返します。 1126 .append( rowNo+1 ); 1127 1128 return buf.toString(); 1129 } 1130 1131 /** 1132 * アプリケーションのサンプルです。 1133 * 1134 * 入力ファイル名 は必須で、第一引数固定です。 1135 * 第二引数は、処理方法で、-ALL か、-LINE を指定します。何も指定しなければ、-ALL です。 1136 * 第三引数を指定した場合は、Encode を指定します。 1137 * 1138 * Usage: java org.opengion.fukurou.model.POIUtil 入力ファイル名 [処理方式] [エンコード] 1139 * -A(LL) ・・・ ALL 一括処理(初期値) 1140 * -L(INE) ・・・ LINE 行単位処理 1141 * -S(heet) ・・・ Sheet名一覧 1142 * -N(AME) ・・・ NAME:名前定義 1143 * -C(ellStyle) ・・・ CellStyle:書式のスタイル 1144 * 1145 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1146 * @og.rev 6.2.3.0 (2015/05/01) パラメータ変更、textReader → extractor に変更 1147 * @og.rev 6.2.4.2 (2015/05/29) 引数判定の true/false の処理が逆でした。 1148 * @og.rev 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更 1149 * 1150 * @param args コマンド引数配列 1151 */ 1152 public static void main( final String[] args ) { 1153 final String usageMsg = "Usage: java org.opengion.fukurou.model.POIUtil 入力ファイル名 [処理方式] [エンコード]" + "\n" + 1154 "\t -A(LL) ・・・ ALL 一括処理(初期値) \n" + 1155 "\t -L(INE) ・・・ LINE 行単位処理 \n" + 1156 "\t -S(heet) ・・・ Sheet名一覧 \n" + 1157 "\t -N(AME) ・・・ NAME:名前定義 \n" + 1158 "\t -C(ellStyle) ・・・ CellStyle:書式のスタイル \n" ; 1159 if( args.length == 0 ) { 1160 System.err.println( usageMsg ); 1161 return ; 1162 } 1163 1164 final File file = new File( args[0] ); 1165 final char type = args.length >= 2 ? args[1].charAt(1) : 'A' ; 1166 final String encode = args.length >= 3 ? args[2] : null ; // 6.2.4.2 (2015/05/29) true/false の処理が逆でした。 1167 1168 switch( type ) { 1169 case 'A' : if( encode == null ) { 1170 System.out.println( POIUtil.extractor( file ) ); 1171 } 1172 else { 1173 System.out.println( POIUtil.extractor( file,encode ) ); 1174 } 1175 break; 1176 // 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更 1177 case 'L' : final TextConverter<String,String> conv = 1178 ( val,cmnt ) -> { 1179 System.out.println( "val=" + val + " , cmnt=" + cmnt ); 1180 return null; 1181 }; 1182 1183 // new TextConverter<String,String>() { 1184 // /** 1185 // * 入力文字列を、変換します。 1186 // * 1187 // * @param val 入力文字列 1188 // * @param cmnt コメント 1189 // * @return 変換文字列(変換されない場合は、null) 1190 // */ 1191 // @Override 1192 // public String change( final String val , final String cmnt ) { 1193 // System.out.println( "val=" + val + " , cmnt=" + cmnt ); 1194 // return null; 1195 // } 1196 // }; 1197 1198 if( encode == null ) { 1199 POIUtil.textReader( file,conv ); 1200 } 1201 else { 1202 POIUtil.textReader( file,conv,encode ); 1203 } 1204 break; 1205 case 'S' : final String[] shts = POIUtil.getSheetNames( POIUtil.createWorkbook(file) ); 1206 System.out.println( "No:\tSheetName" ); 1207 for( int i=0; i<shts.length; i++ ) { 1208 System.out.println( i + "\t" + shts[i] ); 1209 } 1210 break; 1211 case 'N' : final String[] nms = POIUtil.getNames( POIUtil.createWorkbook(file) ); 1212 System.out.println( "No:\tName\tFormula" ); 1213 for( int i=0; i<nms.length; i++ ) { 1214 System.out.println( i + "\t" + nms[i] ); 1215 } 1216 break; 1217 case 'C' : final String[] sns = POIUtil.getStyleNames( POIUtil.createWorkbook(file) ); 1218 System.out.println( "No:\tStyleName" ); 1219 for( int i=0; i<sns.length; i++ ) { 1220 System.out.println( i + "\t" + sns[i] ); 1221 } 1222 break; 1223 default : System.err.println( usageMsg ); 1224 break; 1225 } 1226 } 1227}