I am reading an Excel sheet using POI's XSSF . The Excel sheet has thousands of rows of user information like user name, address, age, department etc.
I can read and write the Excel sheet successfully but i want to locate empty/null cell's address and want to print it as result in another sheet
example result what i want is:
Empty cell : C5
Empty cell : E7
Empty cell : H8
Thanks and appreciate for discussions and replies.
You need to check for both Null and Blank cells. Null cells are ones that have never been used, Blank ones are ones that have been used or styled in some way that Excel has decided to keep them around in the file
The easiest way to control this fetching is with a MissingCellPolicy. Your code can then be something like:
Row r = getRow(); // Logic here to get the row of interest
// Iterate over all cells in the row, up to at least the 10th column
int lastColumn = Math.max(r.getLastCellNum(), 10);
for (int cn = 0; cn < lastColumn; cn++) {
Cell c = r.getCell(cn, Row.RETURN_BLANK_AS_NULL);
if (c == null) {
// The spreadsheet is empty in this cell
} else {
// Do something useful with the cell's contents
// eg...
System.out.println("There is data in cell " + (new CellReference(c)).formatAsString());
}
}
You can find more on this in the POI docs on iterating over rows and cells
As far as I unterstood your question right, this should do the trick:
XSSFCell cell = (XSSFCell) row.getCell( index );
if( cell == null )
{
cell = (XSSFCell) row.createCell( index );
}
if( cell.getStringCellValue().trim().isEmpty() )
{
String cellRef = CellReference.convertNumToColString( cell.getColumnIndex() );
System.err.println( "Empty cell found: " + cellRef
+ Integer.toString( row.getRowNum() ) );
}
Related
Java 8 and Apache POI 4.1.x here. I need to:
Read in a template Excel file, that just has a single row of pre-styled header columns
Write a List<Fizz> (POJO list) to a new Excel file that uses that template
I do this because the template Excel has lots of complicated stylings, and so just using an existing one as a template input was much easier than me trying to manually generate the stylings myself via POI and CellUtil.
I have this working perfectly fine like so:
List<Fizz> fizzes = getSomehow();
InputStream inp = this.getClass().getClassLoader().getResource("my-template.xlsx").openStream();
Workbook workbook = WorkbookFactory.create(inp);
Sheet sheet = workbook.getSheetAt(0);
// the header is at row = 0 (0-based rows)
// so start writing the list on the row=1 (1st data/non-header row)
int rowNum = 1;
for (Fizz fizz : fizzes) {
Row nextRow = sheet.createRow(rowNum);
Cell itemNumber = nextRow.createCell(0);
itemNumber.setCellValue(fizz.getItemNumber());
Cell description = nextRow.createCell(1);
description.setCellValue(fizz.getDescription());
rowNum++;
}
// resize the columns appropriately
for (int c = 0; c < 2; c++) {
sheet.autoSizeColumn(c);
}
// export to file system
FileOutputStream fos = new FileOutputStream("some-output.xlsx");
workbook.write(fos);
fos.close();
inp.close();
workbook.close();
Everything works great, with one exception: often the Fizz#description is pretty lengthy, and is sometimes hundreds of characters long. Because there is no use of text wrapping in my code, and because I use autoSizeColumn for all columns, POI is setting the Description column width to the length of the longest-description.
Instead, I now want to stop using autoSizeColumn (I think!) and instead:
Force the output file's (in our case, "some-output.xlsx") Description column width to be the same as the input template file's Description's column. So if the Description column on my-template.xlsx is, say, 54, then I want some-output.xlsx's Description column to also be 54.
If the current row's Fizz description is too long for that width, I want to apply text wrapping so that the column stays fixed (again, in this case, 54) but that the description fits inside of it.
I would like to do this for every column, and ideally, I would like the code to detect what the width is for each column on the template. I say that because I would like to be able to open the template in Excel, change its width manually, save it, then re-run my code and have it pick up on that width change.
Any ideas on what I can do to accomplish this?
First approach
Set wrap text cell style for whole description column (column B) in your template using Excel's GUI. Then do using following getPreferredCellStyle method to get that column cell style and set it as the preferred cell style for each cell in description column (column B).
CellStyle getPreferredCellStyle(Cell cell) {
// a method to get the preferred cell style for a cell
// this is either the already applied cell style
// or if that not present, then the row style (default cell style for this row)
// or if that not present, then the column style (default cell style for this column)
CellStyle cellStyle = cell.getCellStyle();
// if no explicit cell style applied then cellStyle.getIndex() is 0 for XSSF
// or 15 (0xF = the index to the default ExtendedFormatRecord (0xF)) for HSSF
if ((cell instanceof XSSFCell && cellStyle.getIndex() == 0) || (cell instanceof HSSFCell && cellStyle.getIndex() == 15)) cellStyle = cell.getRow().getRowStyle();
if (cellStyle == null) cellStyle = cell.getSheet().getColumnStyle(cell.getColumnIndex());
if (cellStyle == null) cellStyle = cell.getCellStyle();
return cellStyle;
}
Then
...
Cell description = nextRow.createCell(1);
description.setCellValue(fizz.getDescription());
description.setCellStyle(getPreferredCellStyle(description));
...
Second approach
Do using CellUtil to set wrap text cell style for each cell in description column.
...
Cell description = nextRow.createCell(1);
description.setCellValue(fizz.getDescription());
CellUtil.setCellStyleProperty(description, CellUtil.WRAP_TEXT, true);
...
For both approaches
Do not set autoSizeColumn for the description column (column B). In your example, only autosize column 0 (A) but not column 1 (B):
...
sheet.autoSizeColumn(0);
...
So the column width of column B remains unchanged as width as it is in the template.
I am still new to Java trying only to loop over an excel column.
The Excel sheet:
I want to have in HashMap or Array so that I can compare these dates later on with another column.
My Code is giving me yet a string as you see in the screenshot:
How can I change this String to a another Data structure to access the "index" of the values. Something like dates.get(0) --> So 01-Jul-2018. I would change the time format later.
The Code section:
XSSFWorkbook workbook = new XSSFWorkbook(fsIP);
XSSFSheet sheet = workbook.getSheetAt(0);
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
Row row = sheet.getRow(rowIndex);
if (row != null) {
Cell cell = row.getCell(0); // getColumn(0)
if (cell != null) {
System.out.print(cell);
}
}
}
What would you like to know further from me?
I would be thankful for every help!
I am using poi-ooxml#3.17 to read and write excel file. I have added some styles/protection on some of cells. When i read the file i am not able to get cell styles applied to cells with no value as when i tries to access row/cell with empty value it returns null.
Below is code to write data in same excel file.
public static void writeDataToSheet(final Sheet sheet, final List<Map<String, Object>> sheetData) {
List<String> columns = getColumnNames(sheet);
LOGGER.debug("Inside XLSXHelper writeDataToSheet {}", Arrays.asList(columns));
IntStream.range(0, sheetData.size()).forEach((index) -> {
if (Objects.isNull(sheet.getRow(index + 1))) {
sheet.createRow(index + 1);
}
Row row = sheet.getRow(index + 1);
Map<String, Object> data = sheetData.get(index);
IntStream.range(0, columns.size()).forEach((colIndex) -> {
String column = columns.get(colIndex);
Cell cell = row.getCell(colIndex);
if (Objects.isNull(cell)) {
cell = row.createCell(colIndex);
}
cell.setCellValue(data.get(column) != null ? data.get(column).toString() : null);
});
});
}
Could anyone provide me a solution where i can read the styles applied to cell when cell is empty?
Thanks.
Cells without content or explicit style applied are not present in the sheet because of not to increase the file size unnecessarily. So apache poi returns null for such cells.
If you are looking at the sheet in spreadsheet application, then maybe it looks as if all cells in a row or all cells in a column have the same style applied to. But this is not the case. In real the row and/or the column has the style applied to. Only cells in intersection of styled rows and columns must be present in the sheet having the last applied style.
If a new cell needs to be created, then the spreadsheet application gets the preferred style for that cell. This is either the already applied cell style or if that not present, then the row style (default cell style for this row) or if that not present, then the column style (default cell style for this column). Unfortunately apache poi does not do so. So we need doing this ourself:
public CellStyle getPreferredCellStyle(Cell cell) {
// a method to get the preferred cell style for a cell
// this is either the already applied cell style
// or if that not present, then the row style (default cell style for this row)
// or if that not present, then the column style (default cell style for this column)
CellStyle cellStyle = cell.getCellStyle();
if (cellStyle.getIndex() == 0) cellStyle = cell.getRow().getRowStyle();
if (cellStyle == null) cellStyle = cell.getSheet().getColumnStyle(cell.getColumnIndex());
if (cellStyle == null) cellStyle = cell.getCellStyle();
return cellStyle;
}
This method may be used in code every time a new cell needs to be created:
...
if (Objects.isNull(cell)) {
cell = row.createCell(colIndex);
cell.setCellStyle(getPreferredCellStyle(cell));
}
...
I am using Apache POI to import data from excel file to database.(newbie to APACHE POI)
In which I am allowing user to select columns from excel sheet and Map those columns to the Database columns. After mapping the columns, when I try to insert the records from Excel to Database then:
If Columns with NO blank values in them are Mapped then Proper data is inserted into the database
If columns are Mapped with BLANK values in them, then if a Excel Cell has blank value then previous value of that column is assigned.
Source Code:
FileInputStream file = new FileInputStream(new File("C:/Temp.xls"));
HSSFWorkbook workbook = new HSSFWorkbook(file); //Get the workbook instance for XLS file
HSSFSheet sheet = workbook.getSheetAt(0); //Get first sheet from the workbook
Iterator<Row> rowIterator = sheet.iterator(); //Iterate through each rows from first sheet
while (rowIterator.hasNext())
{
HSSFRow hssfRow = (HSSFRow) rowIterator.next();
Iterator<Cell> iterator = hssfRow.cellIterator();
int current = 0, next = 1;
while (iterator.hasNext())
{
HSSFCell hssfCell = (HSSFCell) iterator.next();
current = hssfCell.getColumnIndex();
for(int i=0;i<arrIndex.length;i++) //arrayIndex is array of Excel cell Indexes selected by the user
{
if(arrIndex[i] == hssfCell.getColumnIndex())
{
if(current<next)
{
//System.out.println("Condition Satisfied");
}
else
{
System.out.println( "pstmt.setString("+next+",null);");
pstmt.setString(next,null);
next = next + 1;
}
System.out.println( "pstmt.setString("+next+","+((Object)hssfCell).toString()+");");
pstmt.setString(next,((Object)hssfCell).toString());
next = next + 1;
}
}
}
pstmt.addBatch();
}
I have look for similar questions on SO, but still not able to solve the issue.. So any help will be appreciated.
Thanks in advance..
You've made a very common mistake, which has been covered in rather a lot of past StackOverflow questions
As the Apache POI documentation on cell iterating says
In some cases, when iterating, you need full control over how missing or blank cells are treated, and you need to ensure you visit every cell and not just those defined in the file. (The CellIterator will only return the cells defined in the file, which is largely those with values or stylings, but it depends on Excel).
It sounds like you are in that situation, where you need to care about hitting every row/cell, and not just grabbing all the available cells without worrying about the gaps
You'll want to change you code to look somewhat like the example in the POI docs:
// Decide which rows to process
int rowStart = Math.min(15, sheet.getFirstRowNum());
int rowEnd = Math.max(1400, sheet.getLastRowNum());
for (int rowNum = rowStart; rowNum < rowEnd; rowNum++) {
Row r = sheet.getRow(rowNum);
int lastColumn = Math.max(r.getLastCellNum(), MY_MINIMUM_COLUMN_COUNT);
for (int cn = 0; cn < lastColumn; cn++) {
Cell c = r.getCell(cn, Row.RETURN_BLANK_AS_NULL);
if (c == null) {
// The spreadsheet is empty in this cell
// Mark it as blank in the database if needed
} else {
// Do something useful with the cell's contents
}
}
}
I attempted to find a solution already, but nothing has come up that matches my problem. I'm using JXL to read an excel spreadsheet and convert each row into a specified object. Each cell within a row corresponds to a property of the object I'm creating. My spreadsheet has 41 columns and after reading 375 rows, the number of cells per row changes from 41 to 32. I can't figure out why.
Here's the code where I'm looping through rows and retrieving the cells:
w = Workbook.getWorkbook(inputWorkbook);
// Get the first sheet
Sheet sheet = w.getSheet(0);
// Loop over first 10 column and lines
for (int row=1; row < sheet.getRows();row++)
{
EventData event = new EventData();
// we skip first row bc that should be header info
//now iterate through columns in row
try
{
Cell[] cell = sheet.getRow(row);
event.Name = cell[0].getContents();
event.Location = cell[1].getContents();
The rest of the code continues to grab the contents of each cell and assign them accordingly. But when attempting to access cell[32] on row 376, I get an out of bounds exception.
Could it not just be that everything after cell[32] on that row is empty and thus cell[32] (and up) in the array are not created at all?
Am now just starting with jxl and I think that's what I'm seeing
Instead of accessing the cell data as:
Cell[] cell = sheet.getRow(row);
event.Name = cell[0].getContents();
event.Location = cell[1].getContents();
Try to access the data as:
sheet.getCell(column, row);
Complete code would be:
w = Workbook.getWorkbook(inputWorkbook);
// Get the first sheet
Sheet sheet = w.getSheet(0);
// Loop over first 10 column and lines
for (int row=1; row < sheet.getRows();row++)
{
EventData event = new EventData();
// we skip first row bc that should be header info
//now iterate through columns in row
try
{
event.Name = sheet.getCell(0, row).getContents();
event.Location = sheet.getCell(1, row).getContents();
}
}