如何在 java 中使用 apache 事件模型解析 excel 文件时检查字符串中的数字是否包含日期和指数

How to check a number in a string contains a date and exponential numbers while parsing excel file using apache event model in java

我正在解析一个 excel 文件,其中包含许多日期,如 13-4-2021 和 3,7%,2,65% format.So 中的一些数字 format.So 我正在解析 excel 文件,我正在获取字符串中的数据以将它们写入文本 file.So 我的问题是我正在获取整数形式的日期,例如 44299,而它实际上是 04/13/2021 excel sheet.And 中的格式 另一种情况是我有一些数字,百分比为 3,7%,2,65%,像 3.6999999999999998E-2.So 我可以将数字转换为使用

的日期
SimpleDateFormat("MM/dd/yyyy").format(javaDate)

这是我使用的代码

private static class SheetHandler extends DefaultHandler {
    private SharedStringsTable sst;
    private String lastContents;
    private boolean nextIsString;
    private int rowNumber;

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

    public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException {
        try {

            // row => row
            if(name.equals("row")) {
                   if (attributes.getValue("r") != null) {
                    rowNumber = Integer.valueOf(attributes.getValue("r"));
                   } else {
                    rowNumber++;
                   }
                   //System.out.println("row: " + rowNumber);
                  }

        if (rowNumber > 6) {

        // c => cell
        if(name.equals("c")) {
            // Print the cell reference 

            //System.out.print(attributes.getValue("r") + " - ");
            // Figure out if the value is an index in the SST
            String cellType = attributes.getValue("t");
            if(cellType != null && cellType.equals("s")) {
                nextIsString = true; 
            } else {
                nextIsString = false;
              }

        }
        // Clear contents cache
        lastContents = "";
        }
        }catch(Exception e) {
            e.printStackTrace();
        }
    }

    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 (rowNumber > 6) {


        if(nextIsString) {
            int idx = Integer.parseInt(lastContents);

            lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
            nextIsString = false;
        }
        // v => contents of a cell
        // Output after we've seen the string contents
        if(name.equals("v")) {
           // System.out.println(lastContents);

            if(!lastContents.isEmpty() ) // Here i am putting the values to a list to process 

                pickUpExcelValues.add(lastContents);
            }
        }
    }

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

但是我将如何检查字符串包含让我们说 44299 是一个日期?而且我也不知道如何在写入文本时将这个 3.6999999999999998E-2 转换为 3.7% file.If 有任何想法的人请帮忙。

此函数包含两种情况(百分比或日期):

private static String convert(String s) {
    if(s.indexOf('E') > -1) {   // It's a percentage
        String[] components = s.split("E");
        double num = Double.parseDouble(components[0]) * Math.pow(10, Integer.parseInt(components[1]));
        //return String.valueOf(num);                   // will return i.e. "0.037"
        return Math.round(num * 10000.0) / 100.0 + "%"; // will return i.e. "3.7%"
    }
    else {  // It's a date
        SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
        GregorianCalendar gc = new GregorianCalendar(1900, 0, 0);
        gc.add(Calendar.DATE, Integer.parseInt(s) - 1);
        Date date = gc.getTime();
        return sdf.format(date);
    }
}

请注意,Excel 中的序列号日期表示自 1900 年 1 月 1 日以来经过的天数,因此我使用了转换。

告诉我它是如何为你工作的

在我看来,您可以通过类似于此的方式将您的字符串与 Excel 区分开来:

private static void checkNumber(String fromExcel) {
    try {
        double asNumber = Double.parseDouble(fromExcel);
        if (asNumber >= 0 && asNumber <= 1) {
            System.out.println("Percentage: " + asNumber * 100 + " %");
        }
        long asWholeNumber = Math.round(asNumber);
        try {
            LocalDate asDate = LocalDate.of(1899, Month.DECEMBER, 30)
                    .plusDays(asWholeNumber);
            if (asDate.isAfter(LocalDate.of(2000, Month.DECEMBER, 31)) 
                    && asDate.isBefore(LocalDate.of(2035, Month.JANUARY, 1))) {
                System.out.println("Date: " + asDate);
            }
        } catch (DateTimeException dte) {
            System.out.println("Unidentified: " + fromExcel);
        }
    } catch (NumberFormatException nfe) {
        System.out.println("Unidentified: " + fromExcel);
    }
}

