2017-10-06 83 views
1

我需要逐行遍历Excel电子表格,但不是一次全部遍历。我想保持一个流向电子表格打开,然后将该对象传递给另一个方法。这种方法会不时要求下一行数据。这种方法已经建立,所以我不能改变它。按顺序阅读行,但按需阅读

我试着用XSSFWorkbook开始做这件事,它工作得很好,直到我用完了真实数据的内存。

然后我尝试使用XSSFSheetXMLHandler切换到SAX解析器,并使用类似于Apache POI项目提供的样本XLSX2CSV的自定义解析器。但这会导致一次处理所有行;如果我将它们存储在内存中以供稍后阅读,我也会耗尽内存。我也无法访问DataFormatter,我需要进行单元值处理。

有人可以指我一个例子/类,可以让我这样做吗?

+0

有一个很好的例子,它可能会好起来的。我最关心的是单元格格式。 'DataFormatter'使得它变得简单,但在我自己的XML中......需要一个很好的例子。 – Peter

+0

那么,对于格式化XML,您可以通过Java Transformer类使用XSLT:https://stackoverflow.com/questions/4604497/xslt-processing-with-java –

+0

p .s.这个问题也将对你有用:https://stackoverflow.com/questions/504689/big-xml-file-and-outofmemoryerror,SAX与我上面提供的XSLT例子应该可以解决这个问题。 XSLT非常简单,可以将它想象为XML的样式表。 –

回答

1

我个人喜欢的用于XML的Streaming API是StAX

既知道*.xlsx文件只是一个ZIP归档和apache poi小号OPCPackage这是一个ZipPackage,我们可以考虑一下以下方法:

  • 获取从*.xlsx ExcelZipPackage/xl/worksheets/sheetN.xml包的一部分。
  • 创建一个StAX阅读器。
  • 现在我们可以使用这个阅读器从这个XML读取。

以下示例创建一个基本应用程序,该应用程序使用按钮单击按行进行此操作。

import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 

import org.apache.poi.openxml4j.opc.*; 

import javax.xml.stream.*; 
import javax.xml.stream.events.*; 

import javax.xml.namespace.QName; 

import java.util.regex.Pattern; 

public class GetExcelRowByRow extends JPanel implements ActionListener { 
protected JButton button; 
protected JTextArea textArea; 
private final static String newline = "\n"; 

//file path to Excel file and sheet number to work with 
private final static String filepath = "file.xlsx"; 
private final static int scheetnr = 1; 

private StaxExcelRowByRowReader reader; 

public GetExcelRowByRow() { 
    super(new GridBagLayout()); 
    button = new JButton("Next Row"); 
    button.addActionListener(this); 
    textArea = new JTextArea(15, 50) { 
    @Override 
    public boolean getScrollableTracksViewportWidth() { 
    return true; 
    } 
    }; 
    textArea.setLineWrap(true); 
    textArea.setEditable(false); 
    JScrollPane scrollPane = new JScrollPane(textArea); 
    GridBagConstraints c = new GridBagConstraints(); 
    c.gridwidth = GridBagConstraints.REMAINDER; 
    c.fill = GridBagConstraints.HORIZONTAL; 
    add(button, c); 
    c.fill = GridBagConstraints.BOTH; 
    c.weightx = 1.0; 
    c.weighty = 1.0; 
    add(scrollPane, c); 

    try { 
    reader = new StaxExcelRowByRowReader(filepath, scheetnr); 
    } catch (Exception ex) { 
    ex.printStackTrace(); 
    } 

} 
@Override 
public void actionPerformed(ActionEvent evt) { 
    String row = "Row not found..."; 
    try { 
    row = reader.getNextRow(); 
    } catch (Exception ex) { 
    ex.printStackTrace(); 
    } 
    textArea.append(row + newline); 
    textArea.setCaretPosition(textArea.getDocument().getLength()); 
} 

public StaxExcelRowByRowReader getReader() { 
    return reader; 
} 

private static void createAndShowGUI() { 
    JFrame frame = new JFrame("Get Excel row by row"); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    GetExcelRowByRow app = new GetExcelRowByRow(); 
    frame.add(app); 

    frame.addWindowListener(new WindowAdapter() { 
    @Override 
    public void windowClosing(WindowEvent windowEvent) { 
    try { 
    app.getReader().close(); 
    } catch (Exception ex) { 
    ex.printStackTrace(); 
    } 
    System.exit(0); 
    } 
    }); 

    frame.pack(); 
    frame.setVisible(true); 
} 

public static void main(String[] args) { 
    javax.swing.SwingUtilities.invokeLater(new Runnable() { 
    public void run() { 
    createAndShowGUI(); 
    } 
    }); 
} 

//class for reading a /xl/worksheets/sheetN.xml package part from a *.xlsx Excel ZipPackage 
private class StaxExcelRowByRowReader { 

