2017-09-25 4 views
1

J'utilise Apache POI 3.17 (actuel). Lorsque j'utilise HSSFCell.setFormula() pour insérer une formule comme "A1 + 17" cela fonctionne. Quand je fais la même chose en mode streaming, en utilisant SXSSFCell.setFormula() la formule apparaît (avec un "=") dans la ligne d'entrée mais le résultat affiché dans la cellule est toujours 0.Comment Apache POI peut-il utiliser des formules en mode streaming?

J'ai essayé avec la cellule types NUMERIC et FORMULA. Voici mon exemple minimal ne fonctionne pas:

final SXSSFWorkbook wb = new SXSSFWorkbook(); 
final SXSSFSheet sheet = wb.createSheet("Test-S"); 
final SXSSFRow row = sheet.createRow(0); 

final SXSSFCell cell1 = row.createCell(0); 
cell1.setCellType(CellType.NUMERIC); 
cell1.setCellValue(124); 

final SXSSFCell formulaCell1 = row.createCell(1); 
formulaCell1.setCellType(CellType.FORMULA); 
formulaCell1.setCellFormula("A1 + 17"); 

final SXSSFCell formulaCell2 = row.createCell(2); 
formulaCell2.setCellType(CellType.NUMERIC); 
formulaCell2.setCellFormula("A1+18"); 

FileOutputStream os = new FileOutputStream("/tmp/test-s.xlsx"); 
wb.write(os); 
wb.close(); 
os.close(); 

Les trois cellules présentent comme 124/0/0, bien que dans la ligne d'entrée des formules sont affichées correctement.

Tous les indices sont appréciés.

Répondre

2

Cela fonctionne pour moi avec Excel 2016, j'obtiens les bons résultats dans les cellules lorsque j'ouvre le fichier d'exemple. Probablement les versions plus anciennes d'Excel gèrent cette façon légèrement différente, s'il vous plaît essayer de forcer l'évaluation des formules avec les suivantes deux choses

// evaluate all formulas and store cached results 
wb.getCreationHelper().createFormulaEvaluator().evaluateAll(); 
// suggest to Excel to recalculate the formulas itself as well 
sheet.setForceFormulaRecalculation(true); 

Espérons que l'un de ces deux vont le faire fonctionner pour vous aussi.

+0

Merci beaucoup! evaluateAll() a fait l'affaire. Mais je vais garder les deux déclarations proposées. – Renardo

+0

Quelques remarques supplémentaires: Dans certains cas, evaluateAll() peut lancer une exception, indiquant qu'une ligne a déjà été vidée. Dans ce cas, evaluateFormulaCellEnum (cellule) peut être utilisé. – Renardo

1

Bien sûr, j'aurais dû mentionner que j'utilise LibreOffice. J'ai maintenant trouvé que LibreOffice ne recalcule pas intentionnellement les formules à partir d'une feuille créée par Excel, et il considère que les feuilles de POI sont créées par Excel.

Voir https://ask.libreoffice.org/en/question/12165/calc-auto-recalc-does-not-work/.

Modifier les paramètres de LibreOffice (Outils - Options - LibreOffice Calc - formule - Recalcul lors du chargement du fichier) aide.

1

Les réponses ne répondent pas à la question de savoir pourquoi ce problème avec OpenOffice/Libreoffice ne se produit que si SXSSFCell est utilisé comme cellule de formule. Lorsque vous utilisez XSSFCell en tant que cellule de formule, cela ne se produit pas.

La réponse est que SXSSFCelltoujours utilise une valeur de cellule, même si la formule n'a pas été évaluée du tout. Et le pire est qu'il utilise la valeur 0 (zéro) si la formule n'a pas été évaluée du tout. C'est un abus fondamental de la valeur 0 en mathématiques. La valeur 0 explicitement et non signifie qu'il n'y a pas de valeur ou qu'il existe une valeur inconnue. Cela signifie qu'il y a la valeur 0 et rien d'autre. Donc la valeur 0 devrait pas être utilisée comme le résultat de la formule cachée d'une formule non évaluée. Au lieu de aucune valeur doit être utilisée jusqu'à ce que la formule est évaluée. Exact comme XSSFCell fait.

Donc la réponse vraiment correcte doit être que apache poi devrait corriger leur code SXSSFCell.

Solution jusqu'à cette:

import java.io.FileOutputStream; 

import org.apache.poi.xssf.streaming.*; 

import org.apache.poi.ss.usermodel.CellType; 

import java.lang.reflect.Field; 

import java.util.TreeMap; 

public class CreateExcelSXSSFFormula { 

public static void main(String[] args) throws Exception { 

    SXSSFWorkbook wb = new SXSSFWorkbook(); 

    SXSSFSheet sheet = wb.createSheet("Test-S"); 
    SXSSFRow row = sheet.createRow(0); 

    SXSSFCell cell = row.createCell(0); 
    cell.setCellValue(124); 

    SXSSFFormulaonlyCell formulacell = new SXSSFFormulaonlyCell(row, 1); 
    formulacell.setCellFormula("A1+17"); 

    cell = row.createCell(2); 
    cell.setCellFormula("A1+17"); 

    formulacell = new SXSSFFormulaonlyCell(row, 3); 
    formulacell.setCellFormula("A1+18"); 

    cell = row.createCell(4); 
    cell.setCellFormula("A1+18"); 

    wb.write(new FileOutputStream("test-s.xlsx")); 
    wb.close(); 
    wb.dispose(); 

} 

private static class SXSSFFormulaonlyCell extends SXSSFCell { 

    SXSSFFormulaonlyCell(SXSSFRow row, int cellidx) throws Exception { 
    super(row, CellType.BLANK); 
    Field _cells = SXSSFRow.class.getDeclaredField("_cells"); 
    _cells.setAccessible(true); 
    @SuppressWarnings("unchecked") //we know the problem and expect runtime error if it possibly occurs 
    TreeMap<Integer, SXSSFCell> cells = (TreeMap<Integer, SXSSFCell>)_cells.get(row); 
    cells.put(cellidx, this); 
    } 

    @Override 
    public CellType getCachedFormulaResultTypeEnum() { 
    return CellType.BLANK; 
    } 

} 

} 
+0

Merci @Axel Richter; ça a l'air très intéressant. Oui, SXSSFRow ne dispose pas de méthodes ou de membres protégés, donc la seule façon raisonnable de l'étendre est par réflexion. Je vois que 'SXXSFCell'.'getCachedFormulaResultTypeEnum()' est obsolète; peut-être que cela signifie que les développeurs POI ne travaillent pas actuellement sur ce sujet - ou que son utilisation est découragée. – Renardo

+0

@Renardo: Ma solution de contournement fournie est ** pas ** une solution mais une solution de contournement uniquement. La solution consiste à modifier le code de 'SXSSFCell' afin qu'il ** n'utilise pas la valeur 0 comme résultat de la formule en mémoire cache pour les formules non évaluées. Mais la gestion du changement de 'apache poi' est aussi étrange. A ce jour, l'apidoc indique 'SXSSFCell.getCachedFormulaResultTypeEnum()' comme étant obsolète et 'SXSSFCell.getCachedFormulaResultType()' renvoyant un 'CellType'. Mais dans la vraie version 3.17 ** utilise ** 'getCachedFormulaResultTypeEnum()' et là 'getCachedFormulaResultType()' retourne un 'int'. –