Apache POI วิธีเพิ่ม DataFormatter ที่กำหนดเองสำหรับจัดการจำนวนเต็ม 13 หลักเป็นสตริงไม่ใช่ตัวเลข

ฉันกำลังสร้างโปรเซสเซอร์ XLSX ที่แปลง XLSX เป็นไฟล์ CSV เนื่องจากไฟล์อาจมีขนาดใหญ่มาก ฉันจึงใช้แนวทางตามเหตุการณ์โดยใช้ XSSFSheetXMLHandler

วิธีนี้ใช้ได้ผลดีอย่างสมบูรณ์ แต่ไฟล์ XLSX ของฉันมีตัวเลขยาว (13 หลัก) ซึ่งเป็นหมายเลขประจำตัวที่ไม่ซ้ำกัน ไม่ใช่ตัวเลขจริง เมื่อรันโค้ดของฉันบนเครื่อง Windows มันจะแยกตัวเลขอย่างถูกต้อง แต่เมื่อรันบนเครื่อง Linux มันจะแปลงเป็นสัญลักษณ์ E

ตัวอย่างเช่น: ค่าแหล่งที่มาคือ 7401075293087 บน windows ค่านี้จะถูกแตกออกเป็น CSV ของฉันอย่างถูกต้อง แต่บน Linux ค่าจะเป็น 7.40108E+12

ปัญหาของ XSSFSheetXMLHandler คือมันจะอ่าน XLSX ใต้หน้าปก จากนั้นจึงโยนเหตุการณ์ที่ SheetContentsHandler จับได้ซึ่งคุณต้องนำไปใช้ วิธีหนึ่งใน SheetContentsHandler คือวิธีการของเซลล์ที่มีลายเซ็นต์: cell(String cellReference, String formattedValue, XSSFComment comment)

อย่างที่คุณเห็น วิธีนี้ได้รับเซลล์ที่จัดรูปแบบแล้ว (ดังนั้นในกรณีของฉันจะได้รับ "7.40108E+12") ตรรกะที่เหลือทั้งหมดเกิดขึ้นภายใต้ผ้าคลุม

จากการตรวจสอบของฉัน ฉันเชื่อว่าวิธีแก้ปัญหาอยู่ที่การกำหนด DataFormatter แบบกำหนดเองซึ่งจะถือว่าจำนวนเต็ม 13 หลักเป็นสตริงโดยเฉพาะ แทนที่จะจัดรูปแบบเป็นสัญลักษณ์ E

น่าเสียดายที่แผนของฉันไม่ได้ผลตามที่คาดไว้ และฉันไม่พบความช่วยเหลือทางออนไลน์ ด้านล่างนี้เป็นสารสกัดจากรหัสของฉัน ฉันลองสิ่งต่อไปนี้ในวิธี processSheet:

     Locale locale = new Locale.Builder().setLanguage("en").setRegion("ZA").build(); 
     DataFormatter formatter = new DataFormatter(locale);
     Format format = new MessageFormat("{0,number,full}");
     formatter.addFormat("#############", format);

นี่คือสารสกัดจากรหัสของฉัน:

เนื้อหาหลักของรหัส:

 public void process(String Filename)throws IOException, OpenXML4JException, ParserConfigurationException, SAXException {
     ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(this.xlsxPackage);
     XSSFReader xssfReader = new XSSFReader(this.xlsxPackage);
     StylesTable styles = xssfReader.getStylesTable();
     XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
     while (iter.hasNext()) {
          InputStream stream = iter.next();
          String sheetName = iter.getSheetName();
          outStream = new FileOutputStream(Filename);
          logger.info(sheetName);
          this.output = new  PrintWriter(Filename);
          processSheet(styles, strings, new SheetToCSV(), stream);
          logger.info("Done with Sheet   :"+sheetName);
          output.flush();
          stream.close();
          outStream.close();
          output.close();
         ++index; 
     }
 } 

 public void processSheet(StylesTable styles,ReadOnlySharedStringsTable strings,SheetContentsHandler sheetHandler, InputStream sheetInputStream)
         throws IOException, ParserConfigurationException, SAXException {

     InputSource sheetSource = new InputSource(sheetInputStream);
     try {
         XMLReader sheetParser = SAXHelper.newXMLReader();
         ContentHandler handler = new XSSFSheetXMLHandler(styles, null, strings, sheetHandler, formatter, false);
         sheetParser.setContentHandler(handler);
         sheetParser.parse(sheetSource);
      } catch(ParserConfigurationException e) {
         throw new RuntimeException("SAX parser appears to be broken - " + e.getMessage());
      }
 }

และนี่คือตัวจัดการแบบกำหนดเอง:

private class SheetToCSV implements SheetContentsHandler {
         private boolean firstCellOfRow = false;
         private int currentRow = -1;
         private int currentCol = -1;

     private void outputMissingRows(int number) {

         for (int i=0; i<number; i++) {
             for (int j=0; j<minColumns; j++) {
                 output.append(',');
             }
             output.append('\n');
         }
     }

     public void startRow(int rowNum) {
         // If there were gaps, output the missing rows
         outputMissingRows(rowNum-currentRow-1);
         // Prepare for this row
         firstCellOfRow = true;
         currentRow = rowNum;
         currentCol = -1;
     }

     public void endRow(int rowNum) {
         // Ensure the minimum number of columns
         for (int i=currentCol; i<minColumns; i++) {
             output.append(',');
         }
         output.append('\n');
     }

