[JAVA] Apache POI Addictive Point List

Was für ein Artikel?

Apache POI ist sehr praktisch, hat aber viele Suchtpunkte. Dieser Artikel enthält eine Aufzeichnung der Punkte, die beim Erstellen des Excel-Dateivergleichstools "Square Diff" [^ 1] hängen geblieben sind. Ich hoffe es hilft jemandem.

Auch hier ist ** Apache POI sehr nützlich. ** Ich bin den Mitwirkenden dankbar, dass sie so etwas kostenlos nutzen. ** Wenn Sie Beschwerden haben, tragen Sie sich selbst bei und werfen Sie zumindest ein Problem auf ** [^ 2]. Mit anderen Worten, dieser Artikel ist ein Bericht über meinen eigenen Mangel an Macht, dies zu tun.

Bitte beachten Sie, dass dieser Artikel wahrscheinlich Inhalte enthält, die auf meinen Missverständnissen und meinem Unverständnis beruhen. Wenn Sie sie bemerken, würde ich mich freuen, wenn Sie einen Kommentar abgeben könnten.

[^ 1]: Es ist sehr praktisch, also benutze es bitte. Es wird auf hier veröffentlicht und Artikel wird ebenfalls geschrieben.

[^ 2]: Eines Tages ist es mein nächstes Ziel als Amateurprogrammierer, zu OSS beizutragen.

Die von diesem Artikel angenommene Version

Liste der Anpassungspunkte

Suchtpunkt Ziel POI ver. Datum der Beschreibung
1 Ich kann den Blatttyp nicht erkennen (Arbeitsblatt, Diagrammblatt, Makroblatt, Dialogblatt). SheetSchnittstelle 4.1.0 2019/7/15 Erster Entwurf
2 Ist die Groß- / Kleinschreibung des Klassennamens ein Typ? XSSFDialogsheetKlasse 4.1.0 2019/7/15 Erster Entwurf
3 Beachten Sie Methodennamen, die sich von den üblichen Namenskonventionen unterscheiden HSSFSheet#getDialog()Methode 4.1.0 2019/7/15 Erster Entwurf
4 HSSFSheet#getDialog()Funktioniert nicht HSSFSheet#getDialog()Methode 4.1.0 2019/7/15 Erster Entwurf
5 .xlsx/.Dialogfeld / Makroblatt im XLSM-Format wird ignoriert WorkbookFactory#create(File)Methode 4.1.0 2019/7/15 Erster Entwurf
6 .Dialogfelder im XLS-Stil können selbst von der Ereignismodell-API nicht identifiziert werden WSBoolRecord#getDialog()Methode 4.1.0 2019/7/15 Erster Entwurf
7 Buchformat (.xls vs .xlsx/.xlsm) kann die Leerzeichen in der Formel reproduzieren oder nicht Cell#getCellFormula()Methode 4.1.0 2019/8/12 zusätzlich
8 Eine API zum Bearbeiten diagonaler Ränder wird nicht bereitgestellt CellStyleSchnittstelle 4.1.0 2019/10/5 hinzugefügt
9 Das Verhalten in Bezug auf leere Kommentare unterscheidet sich je nach Dateiformat Comment#getString()Methode 4.1.2 2020/3/25 hinzugefügt
10 XSSFComment#setVisible(boolean)Funktioniert nicht XSSFComment#setVisible(boolean)Methode 4.1.2 2020/3/25 hinzugefügt
11

Sobald ich mich erinnere, werde ich es hinzufügen.

Individuelle Erklärung

Nr. 1 Blatttyp (Arbeitsblatt, Diagrammblatt, Makroblatt, Dialogblatt) kann nicht unterschieden werden

Erläuterung

Es gibt folgende Arten von Excel-Tabellen:

Möglicherweise möchten Sie zwischen diesen Typen, insbesondere Arbeitsblättern, unterscheiden, aber aus irgendeinem Grund bietet die Benutzermodell-API von Apache POI keine solche Funktionalität.

