2012-04-25 17 views
1

大きなxlsまたはxlsxファイル(約30 MB以上、70,000以上の行)を読みたい。 OutOfMemoryエラーが出るまで、私はApache POIを使って小さなExcelファイルを読むことができました。Apache POI Event APIを使用して特定の行を読み取る方法は?

パフォーマンスとメモリの使用が私にとって懸念されています。私は、メモリのフットプリントが問題であれば、XSSFの場合、基礎となるXMLデータを取得し、XSSFとSAX(Event API)を使用して自分自身で処理することができるということを多くの記事を通じて読んでいます。さて、私はそれが面白いとわかりました、今、問題なしでxlsxファイル全体を読むことができます。イベントAPIを使用しない場合は、GB単位(-Xmxが1024mに設定されていてもハングアップに使用されていた場合は最大1GBまで)に比べて、メモリ使用量が70MB未満です。

しかし、ここでは読み込みプロセスをカスタマイズして、特定の行だけをExcelから読み込むことができます。私は簡単にorg.apache.poi.ss.usermodel.Sheet#getRow(int rownum)を使ってこれを行うことができました。しかし、イベントAPIを使用すると、中断することなくすべての行が読み込まれ、特定の行を読みにくいことが分かります。ちょうど行番号2,3,5などです。以下は私のコード全体です:

import java.io.InputStream; 
import java.util.Iterator; 
import java.util.Vector; 
import org.apache.poi.xssf.eventusermodel.XSSFReader; 
import org.apache.poi.xssf.model.SharedStringsTable; 
import org.apache.poi.xssf.usermodel.XSSFRichTextString; 
import org.apache.poi.openxml4j.opc.OPCPackage; 
import org.xml.sax.Attributes; 
import org.xml.sax.ContentHandler; 
import org.xml.sax.InputSource; 
import org.xml.sax.SAXException; 
import org.xml.sax.XMLReader; 
import org.xml.sax.helpers.DefaultHandler; 
import org.xml.sax.helpers.XMLReaderFactory; 

/** 
* XSSF and SAX (Event API) 
*/ 
public class FromHowTo { 
    public void processAllSheets(String filename) throws Exception { 
     OPCPackage pkg = OPCPackage.open(filename); 
     XSSFReader r = new XSSFReader(pkg); 
     SharedStringsTable sst = r.getSharedStringsTable(); 

     XMLReader parser = fetchSheetParser(sst); 

     Iterator<InputStream> sheets = r.getSheetsData(); 
     while(sheets.hasNext()) { 
      InputStream sheet = sheets.next(); 
      InputSource sheetSource = new InputSource(sheet); 
      parser.parse(sheetSource); 
      sheet.close(); 
     } 
    } 

    public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException { 
     XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser"); 
     ContentHandler handler = new SheetHandler(sst); 
     parser.setContentHandler(handler); 
     return parser; 
    } 

    /** 
    * See org.xml.sax.helpers.DefaultHandler javadocs 
    */ 
    private static class SheetHandler extends DefaultHandler { 
     private SharedStringsTable sst; 
     private String lastContents; 
     private boolean nextIsString; 
     Vector values = new Vector(10); 

     private SheetHandler(SharedStringsTable sst) { 
      this.sst = sst; 
     } 

     public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { 
      // c => cell 

      if(name.equals("c")) { 
       // Figure out if the value is an index in the SST 
       String cellType = attributes.getValue("t"); 
       //System.out.println(cellType); 
       if(cellType != null && cellType.equals("s")) { 
        nextIsString = true; 
       } else { 
        nextIsString = false; 
       } 
      } 
      // Clear contents cache 
      lastContents = ""; 
     } 

     public void endElement(String uri, String localName, String name) throws SAXException { 
      // Process the last contents as required. 
      // Do now, as characters() may be called more than once 
      if(nextIsString) { 
       try { 
        int idx = Integer.parseInt(lastContents); 
        lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString(); 
       } catch (NumberFormatException e) { 
       } 
      } 

      // v => contents of a cell 
      // Output after we've seen the string contents 
      if(name.equals("v")) { 
       values.add(lastContents); 
      } 

      if(name.equals("row")) { 
       System.out.println(values); 
       values.removeAllElements(); 
      } 
     } 

     public void characters(char[] ch, int start, int length) throws SAXException { 
      lastContents += new String(ch, start, length); 
     } 
    } 

    public static void main(String[] args) throws Exception { 
     FromHowTo howto = new FromHowTo(); 
     howto.processAllSheets(args[0]); 
    } 
} 

私はApache POI 3.7でJRE7を使用しています。誰かがEvent APIで特定の行を取得するのを手伝ってもらえますか?

+0

するendElementに応じてデータを処理することができますが、開始要素を取得するとき、あなただけの行の行数で見ることができない、とあなたが望むものでなければ、次の行が始まるまでスキップしますか? – Gagravarr

+0

@Gagravarr:Tx。はい、私はそれをしました。しかし、何か他の選択肢があるかどうか疑問に思っていました。これは依然としてxml全体を解析する必要があったためです。もう1つのことは、大きなxlsファイルを読み込むために私のコードをどのように修正できるのでしょうか? xlsファイルを読み込み中にエラーが発生する(xlsxはうまく動作する) - > org.apache.poi.openxml4j.exceptions.InvalidOperationException:指定されたファイルを開くことができません: 'D:\ Test \ conversions.xls' – ParagJ

+0

。あなたはここで低レベルのものに取り組んでいます、そして、2つのフォーマットは非常に異なっています。 2つの共通して機能するコードが必要な場合は、usermodel – Gagravarr

答えて

5

各行開始要素には行番号があります。属性から検索することができます。

long rowIndex = Long.valueOf(attributes.getValue( "r"));

イベントモデルは、すべての行に通過しますが、あなたは彼インデックスを取得し、

+0

ああ、ありがとうございます。 –

+0

@VictorHugoいつも歓迎 – user1363516

+0

@ user1363516これは、行インデックスを返すのではなく、セル – Mandrek

関連する問題