     public void cell(String cellReference, String formattedValue,
             XSSFComment comment) {
         logger.info("CellRef :: Formatted Value   :"+cellReference+" :: "+formattedValue);              
         if (firstCellOfRow) {
             firstCellOfRow = false;
         } else {
             output.append(',');
         }

         // gracefully handle missing CellRef here in a similar way as XSSFCell does
         if(cellReference == null) {
             cellReference = new CellRangeAddress(currentRow, currentCol, currentCol, currentCol).formatAsString();
         }

         // Did we miss any cells?
         int thisCol = (new CellReference(cellReference)).getCol();
         int missedCols = thisCol - currentCol - 1;
         for (int i=0; i<missedCols; i++) {
             output.append(',');
         }
         currentCol = thisCol;

         // Number or string?
         try {
             Double.parseDouble(formattedValue);
             output.append(formattedValue);
         } catch (NumberFormatException e) {
             //formattedValue = formattedValue.replaceAll("\\t", "");
             //formattedValue = formattedValue.replaceAll("\\n", "");
             //formattedValue = formattedValue.trim();
             output.append('"');
             output.append(formattedValue.replace("\"", "\\\"").trim());
             output.append('"');
         }
     }

     public void headerFooter(String text, boolean isHeader, String tagName) {
         // Skip, no headers or footers in CSV
     }

    @Override
    public void ovveriddenFormat(String celRef, int formatIndex,
            String formatedString) {
        // TODO Auto-generated method stub

    }

 }

person Greg Fullard    schedule 19.03.2018    source แหล่งที่มา


คำตอบ (2)


ไม่สามารถทำซ้ำได้หากไฟล์ถูกสร้างขึ้นโดยใช้ Excel และเซลล์ที่มีตัวเลข 13 หลักถูกจัดรูปแบบโดยใช้รูปแบบตัวเลข 0 หรือ # ไม่ใช่ General

แต่ความหมายของ "การทำงานบนเครื่อง Linux" คืออะไร? หากฉันกำลังสร้างไฟล์ *.xlsx โดยใช้ Libreoffice Calc โดยมีเซลล์ที่มีตัวเลข 13 หลักซึ่งจัดรูปแบบโดยใช้รูปแบบตัวเลข General ดังนั้น Calc จะแสดงเป็นตัวเลข 13 หลัก แต่ Excel จะไม่แสดง ในการแสดงตัวเลข 13 หลักใน Excel เซลล์จะต้องจัดรูปแบบโดยใช้รูปแบบตัวเลข 0 หรือ #

apache poi DataFormatter ถูกสร้างมาให้ทำงานเหมือนที่ Excel ทำ และ Excel แสดงค่าจากตัวเลข 12 หลักเป็นสัญกรณ์ทางวิทยาศาสตร์ เมื่อจัดรูปแบบโดยใช้ General

คุณสามารถเปลี่ยนพฤติกรรมนี้ได้โดยใช้:

...
    public void processSheet(
            StylesTable styles,
            ReadOnlySharedStringsTable strings,
            SheetContentsHandler sheetHandler, 
            InputStream sheetInputStream) throws IOException, SAXException {
        DataFormatter formatter = new DataFormatter();
        formatter.addFormat("General", new java.text.DecimalFormat("#.###############"));
...
person Axel Richter    schedule 19.03.2018
comment
เท็กซัส แอ็กเซล. ข้อเสนอแนะของคุณทำให้ฉันเข้าใจ เกี่ยวกับความคิดเห็นอื่นๆ ของคุณ: ขออภัย ฉันไม่สามารถควบคุมการจัดรูปแบบของไฟล์ต้นฉบับได้ ดังนั้นฉันจึงต้องประมวลผลสิ่งที่ฉันมี (และฉันมีรูปแบบที่แตกต่างกันมากมาย ดังนั้นจึงพยายามเป็นแบบทั่วไป) - person Greg Fullard; 03.04.2018
comment
ความคิดเห็นเกี่ยวกับเครื่อง Linux อีกครั้ง: กล่อง dev หลักของฉันคือเครื่อง Ubuntu 16.04 ซึ่งให้ข้อผิดพลาดตามที่อธิบายไว้ เมื่อฉันรันโค้ดบนแล็ปท็อป Windows ของลูกค้า ข้อผิดพลาดก็ไม่เกิดขึ้น ไฟล์เดียวกันทุกประการ รหัสฐานเดียวกันทุกประการ ด้วยเหตุนี้ ฉันจึงสันนิษฐานว่าเป็นปัญหาเฉพาะสถานที่ - person Greg Fullard; 03.04.2018
comment
สำหรับบันทึกฉันได้เพิ่มรูปแบบดังนี้: formatter.addFormat(General, new java.text.DecimalFormat(#.####################### ###)); ###)); นี่เป็นสิ่งจำเป็นเนื่องจากฟิลด์อื่นๆ ใน XLSX ของฉันมีค่าเช่น 0.00160455519952056 - person Greg Fullard; 03.04.2018

DZONE เขียนบทความเจาะลึกเกี่ยวกับเรื่องนี้: https://dzone.com/articles/simple-string-representation-of-java-decimal-numbe

คำตอบอื่นจาก StackOverflow คือ:

Row row = sheet.getRow(0);
Object o = getCellValue(row.getCell(0));
System.out.println(new BigDecimal(o.toString()).toPlainString());

อ้างอิง: Apache POI DataFormatter ส่งคืนสัญลักษณ์ทางวิทยาศาสตร์

ฉันไม่ได้ทดสอบปัญหาที่แท้จริงของคุณบนเครื่อง linux .. แต่ฉันหวังว่านี่จะให้คำตอบบางอย่างในตอนกลางคืน!

person Luigi D'Amico    schedule 19.03.2018