Die erste Wahl bei der Arbeit mit Apache POI in Excel-Tabellen ist "org.apache.poi", das in einer Reihe von Funktionen enthalten ist, die als "Benutzermodell-API" bezeichnet werden und Dateien im XLS / XLSX / XLSM-Format transparent verarbeiten können. Es ist .ss.usermodel.Sheet` zu verwenden. Für diese Schnittstelle wird keine API wie "Sheet # isWorksheet ()" bereitgestellt.

Was sollen wir dann tun? Die Sheet-Schnittstelle hat die folgende hierarchische Struktur.

① ** Blatt **: Formatblatt .xls / .xlsx / .xlsm ├─ ② ** HSSFSheet **: XLS-Formatblatt ├─ ③ ** XSSFSheet **: .xlsx / .xlsm-Formatblatt X ├─ ④ ** XSSFChartSheet **: Diagrammblatt im XLSX / XLSM-Format X └─ ⑤ ** XSSFDialogsheet **: Dialogblatt im XLSX / XLSM-Format └─ ⑥ ** SXSSFSheet **: .xlsx-Formatblatt (Streaming-Methode)

Bei Blättern im Format .xlsx / .xlsm können Sie den Blatttyp überprüfen, indem Sie prüfen, ob es sich um eine Instanz von ④⑤ [^ 3] handelt. Für Blätter im XLS-Format bietet ② eine Methode, um zurückzugeben, ob es sich um ein Dialogblatt handelt [^ 4], aber keine Methode, um zu unterscheiden, ob es sich um ein Diagrammblatt handelt oder nicht. Dies ist eine Sackgasse.

[^ 3]: In Wirklichkeit gibt es jedoch eine Fallstricke, die in Nr. 5 eingeführt wurde. [^ 4]: In der Realität funktioniert diese Methode jedoch nicht wie in Nr. 4 eingeführt.

Lösung

Dies kann durch Verwendung einer Lösung der unteren Schicht gelöst werden.

Details werden in [diesem Artikel] vorgestellt (https://qiita.com/nmby/items/fe46efb2c849d96c044c).

[^ 5]: In Wirklichkeit hat dies jedoch auch die in Nr. 6 eingeführten Fallstricke.

Nr.2 Ist der Groß- / Kleinbuchstabe des Klassennamens ein Typ?

Erläuterung

Die abgeleitete Hierarchie der in Nr. 1 eingeführten Blattschnittstelle wird neu gedruckt.

org.apache.poi.ss.usermodel.Sheet  ├─ org.apache.poi.hssf.usermodel.HSSFSheet  ├─ org.apache.poi.xssf.usermodel.XSSFSheet  │  ├─ org.apache.poi.xssf.usermodel.XSSFChartSheet  │  └─ org.apache.poi.xssf.usermodel.XSSFDialogsheet  └─ org.apache.poi.xssf.streaming.SXSSFSheet

Verstehst du? Nur das XSSFDialogsheet hat ein niedrigeres "s". Absichtlich? Nein, es ist ein Tippfehler.

Lösung

Wenn der Implementierer es so benannt hat, ist dies der richtige Klassenname. Glaubensbasierter Ichamon ist keine Lösung. Nutzen Sie IDE-Vervollständigungen und -Vorschläge.

Nr. 3 Beachten Sie den Methodennamen, der sich von der allgemeinen Namenskonvention unterscheidet

Erläuterung

In der Erklärung von Nr. 1 habe ich geschrieben, dass die Schnittstelle "HSSFSheet" eine Methode bietet, um zurückzugeben, ob es sich um ein Dialogblatt handelt oder nicht. Die nächste Methode ist das. (Nachdruck aus [API-Dokument], bereitgestellt von Apache POI (https://poi.apache.org/apidocs/4.1/org/apache/poi/hssf/usermodel/HSSFSheet.html#getDialog--))

getDialog   public boolean getDialog() get whether sheet is a dialog sheet or not Returns:   isDialog or not

get gibt boolesche Werte zurück. Ist es nicht gut Es gibt keine Regel, dass es "isDialogSheet ()" heißen muss.

Lösung

Nehmen Sie bei der Suche nach einer Methode diesen Methodennamen nicht an. Wenn Sie eine Methode wünschen, die einen Booleschen Wert zurückgibt, sollten Sie die Methode treffen, deren Rückgabetyp boolesch ist. Das ist die Wahrheit. Sie sollten die API-Dokumentation von oben nach unten lesen, alle Annahmen beseitigen und nach der gewünschten Funktionalität suchen.

Nr.4 HSSFSheet # getDialog () funktioniert nicht

Erläuterung

Beachten Sie, dass die in Nr. 3 eingeführte Methode "HSSFSheet # getDialog ()" nicht funktioniert. Speziell,

Ich werde es demonstrieren.

1) Erstellen Sie eine Test-Excel-Datei (XLS-Format)

Sie können sehen, dass Sie die folgenden Blatttypen erstellen können, indem Sie mit der rechten Maustaste auf die Registerkarte Blattname klicken und Einfügen ...

No.4-a.png

Erstellen Sie jedes Blatt und beschreiben Sie den entsprechenden Inhalt.

No.4-b.png

Speichern Sie diese als Excel-Arbeitsmappe im XLS-Format.

2) Testcode erstellen

Schreiben Sie beispielsweise den folgenden Testcode.

package mypackage;

import java.io.File;

import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

public class Main {
    
    public static void main(String[] args) {
        
        try (Workbook wb = WorkbookFactory.create(
                new File("C:\\Users\\xxx\\No.4 Tests.xls"))) {
            
            System.out.println(wb.getNumberOfSheets());
            
            wb.sheetIterator().forEachRemaining(s -> {
                System.out.print(String.format(
                        "%s : %s : ",
                        s.getSheetName(),
                        s.getClass().getName()));
                
                if (s instanceof HSSFSheet) {
                    HSSFSheet sheet = (HSSFSheet) s;
                    try {
                        System.out.println(sheet.getDialog());
                    } catch (NullPointerException e) {
                        System.out.println("★ NPE-Vorkommen ★");
                    }
                } else {
                    System.out.println();
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3) Testausführung

Das Ausführen des obigen Testcodes führt zu folgenden Ergebnissen:

4
A_Arbeitsblatt: org.apache.poi.hssf.usermodel.HSSFSheet : false
B_Diagrammblatt: org.apache.poi.hssf.usermodel.HSSFSheet :★ NPE-Vorkommen ★
C_Makroblatt: org.apache.poi.hssf.usermodel.HSSFSheet : false
D_Dialogblatt: org.apache.poi.hssf.usermodel.HSSFSheet : false

Sie können sehen, dass das "B_Graph Sheet" eine NullPointerException auslöst und das "D_Dialog Sheet" false anstelle von true zurückgibt.

Lösung

Ich denke, Sie sollten nicht die HSSFSheet # getDialog () -Methode verwenden.

Dialogfeld / Makroblatt im Format .xlsx / .xlsm Nr. 5 wird ignoriert

Erläuterung

Es scheint, dass die Apache POI-Benutzermodell-API keine Dialog- und Makroblätter laden kann, zumindest nicht für Dateien im XLSX- / XLSM-Format, die mit Excel für Office 365 erstellt wurden. Ich werde es demonstrieren.

1) Erstellen Sie eine Test-Excel-Datei (XLSM-Format)

Speichern Sie die in Nr. 4 verwendete Testdatei erneut im XLSM-Format.

2) Testcode erstellen

Verwenden Sie den gleichen Testcode wie Nr. 4.

3) Testausführung

Wenn Sie den Testcode ausführen, erhalten Sie die folgenden Ergebnisse:

2
A_Arbeitsblatt: org.apache.poi.xssf.usermodel.XSSFSheet : 
B_Diagrammblatt: org.apache.poi.xssf.usermodel.XSSFChartSheet : 

Sie können sehen, dass "C_ Macro Sheet" und "D_ Dialog Sheet" nicht geladen sind.

Lösung

Wenn Sie Makro- und Dialogblätter aus einer Excel-Datei im XLSX- / XLSM-Format lesen möchten, sollten Sie die Verwendung der POI-Benutzermodell-API aufgeben und die Ereignismodell-API oder SAX (Simple API for XML) verwenden. Ich denke (nicht verifiziert).

Das Dialogfeld im XLS-Format Nr. 6 kann selbst von der Ereignismodell-API nicht identifiziert werden

Erläuterung

Wie in Nr. 1 eingeführt, kann die Apache POI-Benutzermodell-API nicht zwischen Arbeitsblatttypen im XLS-Format unterscheiden. Eine Funktion namens Event Model API ist Ihre nächste Wahl.

Das XLS-Excel-Dateiformat wird als BIFF (Binary Exchange File Format) bezeichnet. Der Inhalt besteht aus einer Sammlung von BIFF-Datensätzen. Für einen Überblick über BIFF ist dieser Blog leicht zu verstehen und detaillierte Spezifikationen. ms-xls / cd03cb5f-ca02-4934-a391-bb674cb8aa06) wurde von Micorosoft veröffentlicht.

Ich kann den Inhalt des BIFF-Datensatzes mit der Ereignismodell-API von Apache POI lesen, aber anscheinend funktioniert die Methode "WSBoolRecord # getDialog ()" nicht richtig und ich kann das Dialogfeld immer noch nicht richtig erkennen. Ich werde es unten demonstrieren.

1) Bereiten Sie eine Excel-Testdatei (XLS-Format) vor.

Verwenden Sie dieselbe Datei wie in Nr. 4.

2) Testcode

Schreiben Sie beispielsweise den folgenden Testcode.

package mypackage;

import java.io.FileInputStream;

import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

public class Main {
    
    private static class TestListener implements HSSFListener {
        
        @Override
        public void processRecord(Record record) {
            System.out.println(record);
        }
    }
    
    public static void main(String[] args) throws Exception {
        
        try (FileInputStream fin = new FileInputStream(
                "C:\\Users\\xxx\\No.6 Tests.xls");
                POIFSFileSystem poifs = new POIFSFileSystem(fin)) {
            
            HSSFRequest req = new HSSFRequest();
            TestListener listener = new TestListener();
            req.addListenerForAllRecords(listener);
            HSSFEventFactory factory = new HSSFEventFactory();
            factory.abortableProcessWorkbookEvents(req, poifs);
        }
    }
}

3) Testausführung

Wenn Sie den Testcode ausführen, wird der Inhalt des BIFF-Datensatzes ausgegeben.

Wenn Sie genau hinschauen, wird es lange dauern, also werde ich es kurz erklären. Der folgende BOF-Datensatz markiert den Beginn der Definition des Dialogfelds. Es ist schwer zu verstehen, warum, aber das Attribut .type des BOF-Datensatzes im Dialogfeld ist als "0x0010" definiert, was dem Arbeitsblatt entspricht. (Andererseits ist das Diagrammblatt als "0x0020" und das Makroblatt als "0x0040" definiert, was unterschieden werden kann.)

...
5201: 
5202: [BOF RECORD]
5203:     .version  = 0x0600
5204:     .type     = 0x0010 (worksheet)
5205:     .build    = 0x4F5A
5206:     .buildyear= 1997
5207:     .history  = 0x000200C9
5208:     .reqver   = 0x00000806
5209: [/BOF RECORD]
5210: 
...

Arbeitsblätter und Dialogblätter sollten durch das Attribut .dialog nachfolgender WSBOOL-Datensätze (https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-xls/ccbd73f9-) unterschieden werden Aus irgendeinem Grund wird ff1d-4069-be31-13d16c074ec4) trotz des Dialogblatts tatsächlich als ".dialog = false" ausgegeben.

...
5263: 
5264: [WSBOOL]
5265:     .wsbool1        = 4
5266:         .autobreaks = false
5267:         .dialog     = false
5268:         .rowsumsbelw= false
5269:         .rowsumsrigt= false
5270:     .wsbool2        = ffffffd1
5271:         .fittopage  = true
5272:         .displayguts= false
5273:         .alternateex= true
5274:         .alternatefo= true
5275: [/WSBOOL]
5276: 
...

Lösung

WSBoolRecord Ich vermute, dass ein Problem mit der Klassenimplementierung vorliegt. Diese BIFF-Spezifikation und [Von Apahe veröffentlichter Quellcode] Möglicherweise können Sie die Ursache und Gegenmaßnahmen durch einen Blick auf (https://poi.apache.org/download.html#POI-4.1.0) herausfinden.

Alternativ kann ein Blatt, das etwas knifflig ist, aber keinen ROW-Datensatz enthält, als Dialogblatt betrachtet werden. (Nicht überprüft)

Das Buchformat Nr. 7 (.xls vs .xlsx / .xlsm) kann Leerzeichen in der Formel reproduzieren oder nicht

Erläuterung

Die Apache POI-Benutzermodell-API behält keine Leerzeichen in Formeln bei. Dies ist in der Apache POI-Dokumentation (https://poi.apache.org/components/spreadsheet/formula.html) angegeben.

Es wäre schön, wenn die Leerzeichen immer entfernt würden, ohne beibehalten zu werden, aber das Verhalten hängt vom Format der ursprünglichen Excel-Datei ab.

Insbesondere wenn eine Zelle mit "= 1 + 2" gefüllt ist und dies von der Methode "Cell # getCellFormula ()" gelesen wird,

Dies ist beispielsweise ein Problem, wenn Sie Formeln in Excel-Dateien in verschiedenen Formaten vergleichen möchten.

Lösung

Ein Ansatz besteht darin, eine eigene Funktion zu erstellen, um unnötige Leerzeichen aus der Formelzeichenfolge zu entfernen, die mit der Methode "Cell # getCellFormula ()" abgerufen wurde, und die Formelzeichenfolge zu standardisieren. Zu diesem Zeitpunkt sind einige Punkte zu beachten.

Das erste ist, dass Sie den Leerraum im String-Literal nicht entfernen dürfen. Dies liegt daran, dass die Formeln =" Hallo Welt !! " und die Formeln =" Hallo Welt !! " unterschiedliche Bedeutungen haben. Es sollte jedoch nicht zu schwierig sein, mit solchen Problemen umzugehen.

Der zweite zu beachtende Punkt ist, dass in Excel Leerzeichen (Leerzeichen halber Breite) auch als "Referenzoperatoren" dienen. Wenn beispielsweise die Formel "= MAX (A1: C3 B2: E5)" in die Zelle eingegeben wird, ist dies "im gemeinsamen Teil des Zellenbereichs A1: C3 und des Zellenbereichs B2: E5, dh im Zellenbereich B2: C3. Stellt den Maximalwert von dar. In Excel können Sie Zellbereiche benennen. Daher kann die Zeichenfolge "= Bereich A Bereich B" auch eine gute Formel sein. (Wenn der gemeinsam genutzte Teil von "Bereich A" und "Bereich B" aus mehreren Zellen besteht, tritt der Fehler "#VALUE!" Auf, und wenn es sich um eine einzelne Zelle handelt, ist der Wert dieser Zelle das Berechnungsergebnis.) Natürlich sollten Sie solche Leerzeichen wie "Referenzoperatoren" nicht entfernen.

Dies ist eine Möglichkeit, unter Berücksichtigung dieser Punkte eine standardisierte Funktion zu erstellen.

Alternativ können Sie das Excel-Dateiformat unter PDF-Datei hier verstehen und Ihren eigenen Parser erstellen.

In jedem Fall ist es eine entmutigende Aufgabe.

Die API Nr. 8 zum Bearbeiten diagonaler Ränder wird nicht bereitgestellt

Erläuterung

CellStyle Schnittstelle und ihre abgeleitete Schnittstelle [HSSFCellStyle](https: //poi.apache) .org / apidocs / 4.1 / org / apache / poi / hssf / usermodel / HSSFCellStyle.html), [XSSFCellStyle](https://poi.apache.org/apidocs/4.1/org/apache/poi/xssf/usermodel/ Die XSSFCellStyle.html) -Schnittstelle bietet mehrere APIs zum Bearbeiten von Rahmen.

Wie Sie sehen können, wird nur die API für vertikale (links, rechts) und horizontale (oben, unten) Ränder bereitgestellt, nicht die API für diagonale Ränder.

Lösung

Es wird in diesem Blog veröffentlicht (Programmmemorandum usw. - Java Apache POI xls-Datei nur, um diagonale Ränder zu erhalten) Sie müssen sich auf die Informationen beziehen und die Verarbeitung in Bezug auf diagonale Ränder selbst implementieren. In diesem Fall ist es erforderlich, die Klassenstruktur innerhalb des POI zu verstehen.

Nr.9 Das Verhalten in Bezug auf leere Kommentare unterscheidet sich je nach Dateiformat

Erläuterung

Für leere Kommentare (Zellen mit Kommentaren, aber nichts in den Kommentaren) gibt die Arbeitsmappe im xls-Format die leere Zeichenfolge "" "zurück, während xlsx / xlsm Die Format-Arbeitsmappe gibt "null" zurück.

Cell cell = (Holen Sie sich das Zellobjekt auf irgendeine Weise)
Comment comment = cell.getCellComment();  //Wenn es keinen Kommentar gibt, wird null zurückgegeben
RichTextString richText = comment.getString();  //Für leere Kommentare wird auch ein Wert ungleich Null zurückgegeben
String str = richText.getString();  //★ Das Verhalten leerer Kommentare unterscheidet sich je nach Buchformat ★

Lösung

Zum Beispiel wäre es besser, mit der folgenden Methode auf eine leere Zeichenfolge "" "zu normalisieren.

String str = Optional.ofNullable(richText.getString).orElse("");

No.10 XSSFComment # setVisible (boolean) funktioniert nicht

Erläuterung

Wenn Sie "setVisible (false)" für einen Kommentar in einer sichtbaren Zelle ausführen, wird dieser nicht ausgeblendet, und wenn Sie "setVisible (true)" für einen Kommentar in einer versteckten Zelle ausführen, wird dies nicht angezeigt. Hmm. Ich fragte mich, ob wahr und falsch das Gegenteil waren, aber es funktionierte nicht.

Lösung

Die Methode "XSSFComment # setVisible (boolean)" kann nicht verwendet werden. Es wurde keine Alternative gefunden.

Mittel festziehen

Ich bin ein bisschen müde, also höre ich hier auf.

Es scheint hartnäckig zu sein, aber der Apache-POI ist sehr nützlich. Es ist sehr praktisch, aber wie oben erwähnt, ist es auch wahr, dass Sie verrückt werden, wenn Sie versuchen, es zu verwenden.

Eines Tages möchte ich mich selbst einbringen, aber ich möchte für eine Weile gehen, um meine geistige Gesundheit zu erhalten.

Recommended Posts

Apache POI Addictive Point List
[Apache POI] Entsprechende Excel-Version
Excel-Operation mit Apache POI
Apache Commons BeanUtils passen Punkt
Apache POI persönliches Memo Crossfish21
Verwendung von Apache POI
Bearbeiten Sie Excel mit Apache POI
Apache POI Excel mit Kotlin
Ausgabe nach Excel mit Apache POI!
[Apache POI] Beurteilung unnötiger Zellen