I've got a template workbook, with a sheet ("All data") which I populate using Apache POI. I don't know how many rows I'm going to need in "All data" when I start.
In another sheet (call it "Calc"), I have 4 columns containing formulae that do stuff based on "All data". I need to have as many rows in Calc as in "All data", and I thought the easiest way to do it would be to have, in the template, one row with the formulae in it, which I can then fill down the sheet as many times as necessary.
Thus, in the template I have:
Col1Header | Col2Header | Col3Header | Col4Header
=+'All data'!F2 | =IF(LEFT(A55,1)="4",'All data'!R2,"") | =IF(LEFT(A55,1)="4",'All data'!O2,"") | =+'All data'!W2
Then I would expect to be able to "fill down" from that first formula line, so that I have n rows (where n is the number of rows I'm using in the "All data" sheet).
However, I cannot see how to do "fill down" in Apache POI. Is it something that's not possible? Or am I looking for the wrong name?
Yes, an alternative method would be simply to change the template by manually copying down more rows than I would ever expect to be using, but that is (a) inelegant and (b) is asking for trouble in the future:-)
I feel sure there must be a better way?
If this is for an Office Open XML workbook (*.xlsx, XSSF) and current apache poi 5.0.0 is used, then XSSFSheet.copyRows can be used. The default CellCopyPolicy copies formulas and adjusts the cell references in them.
Example:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import java.io.FileInputStream;
import java.io.FileOutputStream;
class ExcelReadCopyRowsAndWrite {
public static void main(String[] args) throws Exception {
String fileIn= "./TestIn.xlsx";
String fileOut= "./TestOut.xlsx";
int n = 10; // 10 rows needed
int fillRowsFromIdx = 1; // start at row 2 (index 1) which is row having the formulas
int fillRowsToIdx = fillRowsFromIdx + n - 1;
try (Workbook workbook = WorkbookFactory.create(new FileInputStream(fileIn));
FileOutputStream out = new FileOutputStream(fileOut)) {
Sheet sheet = workbook.getSheet("Calc"); // do it in sheet named "Calc"
if (sheet instanceof XSSFSheet) {
XSSFSheet xssfSheet = (XSSFSheet) sheet;
for (int i = fillRowsFromIdx; i < fillRowsToIdx; i++) {
xssfSheet.copyRows(i, i, i+1, new CellCopyPolicy());
}
}
workbook.write(out);
}
}
}
ThecopyRows method is only in XSSF up to now. For an example how to copy formulas also working for BIFF workbook (*.xls, HSSF) see Apache POI update formula references when copying.
Related
When use setCellFormula set by paramete "CHISQ.TEST(ChiSq_Data!D5:F5,ChiSq_Data!L5:N5)",but the output was "=#CHISQ.TEST(ChiSq_Data!D5:F5,ChiSq_Data!L5:N5)", the # symbol make the formula did not work and shows #VALUE in the result excel.
How can I remove the # automatically?
This is a similar problem as this one: Apache POI Excel Formula entering # Symbols where they don't belong.
All new functions (introduced after Excel 2007) are prefixed with _xlfn in internally file storage. The GUI does not show that prefix if the Excel version is able to interpret that function. If the Excel version is too old to be able to interpret that function you may see that prefix even in GUI.
Apache POI creates Excel files and that's why writes formulas in file storage directly. Using:
cell.setCellFormula("CHISQ.TEST(ChiSq_Data!D5:F5,ChiSq_Data!L5:N5)");
it writes CHISQ.TEST(ChiSq_Data!D5:F5,ChiSq_Data!L5:N5) into the file storage but the Excel GUI expects _xlfn.CHISQ.TEST(ChiSq_Data!D5:F5,ChiSq_Data!L5:N5). That's why the #NAME? error.
But why the #? The # is the implicit intersection operator. Implicit intersection is a new feature of Excel 365 (a silly one in my opinion, as well as dynamic array and spilling array behavior). And because Excel 365 does not know the function CHISQ.TEST without the prefix but it contains arrays of cells as parameters, it puts # in front of it to show that it would use implicit intersection if it would know it.
So the solution is to put the correct prefix before the function name in file storage to make it work:
cell.setCellFormula("_xlfn.CHISQ.TEST(ChiSq_Data!D5:F5,ChiSq_Data!L5:N5)");
Complete example to test:
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCell;
class CreateExcelCHISQ_TEST {
public static void main(String[] args) throws Exception {
try (
Workbook workbook = new XSSFWorkbook(); FileOutputStream fileout = new FileOutputStream("Excel.xlsx") ) {
Sheet sheet = workbook.createSheet();
Row row;
Cell cell;
// Filling dummy data to another sheet
Sheet otherSheet = workbook.createSheet("ChiSq_Data");
row = otherSheet.createRow(4);
row.createCell(3).setCellValue(123);
row.createCell(4).setCellValue(456);
row.createCell(5).setCellValue(78);
row.createCell(11).setCellValue(122.5);
row.createCell(12).setCellValue(456.5);
row.createCell(13).setCellValue(77.5);
row = sheet.createRow(0);
cell = row.createCell(0);
//cell.setCellFormula("CHISQ.TEST(ChiSq_Data!D5:F5,ChiSq_Data!L5:N5)"); // wrong
cell.setCellFormula("_xlfn.CHISQ.TEST(ChiSq_Data!D5:F5,ChiSq_Data!L5:N5)");
workbook.write(fileout);
}
}
}
Title says it all -- need to hide all rows and columns that are outside of the the rows and columns containing my data.
I have tried several options:
How to hide the following Un-used rows in Excel sheet using Java Apache POI?
Permanently Delete Empty Rows Apache POI using JAVA in Excel Sheet
How to hide the following Un-used rows in Excel sheet using Java Apache POI?
But these never produce the desired effect. I'm using apache poi version 4.1.1
See the following screenshots showing the excel format I have versus the format I want. (Since I am new on stackoverflow, it doesn't allow me to embed the pictures directly. Weird I know.)
What I have
What I need
Hiding unused rows and columns is not provided by high level classes of apache poi until now.
Hiding unused rows is a setting in sheet format properties of Office Open XML, the format of XSSF (*.xlsx). There is defined how to handle rows per default. For example default row height. But there also can be set that rows are zero height per default. So only used rows, which have cells having content or format are visible. As apache poi does not have a method to set SheetFormatPr.setZeroHeight we need using the underlaying org.openxmlformats.schemas.spreadsheetml.x2006.main.* classes.
In binary BIFF format of HSSF (*.xls) hiding unused rows is a setting in DEFAULTROWHEIGHT record within the worksheet's record stream. There option flags can be set. Option flag 0x0002 means hiding unused rows. To set that using apache poi we need access to the org.apache.poi.hssf.record.DefaultRowHeightRecord. This only can be got from InternalSheet.
Hiding columns could be done using Sheet.setColumnHidden, but only for single columns. So to hide 100 columns one needs calling Sheet.setColumnHidden 100 times.
Excel also provides settings for column ranges from min column to max column. But Apache poi does not providing high level methods for this.
Using XSSF (Office Open XML) we need the org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols to get or set a org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol having the appropriate min and max and setHidden(true).
Using HSSF (BIFF) we need get or set the COLINFOrecord from/to the the worksheet's record stream which has the appropriate min and max and and setHidden(true).
The following complete example shows the code sample for the above. It uses ExcelExampleIn.xlsx or ExcelExampleIn.xls as input and sets unused rows hidden and sets columns hidden from given min to max column.
Tested and works using apache poi 4.1.1.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.model.InternalSheet;
import org.apache.poi.hssf.record.DefaultRowHeightRecord;
import org.apache.poi.hssf.record.ColumnInfoRecord;
import org.apache.poi.hssf.record.RecordBase;
import java.util.List;
public class ExcelHideUnusedRowsAndColumns {
static void setUnusedRowsHidden(Sheet sheet) throws Exception {
if (sheet instanceof XSSFSheet) {
// in OOXML set zeroHeight property true for all undefined rows, so only rows having special settings are visible
XSSFSheet xssfSheet = (XSSFSheet)sheet;
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet ctWorksheet = xssfSheet.getCTWorksheet();
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetFormatPr ctSheetFormatPr = ctWorksheet.getSheetFormatPr();
if (ctSheetFormatPr == null) ctSheetFormatPr = ctWorksheet.addNewSheetFormatPr();
ctSheetFormatPr.setZeroHeight(true);
} else if (sheet instanceof HSSFSheet) {
// in BIFF file format set option flag 0x0002 in DEFAULTROWHEIGHT record
HSSFSheet hssfSheet= (HSSFSheet)sheet;
java.lang.reflect.Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
_sheet.setAccessible(true);
InternalSheet internalSheet = (InternalSheet)_sheet.get(hssfSheet);
java.lang.reflect.Field defaultrowheight = InternalSheet.class.getDeclaredField("defaultrowheight");
defaultrowheight.setAccessible(true);
DefaultRowHeightRecord defaultRowHeightRecord = (DefaultRowHeightRecord)defaultrowheight.get(internalSheet);
defaultRowHeightRecord.setOptionFlags((short)2);
}
}
static void setColumnsHidden(Sheet sheet, int min, int max) throws Exception {
if (sheet instanceof XSSFSheet) {
// respect max column count 16384 (1 to 16384) for OOXML
if (max > 16384) max = 16384;
// in OOXML set cols settings in XML
XSSFSheet xssfSheet = (XSSFSheet)sheet;
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet ctWorksheet = xssfSheet.getCTWorksheet();
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols ctCols = ctWorksheet.getColsArray(0);
boolean colSettingFound = false;
for (org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol ctCol : ctCols.getColList()) {
if (ctCol.getMin() == min && ctCol.getMax() == max) {
colSettingFound = true;
ctCol.setHidden(true);
}
System.out.println(ctCol);
}
if (!colSettingFound) {
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol ctCol = ctCols.addNewCol();
ctCol.setMin(min);
ctCol.setMax(max);
ctCol.setHidden(true);
System.out.println(ctCol);
}
} else if (sheet instanceof HSSFSheet) {
// in BIFF min and max are 0-based
min = min -1;
max = max -1;
// respect max column count 256 (0 to 255) for BIFF
if (max > 255) max = 255;
// in BIFF file format set hidden property in COLINFO record
HSSFSheet hssfSheet= (HSSFSheet)sheet;
java.lang.reflect.Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
_sheet.setAccessible(true);
InternalSheet internalSheet = (InternalSheet)_sheet.get(hssfSheet);
java.lang.reflect.Field _records = InternalSheet.class.getDeclaredField("_records");
_records.setAccessible(true);
#SuppressWarnings("unchecked")
List<RecordBase> records = (List<RecordBase>)_records.get(internalSheet);
boolean colInfoFound = false;
for (RecordBase record : records) {
if (record instanceof ColumnInfoRecord) {
ColumnInfoRecord columnInfoRecord = (ColumnInfoRecord)record;
if (columnInfoRecord.getFirstColumn() == min && columnInfoRecord.getLastColumn() == max) {
colInfoFound = true;
columnInfoRecord.setHidden(true);
}
System.out.println(columnInfoRecord);
}
}
if (!colInfoFound) {
ColumnInfoRecord columnInfoRecord = new ColumnInfoRecord();
columnInfoRecord.setFirstColumn(min);
columnInfoRecord.setLastColumn(max);
columnInfoRecord.setHidden(true);
records.add(records.size()-1, columnInfoRecord);
System.out.println(columnInfoRecord);
}
}
}
public static void main(String[] args) throws Exception {
String inFilePath = "./ExcelExampleIn.xlsx"; String outFilePath = "./ExcelExampleOut.xlsx";
//String inFilePath = "./ExcelExampleIn.xls"; String outFilePath = "./ExcelExampleOut.xls";
try (Workbook workbook = WorkbookFactory.create(new FileInputStream(inFilePath));
FileOutputStream out = new FileOutputStream(outFilePath ) ) {
Sheet sheet = workbook.getSheetAt(0);
//set unused rows hidden
setUnusedRowsHidden(sheet);
//set multiple columns hidden, here column 7 (G) to last column 16384 (XFD)
setColumnsHidden(sheet, 7, 16384);
workbook.write(out);
}
}
}
Mark the first "outside" column, hold CTRL + SHIFT and then right arrow. Then, all columns should be highlighted. Right click, select "Hide".
Repeat the same with rows, select the first row outside of your data, hold CTRL + SHIFT and press Arrow Down.
Best of luck! ^_^
I have used ClosedXML to create an excel file with 3 worksheets from AX 2012 R3.
I'm wondering if it is possible to use ClosedXML to read the excel file back in to AX?
Yes, that is possible.
Here is the sample code:
ClosedXML.Excel.XLWorkbook workbook;
ClosedXML.Excel.IXLWorksheet worksheet;
ClosedXML.Excel.IXLCells cellsUsed;
ClosedXML.Excel.IXLCell cell;
System.Collections.IEnumerator enumerator;
str value;
;
try
{
workbook = new ClosedXML.Excel.XLWorkbook(#"YourTestFile.xlsx");
worksheet = workbook.Worksheet("SomeEpicSheetName");
cellsUsed = worksheet.CellsUsed();
enumerator = cellsUsed.GetEnumerator();
while (enumerator.MoveNext())
{
cell = enumerator.get_Current();
value = System.Convert::ToString(cell.get_Value());
info(value);
}
}
catch
{
error(AifUtil::getClrErrorMessage());
}
This code creates a message in the InfoLog for each cell filled with a value. You can use it as a start in any new job after changing the used path and sheet name.
This is my example sheet:
The output:
I need to generate csv files and I stumbled on a module in our project itself which uses Apache POI to generate excel sheets aleady. So I thought I could use the same to generate csv. So I asked google brother, but he couldnt find anything for sure that says Apache POI can be used for CSV file generation. I was checking on the following api too and it only talks about xls sheets and not csv anywhere. Any ideas?
http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/Workbook.html
Apache Poi will not output to CSV for you. However, you have a couple good options, depending on what kind of data you are writing into the csv.
If you know that none of your cells will contain csv markers such as commas, quotes, line endings, then you can loop through your data rows and copy the text into a StringBuffer and send that to regular java IO.
Here is an example of writing an sql query to csv along those lines: Poi Mailing List: writing CSV
Otherwise, rather than figure out how to escape the special characters yourself, you should check out the opencsv project
If you check official web site Apache POI, you can find lots of example there. There is also an example that shows how you can have csv formatted output by using apache POI.
ToCSV example
Basic strategy:
1) Apache Commons CSV is the standard library for writing CSV values.
2) But we need to loop through the Workbook ourselves, and then call Commons CSV's Printer on each cell value, with a newline at the end of each row. Unfortunately this is custom code, it's not automatically available in XSSF. But it's easy:
// In this example we construct CSVPrinter on a File, can also do an OutputStream
Reader reader = Files.newBufferedReader(Paths.get(SAMPLE_CSV_FILE_PATH));
CSVPrinter csvPrinter = new CSVPrinter(reader, CSVFormat.DEFAULT);
if (workbook != null) {
XSSFSheet sheet = workbook.getSheetAt(0); // Sheet #0
Iterator<Row> rowIterator = sheet.rowIterator();
while (rowIterator.hasNext()) {
Row row = rowIterator.next();
Iterator<Cell> cellIterator = row.cellIterator();
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
csvPrinter.print(cell.getStringCellValue()); // Call Commons CSV here to print
}
// Newline after each row
csvPrinter.println();
}
}
// at the end, close and flush CSVPrinter
csvPrinter.flush();
csvPrinter.close();
An improved and tested version of gene b's response is this:
/**
* Saves all rows from a single Excel sheet in a workbook to a CSV file.
*
* #param excelWorkbook path to the Excel workbook.
* #param sheetNumber sheet number to export.
* #param csvFile CSV file path for output.
* #throws IOException if failed to read the Excel file or create/write to a CSV file.
*/
public static void excelToCsv(String excelWorkbook, int sheetNumber, String csvFile) throws IOException {
try (Workbook workbook = WorkbookFactory.create(new File(excelWorkbook), null, true); // Read-only: true
BufferedWriter writer = new BufferedWriter(new FileWriter(csvFile));
CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT)) {
Sheet sheet = workbook.getSheetAt(sheetNumber);
DataFormatter format = new DataFormatter();
for (Row row : sheet) {
for (int c = 0; c < row.getLastCellNum(); c++) {
// Null cells returned as blank
Cell cell = row.getCell(c, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
String cellValue = format.formatCellValue(cell);
csvPrinter.print(cellValue);
}
csvPrinter.println();
}
csvPrinter.flush();
}
}
The following improvements were made:
NullPointerException won't be thrown if a cell in an Excel Row was never edited. A blank value will be written to the CSV instead.
Excel values are rendered using DataFormatter allowing the CSV to match the visual representation of the Excel sheet.
try-with-source used for auto-close of the file objects.
The workbook is opened in the read-only mode.
I've been looking on the web for 30 minutes now and can't find any explanation about that. Here is my problem :
I wrote an application with poi to parse some data from 200 excel files or so and put some of it into a new file. I do some cell evaluation with FormulaEvaluator to know the content of the cells before choosing to keep them or not.
Now, when i test it on a test file with only values in the cells, the program works perfectly but when i use it on my pile of files I get this error :
"could not resolve external workbook name"
Is there any way to ignore external workbook references or set up the environment so that it wont evaluate formula with external references?
Because the ones I need don't contain references...
Thank you
Can you not just catch the error, and skip over that cell?
You're getting the error because you've asked POI to evaluate a the formula in a cell, and that formula refers to a different file. However, you've not told POI where to find the file that's referenced, so it objects.
If you don't care about cells with external references, just catch the exception and move on to the next cell.
If you do care, you'll need to tell POI where to find your files. You do this with the setupEnvironment(String[],Evaluator[]) method - pass it an array of workbook names, and a matching array of evaluators for those workbooks.
In order for POI to be able to evaluate external references, it needs access to the workbooks in question. As these don't necessarily have the same names on your system as in the workbook, you need to give POI a map of external references to open workbooks, through the setupReferencedWorkbooks(java.util.Map<java.lang.String,FormulaEvaluator> workbooks) method.
I have done please see below code that is working fine at my side
public static void writeWithExternalReference(String cellContent, boolean isRowUpdate, boolean isFormula)
{
try
{
File yourFile = new File("E:\\Book1.xlsx");
yourFile.createNewFile();
FileInputStream myxls = null;
myxls = new FileInputStream(yourFile);
XSSFWorkbook workbook = new XSSFWorkbook(myxls);
FormulaEvaluator mainWorkbookEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
XSSFWorkbook workbook1 = new XSSFWorkbook(new File("E:\\elk\\lookup.xlsx"));
// Track the workbook references
Map<String,FormulaEvaluator> workbooks = new HashMap<String, FormulaEvaluator>();
workbooks.put("Book1.xlsx", mainWorkbookEvaluator);
workbooks.put("elk/lookup.xlsx", workbook1.getCreationHelper().createFormulaEvaluator());
workbook2.getCreationHelper().createFormulaEvaluator());
// Attach them
mainWorkbookEvaluator.setupReferencedWorkbooks(workbooks);
XSSFSheet worksheet = workbook.getSheetAt(0);
XSSFRow row = null;
if (isRowUpdate) {
int lastRow = worksheet.getLastRowNum();
row = worksheet.createRow(++lastRow);
}
else {
row = worksheet.getRow(worksheet.getLastRowNum());
}
if (!isFormula) {
Cell cell = row.createCell(row.getLastCellNum()==-1 ? 0 : row.getLastCellNum());
cell.setCellValue(Double.parseDouble(cellContent));
} else {
XSSFCell cell = row.createCell(row.getLastCellNum()==-1 ? 0 : row.getLastCellNum());
System.out.println(cellContent);
cell.setCellFormula(cellContent);
mainWorkbookEvaluator.evaluateInCell(cell);
cell.setCellFormula(cellContent);
// mainWorkbookEvaluator.evaluateInCell(cell);
//System.out.println(cell.getCellFormula() + " = "+cell.getStringCellValue());
}
workbook1.close();
myxls.close();
FileOutputStream output_file =new FileOutputStream(yourFile,false);
//write changes
workbook.write(output_file);
output_file.close();
} catch (Exception e) {
e.printStackTrace();
}
}