    private XMLEventReader sheetreader; 
    private OPCPackage opcpackage; 

    public StaxExcelRowByRowReader(String filepath, int sheetnr) { 
    try { 
    opcpackage = OPCPackage.open(filepath, PackageAccess.READ); 
    //get the sheet package part 
    PackagePart sheetpart = opcpackage.getPartsByName(Pattern.compile("/xl/worksheets/sheet"+sheetnr+".xml")).get(0); 
    //create reader for the sheet package part    
    sheetreader = XMLInputFactory.newInstance().createXMLEventReader(sheetpart.getInputStream()); 
    } catch (Exception ex) { 
    ex.printStackTrace(); 
    } 
    } 

    //method for getting the next row from the reader 
    public String getNextRow() throws Exception { 
    StringBuffer row = new StringBuffer(); 
    boolean valueFound = false; 
    boolean nextValueIsSharedString = false; 
    while(sheetreader.hasNext()){ 
    XMLEvent event = sheetreader.nextEvent(); 
    if(event.isStartElement()) { 
    StartElement startElement = (StartElement)event; 
    QName startElementName = startElement.getName(); 
    if(startElementName.getLocalPart().equalsIgnoreCase("row")) { //start element of row 
     row.append("<row"); 
     row.append(" " + startElement.getAttributeByName(new QName("r"))); 
     row.append(">"); 
    } else if(startElementName.getLocalPart().equalsIgnoreCase("c")) { //start element of cell 
     row.append("<c"); 
     row.append(" " + startElement.getAttributeByName(new QName("r"))); 
     row.append(" " + startElement.getAttributeByName(new QName("s"))); 
     row.append(" " + startElement.getAttributeByName(new QName("t"))); 
     row.append(">"); 
     Attribute type = startElement.getAttributeByName(new QName("t")); 
     if (type != null && "s".equals(type.getValue())) { 
     nextValueIsSharedString = true; 
     } else { 
     nextValueIsSharedString = false; 
     } 
    } else if(startElementName.getLocalPart().equalsIgnoreCase("v")) { //start element of value 
     row.append("<v>"); 
     valueFound = true; 
    } 
    } else if(event.isCharacters() && valueFound) { 
    Characters characters = (Characters)event; 
    if (nextValueIsSharedString) { 
     row.append("shared string: " + characters.getData()); 
    } else { 
     row.append(characters.getData()); 
    } 
    } else if(event.isEndElement()) { 
    EndElement endElement = (EndElement)event; 
    QName endElementName = endElement.getName(); 
    if(endElementName.getLocalPart().equalsIgnoreCase("v")) { //end element of value 
     row.append("</v>"); 
     valueFound = false; 
    } else if(endElementName.getLocalPart().equalsIgnoreCase("c")) { //end element of cell 
     row.append("</c>"); 
    } else if(endElementName.getLocalPart().equalsIgnoreCase("row")) { //end element of row 
     row.append("</row>"); 
     return row.toString(); 
    } 
    } 
    } 
    return "No more rows."; 
    } 

    public void close() throws Exception { 
    if (sheetreader != null) sheetreader.close(); 
    if (opcpackage != null) opcpackage.close(); 
    } 
} 
} 

肯定这只是一个草案,以显示原则。整个应用程序将会有更多的代码。

接下来,我们将不得不阅读并解析/xl/sharedStrings.xml包部分,其中包含共享字符串。此外,我们必须阅读并解析包含单元格样式的/xl/styles.xml包部分。我们需要样式来检测一个数字值是一个日期还是一个数字,如果是一个数字,那么是一个什么样的数字。这是必要的,因为Excel将所有类型的数字存储为双精度值。日期也是数字双打,意思是在01/01/1900之后的几天,小数部分为1h = 1/24,1m = 1/24/60,1s = 1/24/60/60。

但是这可以使用与/xl/worksheets/sheetN.xml包部件相同的方法。

+0

我很欣赏这种回应,但是您的解决方案比我期望的要好得多:)我仍然想要弄清楚这方面的一些东西,它与'XSSFSheetXMLHandler'提供的更类似,但现在我已经重新设计了我的解决方案以解决这些限制。 – Peter