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