试试这个方法:

    checkNumber("44299");
    checkNumber("3.6999999999999998E-2");

这会打印:

Date: 2021-04-13
Percentage: 3.6999999999999997 %

字符串有两种可能的解释不应阻止您进行验证并捕获不符合任何一种解释的字符串,因此我正在尝试对每种情况进行一些过滤。请注意,如果您在接受的日期中包含 1899,则“0”和“1”将被接受为日期和百分比。

我正在使用并推荐 java.time 中的 LocalDate,现代 Java 日期和时间 API,用于处理日期。现代的 API 比过时的 DateGregorianCalendar.

更好用

可能是更正确的方法。

问题:我的代码需要兼容Java6;我可以使用 java.time 吗?

编辑: 是的,java.time 可以在 Java 6.

中很好地工作
  • 在 Java 8 和更高版本以及新的 Android 设备上(据我所知,来自 API 26 级)新的 API 出现 built-in.
  • Java 6 和 7 中得到 ThreeTen Backport,新 类 的 backport(ThreeTen 用于 JSR 310,其中现代 API 首次被描述)。 编辑 2: 确保从包 org.threeten.bp 和子包中导入日期和时间 类 以及例外情况。
  • 在(较旧的)Android 上,使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP。还要确保从包 org.threeten.bp 和子包中导入日期和时间 类。

链接

这个问题需要进一步解释。

一开始是和相关的,已经回答了。

但如果想使用 XSSF and SAX (Event API) 中的示例,则需要了解 Office Open XML 中使用的 XML 的基本知识。

ExampleEventUserModel 是一个非常低级的示例,展示了流式处理原理。为了将其扩展到考虑格式,也需要解析样式 table 然后使用 DataFormatter

下面是一个完整的例子,它就是这样做的。但是还有一个更完整的示例,包括支持获取数字格式信息并将其应用于数字单元格(例如格式化日期或百分比)。请参阅 svn 中的 XLSX2CSV example

import java.io.InputStream;
import java.util.Iterator;

import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.BuiltinFormats;

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.util.SAXHelper;
import javax.xml.parsers.ParserConfigurationException;

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;


public class ExampleEventUserModel {
 public void processOneSheet(String filename) throws Exception {
  OPCPackage pkg = OPCPackage.open(filename);
  XSSFReader r = new XSSFReader( pkg );
  SharedStringsTable sst = r.getSharedStringsTable();

  StylesTable st = r.getStylesTable();
  XMLReader parser = fetchSheetParser(sst, st);

  // To look up the Sheet Name / Sheet Order / rID,
  //  you need to process the core Workbook stream.
  // Normally it's of the form rId# or rSheet#
  InputStream sheet2 = r.getSheet("rId2");
  InputSource sheetSource = new InputSource(sheet2);
  parser.parse(sheetSource);
  sheet2.close();
 }

 public void processAllSheets(String filename) throws Exception {
  OPCPackage pkg = OPCPackage.open(filename);
  XSSFReader r = new XSSFReader( pkg );
  SharedStringsTable sst = r.getSharedStringsTable();

  StylesTable st = r.getStylesTable();
  XMLReader parser = fetchSheetParser(sst, st);

  Iterator<InputStream> sheets = r.getSheetsData();
  while(sheets.hasNext()) {
   System.out.println("Processing new sheet:\n");
   InputStream sheet = sheets.next();
   InputSource sheetSource = new InputSource(sheet);
   parser.parse(sheetSource);
   sheet.close();
   System.out.println("");
  }
 }

