序文
POIは、Apacheの下でMicrosoftのドキュメントを読み書きするための有名なライブラリです。多くの人々は、レポートをエクスポートし、単語文書を作成し、読み取るときにPOIを使用する必要がありました。 POIはこれらの業務に大きな利便性をもたらします。私が最近作成したツールの1つは、コンピューターで単語とExcelファイルを読むことです。
POI構造の説明
パッケージ名の説明
HSSFは、Microsoft Excel XLS形式のアーカイブを読み書きできることを提供します。
XSSFは、Microsoft Excel OOXML XLSX形式のアーカイブを読み書きできることを提供します。
HWPFは、Microsoft Word Doc形式のアーカイブを読み書きする機能を提供します。
HSLFは、Microsoft PowerPoint形式のアーカイブを読み書きできることを提供します。
HDGFは、Microsoft Visio形式のアーカイブを読み取る機能を提供します。
HPBFは、Microsoft Publisher形式でアーカイブを読み取る機能を提供します。
HSMFは、Microsoft Outlookフォーマットアーカイブの読み取り機能を提供します。
ここに、単語とExcelの両方で遭遇する落とし穴のいくつかがあります。
言葉
単語ファイルの場合、必要なのは、ファイル内のメインテキストにテキストを抽出することだけです。したがって、docまたはdocxファイルを読み取る方法を作成できます。
private static string readdoc(string filepath、inputstream is){string text = ""; try {if(filepath.endswith( "doc")){wordextractor ex = new wordextractor(is); text = ex.getText(); ex.close(); is.close(); } else if(filepath.endswith( "docx")){xwpfdocument doc = new xwpfdocument(is); XWPFWORDEXTRACTOR EXTRACTOR = new XWPFWordExtractor(doc); text = extractor.getText(); extractor.close(); is.close(); }} catch(Exception E){logger.error(filepath、e); }最後に{if(is!= null){is.close(); }} textを返します。 }理論的には、このコードは、ほとんどのDOCまたはDOCXファイルを読み取るために有効である必要があります。しかし!!!私は奇妙な問題を発見しました。つまり、私のコードが特定のドキュメントファイルを読み取ると、しばしばそのような例外が与えられます。
org.apache.poi.poifs.filesystem.officexmlfileexception:提供されたデータは、2007+ XMLにあるようです。 OLE2 Office Documentsを扱うPOIの一部を呼び出しています。
この例外とはどういう意味ですか?簡単に言えば、開いたファイルはドキュメントファイルではありません。DOCXを読み取る方法を使用して読み取る必要があります。しかし、私たちが明確に開いているのは、接尾辞ドキュメントのファイルです!
実際、DOCとDOCXは本質的に異なります。 docはole2タイプであり、docxはooxmlタイプです。圧縮ファイルを使用してDocxファイルを開くと、いくつかのフォルダーがあります。
本質的に、Docxファイルは、いくつかのXMLファイルを含むZIPファイルです。したがって、一部のDOCXファイルのサイズは大きくありませんが、内部のXMLファイルは実際には比較的大きいため、あまり大きくないと思われるDocxファイルを読むときに多くのメモリを消費します。
次に、圧縮ファイルを使用してこのドキュメントファイルを開きました。予想どおり、その内部は上の写真に示されているようにあるため、本質的にはDOCXファイルと考えることができます。たぶん、それは互換性モードで保存されているため、そのような詐欺の問題につながるからでしょう。したがって、これで、ファイルがサフィックス名に基づいてdocxであるか、信頼できないかどうかを判断できます。
正直に言うと、これはまれな問題だとは思いません。しかし、Googleでこれについて何も見つかりませんでした。ファイルが.docxであるか.doc形式であるかを知る方法この例は、zipinputStreamを使用してファイルがdocxファイルであるかどうかを判断することです。
boolean iszip = new zipinputStream(fileStream).getNextEntry()!= null;
しかし、これは良い方法ではないと思います。なぜなら、私はZipinPustreamを構築しなければならないからです。さらに、この操作はinputStreamに影響を与えるようであるため、通常のDOCファイルの読み取りに問題があります。または、ファイルオブジェクトを使用して、それがzipファイルであるかどうかを判断します。しかし、これも良い方法ではありません。なぜなら、圧縮ファイルのdocまたはdocxファイルを読み取る必要があるため、入力は入力ストリームである必要があるため、このオプションも問題ありません。私はほとんどの間、Stackoverflowで外国人のグループと話をしました。時々、私はこれらの外国人が理解する能力を本当に疑っていましたが、最終的には、大物が私にecとした、繊細な解決策を与えてくれました。これは、POI 3.17に追加された新機能です。
Public Enum filemagic { / ** ole2 / biff8+ Office 97以降に使用されるストリーム* / ole2(headerblockconstants._signature)、 / ** ooxml / zipストリーム* / ooxml(ooxml_file_header)、 / ** xmlファイル2 */ biff2(new byte [] {0x09、0x00、// sid = 0x0009 0x04、0x00、// size = 0x004 0x00、0x00、//未使用0x70、0x00 // 0x70 =複数値}) // sid = 0x0209 0x06、0x00、// size = 0x0006 0x00、0x00、//未使用0x70、0x00 // 0x70 =複数値})、 /** biff4 raw stream-サイズ= 0x0006 0x00、0x00、//未使用0x70、0x00 // 0x70 =複数値}、new byte [] {0x09、0x04、// sid = 0x0409 0x06、0x00、// size = 0x0006 0x00、0x00 mswrite(new byte [] {0x31、(byte)0xbe、0x00、0x00}、new byte [] {0x32、(byte)0xbe、0x00、0x00})、/** rtf document*/rtf( "{// rtf")列挙! / **不明な魔法*/不明(new byte [0]);最終的なバイト[] []マジック; filemagic(long magic){this.magic = new byte [1] [8]; Littleendian.putlong(this.magic [0]、0、magic); } filemagic(byte [] ... magic){this.magic = magic; } filemagic(String Magic){this(magic.getBytes(localeutil.charset_1252)); } public static filemagic valueof(byte [] magic){for(filemagic fm:values()){int i = 0; Boolean fund = true; for(byte [] ma:fm.magic){for(byte m:ma){byte d = magic [i ++]; if(!(d == m ||(m == 0x70」&&(d == 0x10 || d == 0x20 || d == 0x40))){fund = false;壊す; }} if(found){return fm; }}} return unknown; } / ** *供給されたinputstreamのファイルマジック(マークとリセットをサポートする必要があります)を取得します。<p> * *入力ストリームがマーク /リセットをサポートしているかどうかわからない場合 * {@link #preparetocheckmagic(inputstream)}を使用して、常にそれを使用します。平均 * ZIPストリームにはジャンクバイトがリーディングされていること * * @param inp Mark/ Reset */ public static filemagic Valueof(inputstream inp)をサポートする入力ストリームはioException {if(!inp.marksupported()){throw new ioException( " } //最初の8バイトをつかむバイト[] data = ioutils.peekfirst8bytes(inp); return filemagic.valueof(data); } / ** * {@link inputStream}をリセット(つまり、ヘッダーマジックのチェックに使用する)をチェックし、 * * @paramストリームをラップするためにチェックする * @return Anabledストリーム * / public static inputtream prepertocheckmagic(inputstream stream){(Stream.markpported() } // bushbackinputStreamを介してデータを処理していましたが、ユーザーコードは小さすぎるものを提供できます// BufferedInputStreamを使用する代わりに、新しいBufferedInputStream(Stream)を返します。 }}主に入力ストリームの最初の8バイトに基づいてファイルタイプを決定するメインコードは次のとおりです。これが最もエレガントなソリューションであると考える方法はありません。当初、私は実際に、圧縮されたファイルの最初の数バイトが別のマジックマンバーによって定義されているように見えると思っていました。 Filemagicの依存関係はバージョン3.16と互換性があるため、このクラスを追加するだけであるため、Wordファイルを読む正しい方法は次のとおりです。
private static string readdoc(string filepath、inputstream is){string text = ""; is = filemagic.preparetocheckmagic(is); try {if(filemagic.valueof(is)== filemagic.ole2){wordextractor ex = new wordextractor(is); text = ex.getText(); ex.close(); } else if(filemagic.valueof(is)== filemagic.ooxml){xwpfdocument doc = new xwpfdocument(is); XWPFWORDEXTRACTOR EXTRACTOR = new XWPFWordExtractor(doc); text = extractor.getText(); extractor.close(); }} catch(Exception E){logger.error( "for file" + filepath、e); }最後に{if(is!= null){is.close(); }} textを返します。 } Excel
Excelの記事では、以前の計画と現在の計画の比較を探しません。今すぐベストプラクティスを教えてください:
@suppresswarnings( "deprecation")private static string readexcel(string filepath、inputstream inp)スロー例外{workbook wb; stringbuilder sb = new StringBuilder(); try {if(filepath.endswith( "。xls")){wb = new hssfworkbook(inp); } else {wb = streamingreader.builder().rowcachesize(1000)//メモリに保つための行の数(デフォルトは10に).buffersize(4096)// inputstreamを読み取るときに使用するバッファサイズ(デフォルトは1024にデフォルト).open(inp); // xlsxファイルの入力ストリームまたはファイル(必須)} sb = readsheet(wb、sb、filepath.endswith( "。xls")); wb.close(); } catch(ole2notofficexmlfileexception e){logger.error(filepath、e); }最後に{if(inp!= null){inp.close(); }} return sb.toString(); } private static string readexcelbyfile(string filepath、file file){workbook wb; stringbuilder sb = new StringBuilder(); try {if(filepath.endswith( "。xls")){wb = workbookfactory.create(file); } else {wb = streamingreader.builder().rowcachesize(1000)//メモリに保持する行の数(デフォルトは10に).buffersize(4096)// inputstreamを読み取るときに使用するバッファサイズ(デフォルトは1024にデフォルト).open(ファイル); // xlsxファイルの入力ストリームまたはファイル(必須)} sb = readsheet(wb、sb、filepath.endswith( "。xls")); wb.close(); } catch(Exception E){logger.error(filepath、e); } return sb.toString(); } private static stringbuilder readsheet(Workbook WB、StringBuilder SB、Boolean ISXLS)は例外{for(row r:sheet){for(cell cell:r){if(cell.getCelltype()= = cell.cell_tepe_String){SB.GetString(){sb.getString){for(cell.getCelltype()== cell.cell_tepe_string() sb.append( ""); } else if(cell.getCellType()== cell.cell_type_numeric){if(isxls){dataformatter formatter = new DataFormatter(); sb.append(formatcellvalue(cell)); } else {sb.append(cell.getStringCellValue()); } sb.append( ""); }}}} sbを返します。 }実際、Excelの読み取りでは、私のツールが直面する最大の問題はメモリオーバーフローです。多くの場合、特定の特に大きなExcelファイルを読むと、メモリオーバーフローの問題が発生します。その後、ついに優れたツールExcel-Streaming-Readerを見つけました。これにより、XLSXファイルを合理化し、特に大きなファイルを小さなファイルに分割して読み取ることができます。
別の最適化は、ファイルオブジェクトを使用できるシナリオでは、入力ストリームを使用する代わりにファイルオブジェクトを使用してファイルを読み取るために読み取ることです。
最後に、私の小さなトリックは、Cell.GetCellTypeを使用してデータの量を減らすことです。
上記は、POIを使用してファイルを読むときの私の探索と発見の一部であり、それがあなたに役立つことを願っています。上記の例は、どこでも私のツールの1つにも適用されます(このツールは、主にコンピューターのコンテンツの全文を検索するのに役立ちます)。興味があれば、見てみましょう。スターまたはPRへようこそ。
要約します
上記は、この記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。