 public XMLReader fetchSheetParser(SharedStringsTable sst, StylesTable st) throws SAXException, ParserConfigurationException {
/*
  XMLReader parser =
  XMLReaderFactory.createXMLReader(
       "org.apache.xerces.parsers.SAXParser"
  );
*/
  XMLReader parser = SAXHelper.newXMLReader();
  ContentHandler handler = new SheetHandler(sst, st);
  parser.setContentHandler(handler);
  return parser;
 }


 /** 
  * See org.xml.sax.helpers.DefaultHandler javadocs 
  */
 private static class SheetHandler extends DefaultHandler {
  private SharedStringsTable sst;
  private StylesTable st;
  private String lastContents;
  private boolean nextIsString;
  private boolean nextIsStyledNumeric;
  private boolean inlineStr;
  private int styleIndex;
  private DataFormatter formatter;

  private int rowNumber;

  private SheetHandler(SharedStringsTable sst, StylesTable st) {
   this.sst = sst;
   this.st = st;
   this.rowNumber = 0;
   this.formatter = new DataFormatter(java.util.Locale.US, true);
   this.styleIndex = 0;
  }

  public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException {

   // row => row
   if(name.equals("row")) {
    if (attributes.getValue("r") != null) {
     rowNumber = Integer.valueOf(attributes.getValue("r"));
    } else {
     rowNumber++;
    }
    System.out.println("row: " + rowNumber);
   }

   if (rowNumber > 6) {

    // c => cell
    if(name.equals("c")) {
     // Print the cell reference
     System.out.print(attributes.getValue("r") + " - ");

     String cellType = attributes.getValue("t");

     // Figure out if the value is an index in the SST
     nextIsString = false;
     if(cellType != null && cellType.equals("s")) {
      nextIsString = true;
     } 

     // Figure out if the value is an inline string     
     inlineStr = false;
     if(cellType != null && cellType.equals("inlineStr")) {
      inlineStr = true;
     } 

     // Figure out if the value is an styled numeric value or date
     nextIsStyledNumeric = false;
     if(cellType != null && cellType.equals("n") || cellType == null) {
      String cellStyle = attributes.getValue("s");
      if (cellStyle != null) {
       styleIndex = Integer.parseInt(cellStyle);
       nextIsStyledNumeric = true;
      }
     } 
    }
   }

   // Clear contents cache
   lastContents = "";
  }

  public void endElement(String uri, String localName, String name)
            throws SAXException {
   if (rowNumber > 6) {

    // Process the last contents as required.
    // Do now, as characters() may be called more than once

    // If the value is in the shared string table, get it
    if(nextIsString) {
     int idx = Integer.parseInt(lastContents);
     lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
     nextIsString = false;
    }

    // v => contents of a cell
    // Output after we've seen the string contents
    if(name.equals("v") || (inlineStr && name.equals("c"))) {
     // If the value is styled numeric, use DataFormatter to formaat it
     if (nextIsStyledNumeric) {
      XSSFCellStyle style = st.getStyleAt(styleIndex);
      int formatIndex = style.getDataFormat();
      String formatString = style.getDataFormatString();
      if (formatString == null) {
       // formatString could not be found, so it must be a builtin format.
       formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
      }
      double value = Double.valueOf(lastContents);
      lastContents = formatter.formatRawCellContents(value, formatIndex, formatString);
      nextIsStyledNumeric = false;
     } 
     // Print out the contents
     System.out.println(lastContents);
    }
   }
  }

  public void characters(char[] ch, int start, int length)
            throws SAXException {
   //collect each character part to the content
   lastContents += new String(ch, start, length);
  }
 }

 public static void main(String[] args) throws Exception {
  ExampleEventUserModel example = new ExampleEventUserModel();
  //example.processOneSheet(args[0]);
  example.processAllSheets(args[0]);
 }
}