Can't retrieve value of a computed cell using JExcelAPI - java

This is my first post, please bear with me. I'm trying to do something simple: look at the value of a spreadsheet cell that contains a formula. Nothing seems to work, instead of the correct answer (579) , I get "4". (output below). This program creates a simple spreadsheet with numbers in A1 and A2, and the sum in A3.
import java.io.*;
import jxl.*;
import jxl.Workbook;
import jxl.biff.FormulaData;
import jxl.biff.formula.FormulaException;
import jxl.write.Number;
import jxl.write.*;
import jxl.write.Formula;
import jxl.read.biff.CellValue;
public class ReadCell {
public static void main(String[] args) throws IOException,
jxl.read.biff.BiffException,
WriteException, FormulaException
{
String filename = "readcell.xls";
WritableWorkbook workbook = Workbook.createWorkbook(new File(filename));
WritableSheet ws = workbook.createSheet("Sheet1", 0);
Number n = new Number(0,0,123); // 123 in A1
ws.addCell(n);
n = new Number(0,1,456); // 456 in A2
ws.addCell(n);
Formula f = new Formula(0,2, "A1+A2"); // formula in A3, yields 579
ws.addCell(f);
workbook.write();
workbook.close();
//now try to retrieve the sum of A1 + A2, which is in A3 (value should be 579)
Workbook wb = Workbook.getWorkbook( new File (filename));
Sheet sheet = wb.getSheet(0);
Cell cell = sheet.getCell("A3");
System.out.println("\n\nCell A3 contents = " + cell.getContents());
//that didn't work, maybe try it by casting to a NumberCell?
NumberCell numbercell = (NumberCell)cell;
System.out.println("NumberCell value = " + numbercell.getValue());
System.out.println("NumberCell contents = " + numbercell.getContents());
//that didn't work, maybe try it by casting to a FormulaCell?
FormulaCell fc = (FormulaCell)cell;
System.out.println("FormulaCell contents = " + fc.getContents());
System.out.println("FormulaCell formula = " + fc.getFormula());
//that didn't work, maybe try to get contents by casting to FormulaData?
FormulaData fd = (FormulaData)cell;
System.out.println("FormulaData contents = " + fd.getContents());
System.out.println("FormulaData type = " + fd.getType());
//that didn't work, maybe try to get contents by casting to NumberFormulaCell?
NumberFormulaCell nfc = (NumberFormulaCell)cell;
System.out.println("NumberFormulaCell value = " + nfc.getValue());
System.out.println("NumberFormulaCell contents = " + nfc.getContents());
System.out.println("NumberFormulaCell formula = " + nfc.getFormula());
//that didn't work, try to get contents by casting to CellValue?
CellValue cv = (CellValue)cell;
System.out.println("cellValue contents = " + cv.getContents());
//Nothing works and these casts won't compile:
//Number nnn = (Number)cell;
//NumberFormulaRecord nfr = (NumberFormulaRecord)cell;
wb.close();
}
}
Here is the output, the "4"s should be "579"s
Cell A3 contents = 4
NumberCell value = 4.0
NumberCell contents = 4
FormulaCell contents = 4
FormulaCell formula = A1+A2
FormulaData contents = 4
FormulaData type = Numerical Formula
NumberFormulaCell value = 4.0
NumberFormulaCell contents = 4
NumberFormulaCell formula = A1+A2
cellValue contents = 4
Thanks for any help you can provide!

I ran the code that writes the workbook. Then I opened the workbook in Excel. When I told it to close, I was prompted with a message:"Microsoft Excel recalculates formulas when opening files last saved by an earlier version of Excel". After choosing to save, and then running the code that reads (but not the code that writes the workbook) I got the correct cell value of 579. When I deleted the file and ran all the code you posted, I got 4. It looks like the formula needs to be calculated before it can be read. This post seems to say there's no fixing it, and makes a supposition as to why: Updated excel file still returns old values

Related

How to read .xlsx file with apache poi?

See the picture below, I am trying to write a program that can "scan" a given row with no limit of from which cell to which cell, then, find all the "strings" that are identical the same. Is it possible to do that? Thank you.
To give an example so that this will not be very confusing, for ex.: On row H, you see there are customer's names, there are "Coresystem", "Huawei", "VIVO", etc... Now the problem is, what if the names are not grouped together, they are all split up, like, On H5, it will be "Huawei" and On H9, it will be "VIVO", etc, it's like, unlike the picture provided below, on row H all the names are split up, and I want apache POI to find all the customers that have the same name, for ex.: If user enter "coReSysteM", it should be able to find all the .equalsIgnoreCase of all Coresystem on row H (btw, the user should be able to enter the customer's name that they want to enter and the row they want to search for), and display from A5, B5, C5, D5, E5, F5, G5, H5 to A14, B14, C14, D14, E14, F14, G14, H5, is it possible?
I was thinking about setting a formula to find all the customer, for example: =CountIf
These are the code that I am currently trying to do, but then I am stuck with it:
package excel_reader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Scanner;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ExcelReader2d {
ExcelReader link = new ExcelReader();
Scanner scan = new Scanner(System.in);
// instance data
private static int numberGrid[][] = null;
private static String stringGrid[][] = null;
// constructor
public ExcelReader2d(String desLink) {
}
// methods
public void ExeScan() throws FileNotFoundException, IOException {
Scanner scan = new Scanner(System.in);
XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream("C:\\Users\\Sonic\\Desktop\\20191223 IMPORTS.xlsx"));
XSSFSheet sheet = workbook.getSheetAt(0);
final int rowStart = Math.min(15, sheet.getFirstRowNum()), rowEnd = Math.max(1400, sheet.getLastRowNum());
System.out.print("Enter the rows that you want to search for: (for ex. the rows that stores customer's name) ");
int searchRows = scan.nextInt();
System.out.print("Enter the customer's name that you are looking for: ");
String name = scan.nextLine();
//int rowNum;
// Search given row
XSSFRow row = sheet.getRow(searchRows);
try {
for (int j = 4; j < rowEnd; j++) {
Row r = sheet.getRow(j);
if (name.equalsIgnoreCase(name)) {
row.getCell(j).getStringCellValue();
}
// skip to next iterate if that specific cell is empty
if (r == null)
continue;
}
} catch (Exception e){
System.out.println("Something went wrong.");
}
}
}
ps. I know that this will be very confusing, but please feel free to ask for any kind of questions to help you get rid of the confusion and help me either because this has been a problem for me. Thank you very much and I will super appreciated for your help. Currently using apache poi, vscode, java.
I would iterate over the rows in the sheet and get the string content of cell 7 (H) from each row. If that string fulfills the requirement equalsIgnoreCase the searched value, that row is one of the result rows, else not.
One could collect the result rows in a List<Row>. Then this List contains the result rows after that.
Example:
ExcelWorkbook.xlsx:
Code:
import org.apache.poi.ss.usermodel.*;
import java.util.List;
import java.util.ArrayList;
import java.io.FileInputStream;
public class ExcelReadRowsByColumnValue {
public static void main(String[] args) throws Exception {
String filePath = "./ExcelWorkbook.xlsx";
String toSearch = "coresystem";
int searchColumn = 7; // column H
List<Row> results = new ArrayList<Row>();
DataFormatter dataFormatter = new DataFormatter();
Workbook workbook = WorkbookFactory.create(new FileInputStream(filePath));
FormulaEvaluator formulaEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) { // iterate over all rows in the sheet
Cell cellInSearchColumn = row.getCell(searchColumn); // get the cell in seach column (H)
if (cellInSearchColumn != null) { // if that cell is present
String cellValue = dataFormatter.formatCellValue(cellInSearchColumn, formulaEvaluator); // get string cell value
if (toSearch.equalsIgnoreCase(cellValue)) { // if cell value equals the searched value
results.add(row); // add that row to the results
}
}
}
// print the results
System.out.println("Found results:");
for (Row row : results) {
int rowNumber = row.getRowNum()+1;
System.out.print("Row " + rowNumber + ":\t");
for (Cell cell : row) {
String cellValue = dataFormatter.formatCellValue(cell, formulaEvaluator);
System.out.print(cellValue + "\t");
}
System.out.println();
}
workbook.close();
}
}
Result:

Library to evaluate excel/other language expression evaluation in java without using excel

I have been searching the internet for a library which can evaluate excel formula-like expressions in java The formula is not embedded in the excel.
Let's say, the values are not in excel either Let's say 3 variables a, b, c I want an evaluator in java which given a string like CONCAT(a,b,c) written in excel expression or in some other language, can give me the output.
I have found some expression evaluator libraries for java online, but there I need to define my own expression language, is there something which directly uses excel language or some other language?
I have looked at apache POI and jxls also looked at jsp.el as far as I have understood poi and jxls do the evaluations from formulas already embedded in the excel file or we need to create a spreadsheet to actually run the formula which imo will be heavier than running the formula in java. For jsp.el I'll have to define my own expression language, which is not robust enough.
It is correct that apache poi's WorkbookEvaluator needs a workbook. And since you are talking about evaluating "excel formula-like expressions", this is necessary because all variables in such formulas must either be cell references or names in that workbook. Your given example CONCATENATE(a,b,c) can only work as an Excel formula when a, b, and c are Excel names . Else it would lead to #Name? error in Excel. And btw.: The Excel function is CONCATENATE and not CONCAT.
But this workbook must not necessarily stored somewhere. It can be only in random access memory too.
And the formulas itself need not to be in the sheet somewhere. The formula can also be given as a string since there is WorkbookEvaluator.evaluate(java.lang.String formula, CellReference ref).
Example:
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.formula.BaseFormulaEvaluator;
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.eval.*;
import org.apache.poi.ss.util.CellReference;
public class EvaluateExcelFunctions {
static Object evaluateExcelFormula(String formula, Workbook workbookWithVariables) {
if (workbookWithVariables.getNumberOfSheets() < 1) workbookWithVariables.createSheet();
CellReference reference = new CellReference(workbookWithVariables.getSheetName(0), 0 , 0, false, false);
CreationHelper helper = workbookWithVariables.getCreationHelper();
FormulaEvaluator formulaevaluator = helper.createFormulaEvaluator();
WorkbookEvaluator workbookevaluator = ((BaseFormulaEvaluator)formulaevaluator)._getWorkbookEvaluator();
ValueEval valueeval = null;
try {
valueeval = workbookevaluator.evaluate(formula, reference);
} catch (Exception ex) {
return ex.toString();
}
if (valueeval instanceof StringValueEval) {
String result = ((StringValueEval)valueeval).getStringValue();
return result;
} else if (valueeval instanceof NumericValueEval) {
double result = ((NumericValueEval)valueeval).getNumberValue();
return result;
} else if (valueeval instanceof ErrorEval) {
String result = ((ErrorEval)valueeval).getErrorString();
return result;
}
return null;
}
public static void main(String[] args) throws Exception {
Workbook workbook =
//new XSSFWorkbook();
new HSSFWorkbook();
Name name;
String formula;
Object result;
// example 1 concatenating strings - your example
name = workbook.createName();
name.setNameName("_a");
name.setRefersToFormula("\"Text A \"");
name = workbook.createName();
name.setNameName("_b");
name.setRefersToFormula("\"Text B \"");
name = workbook.createName();
name.setNameName("_c");
name.setRefersToFormula("\"Text C \"");
formula = "CONCATENATE(_a, _b, _c)";
result = evaluateExcelFormula(formula, workbook);
System.out.println(result);
// example 2 Pythagorean theorem
name = workbook.getName("_a");
name.setRefersToFormula("12.34");
name = workbook.getName("_b");
name.setRefersToFormula("56.78");
formula = "SQRT(_a^2 + _b^2)";
result = evaluateExcelFormula(formula, workbook);
System.out.println(result);
// example 3 complex math formula
name = workbook.getName("_a");
name.setRefersToFormula("12.34");
name = workbook.getName("_b");
name.setRefersToFormula("56.78");
name = workbook.getName("_c");
name.setRefersToFormula("90.12");
formula = "((_a+_b+_c)*_c/_b-_a)/2";
result = evaluateExcelFormula(formula, workbook);
System.out.println(result);
// example 4 faulty formulas
name = workbook.getName("_a");
name.setRefersToFormula("56.78");
name = workbook.getName("_b");
name.setRefersToFormula("190.12");
name = workbook.getName("_c");
name.setRefersToFormula("\"text\"");
formula = "_a + _c";
result = evaluateExcelFormula(formula, workbook);
System.out.println(result);
formula = "((_a + _b";
result = evaluateExcelFormula(formula, workbook);
System.out.println(result);
formula = "_a \\ 2";
result = evaluateExcelFormula(formula, workbook);
System.out.println(result);
formula = "_a^_b";
result = evaluateExcelFormula(formula, workbook);
System.out.println(result);
formula = "_a/(_b-_b)";
result = evaluateExcelFormula(formula, workbook);
System.out.println(result);
formula = "CONCAT(_a, _b)";
result = evaluateExcelFormula(formula, workbook);
System.out.println(result);
workbook.close();
}
}
This code is tested using apache poi 4.1.0.
Note, Excel names cannot be all possible variable names. For example a Excel name cannot be c or C because this would be in conflict with possible R1C1 cell references. That's why I have named my names _a, _b and _c.
To use IF function, I had to create a Cell instance to prevent a NullPointerException:
Cell cell = workbookWithVariables.getSheet(workbookWithVariables.getSheetName(0)).createRow(0).createCell(0);
CellReference reference = new CellReference(cell);

How to get int from string involving other characters?

I'm writing a program where I want to iterate certain attributes inside a document with an ID that looks like this:
"id": "competitor:2672"
I want to iterate based on data that I read from an excel file. The only problem is that, in said excel file, the ID is only given as
2672
in the column "Competitor ID".
I cannot parse the given String to integer. What is the best and cleanest way to compare the two IDs
Using apache POI I want to do something like this
String COLUMN_ID = "G" // Column letter in which the IDs are stored
Document home = competitors.get(0);
Document away = competitors.get(1);
String homeIDString = home.get("id").toString();
int homeID = //how to get this from the upper string?
String awayIDString = away.get("id").toString();
int awayID = //how to get this from the upper string?
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
XSSFSheet sheet = workbook.getSheetAt(0);
for (int row=0; <something>; row++) {
XSSFCell cell = sheet.getRow(row).getCell(CellReference.convertColStringToIndex(COLUMN_ID));
if (cell.getCellTypeEnum().equals(NUMERIC)) int cellValue = (int) cell.getNumericValue();
if (cellValue == homeID) { do something }
else if (cellValue == awayID) { do something }
}
You are currently getting a NumberFormatException because you are trying to convert your String into an int before comparing it with another int.
What #azurefrog is saying is that you can try instead to convert your int into a String (the other way around) and it will be fine.
strVariable.endsWith(String.valueOf(intVariable))
However this has the problem that "id": "competitor:2672" and 72 would return true too.
A better way is to just remove competitor: using substring before converting 2672 to an int
String myInput = "competitor:2672"; // "competitor:2672"
myInput = myInput.substring(11); // "2672"
int myValue = Integer.parseInt(myInput); // 2672

perform operations on excel data sheet using java

there are four columns in excel sheet
I need to perform operations on three columns and display the result on the fourth one.
Image with data in excel
If I perform B9-D9 then the result is equal to C9.
when this happens the output should be as "matched".
i need to know how to access each row and column and perform the necessary operation on it.
See if you can help me and let me know if any additional details are required.
package com.infy;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Iterator;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ReconMatch {
public static void main(String[] args) throws IOException,
FileNotFoundException{
// TODO Auto-generated method stub
String excelFilePath ="C:/Users/akshay.kuchankar/Documents/demo.xlsx";
FileInputStream inputStream = new FileInputStream(new
File(excelFilePath));
Workbook workbook = new XSSFWorkbook(inputStream);
Sheet firstSheet = workbook.getSheetAt(0);
Iterator<Row> iterator = firstSheet.iterator();
while (iterator.hasNext()) {
Row nextRow = iterator.next();
Iterator<Cell> cellIterator = nextRow.cellIterator();
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
//what should be the basic approach or the syntax to perform the operaiton??
}
System.out.println();
}
workbook.close();
inputStream.close();
}
}
for(int i= 0; i<firstSheet.getRow(0).getCell(0).getNumericCellValue(); i++)
{
FGAmount = firstSheet.getRow(1).getCell(1).getNumericCellValue();
// System.out.println(FGAmount);
difference = firstSheet.getRow(1).getCell(3).getNumericCellValue();
value = FGAmount + difference;
}
alconAmount = firstSheet.getRow(1).getCell(2).getNumericCellValue();
// result = firstSheet.getRow(1).getCell(4).getStringCellValue();
}
}
try {
if(value== alconAmount){
firstSheet.getRow(1).getCell(4).setCellValue("Manual Matched");
System.out.println("matched");
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(e);
}
// System.out.println(result);
workbook.close();
inputStream.close();
Take a look at the API for your library:
https://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/Cell.html
You can interact with the cell in a number of ways, for example:
cell.setCellValue("My new value");
As for how to get values form cells and check against them you can do something a bit like this example where we do a bit of math and compare the values:
//Value of row 9, column 1 (column B)
String B9 = firstSheet.getRow(9).getCell(1).getStringCellValue();
//Value of row 9, column 2 (column C)
String D9 = firstSheet.getRow(9).getCell(2).getStringCellValue();
//Value of row 9, column 3 (column D)
String D9 = firstSheet.getRow(9).getCell(3).getStringCellValue();
//do math B9 - D9
double value = Double.parseDouble(B9) - Double.parseDouble(D9);
//check if C9 matches the result of B9 - D9
if(value == Double.parseDouble(C9))
{
//if it matches set cell E9 to display "Matched" and print out a message
firstSheet.getRow(9).getCell(4).setCellValue("matched");
System.out.println("matched");
}
I have not tested this code, but it should point you in the right direction.
Obviously there are things you should do like putting this in a loop rather than hard coding values, and you should check the column name to the make sure you have the right column before getting your values, and then you should check that the cell is a number and not text etc.
Edit in reply to your comment. Here is some code that will go over every row in a sheet except row 1 and will do exactly the same thing as in my example above and write "matched" in column E if column B - column D is equal to column C:
Iterator<Row> iterator = firstSheet.iterator();
while (iterator.hasNext()) {
Row nextRow = iterator.next();
//Check to make sure we skip the first row because that has all the column names:
if (nextRow.getRowNum() != 0){
//Get cell values of the current row
String columnB = nextRow.getCell(1).getStringCellValue();
String columnC = nextRow.getCell(2).getStringCellValue();
String columnD = nextRow.getCell(3).getStringCellValue();
try{
//do math column B - column D
double value = Double.parseDouble(columnB) - Double.parseDouble(columnD);
//check if column C matches the result of (column B - column D)
if(value == Double.parseDouble(columnC)){
//if it matches set text in column E to "matched"
nextRow.getCell(4).setCellValue("matched");
//print to console showing if a row matched
System.out.println("Row " + (nextRow.getRowNum()+1) + " matched");
}
}
catch(NumberFormatException nfe){
//do nothing here, this will happen if a cell contains text instead of numbers
}
catch(NullPointerException npe){
//Something else happened, you can probably ignore this as well but it will pay to throw a stack trace just in case something is wrong with this code
npe.printStackTrace();
}
}
}

detecting and replacing variables in a string by values

I have an excel sheet whose first column contains following data "What is ${v1} % of ${v2}?", two more columns (v1 and v2) in this sheet contains {"type":"int", "minimum":15, "maximum":58} and {"type":"int", "minimum":30, "maximum":100}, these are the ranges of variable v1 and v2. I need to replace v1 and v2 in the expression with a random value from the given range and store the expression in another spread sheet using JAVA. How can I do this by making use of JETT?
For example: I should store "What is 25% of 50?"
This is what I have done,I am able to read the column in my java program but not replace the values
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
public class ACGS {
public static void main(String[] args) throws Exception {
//test file is located in your project path
FileInputStream fileIn = new FileInputStream("C://users/user/Desktop/Content.xls");
//read file
POIFSFileSystem fs = new POIFSFileSystem(fileIn);
HSSFWorkbook filename = new HSSFWorkbook(fs);
//open sheet 0 which is first sheet of your worksheet
HSSFSheet sheet = filename.getSheetAt(0);
//we will search for column index containing string "Your Column Name" in the row 0 (which is first row of a worksheet
String columnWanted = "${v1}";
Integer columnNo = null;
//output all not null values to the list
List<Cell> cells = new ArrayList<Cell>();
Row firstRow = sheet.getRow(0);
for(Cell cell:firstRow){
if (cell.getStringCellValue().contains(columnWanted)){
columnNo = cell.getColumnIndex();
System.out.println("cell contains "+cell.getStringCellValue());
}
}
if (columnNo != null){
for (Row row : sheet) {
Cell c = row.getCell(columnNo);
if (c == null || c.getCellType() == Cell.CELL_TYPE_BLANK) {
// Nothing in the cell in this row, skip it
} else {
cells.add(c);
}
}
} else{
System.out.println("could not find column " + columnWanted + " in first row of " + fileIn.toString());
}
}
}
First, it looks like you aren't using JETT at all. You appear to be attempting to read the spreadsheet yourself and do some processing.
Here is how you would do this in JETT. JETT doesn't provide its own random number support, but together with its Apache Commons JEXL expression support, and Java's own Random, you can publish the expected ranges of your random variables as beans to JETT, and you can calculate a random variable with an expression.
First, create your template spreadsheet, populating it with expressions (between ${ and }) that JETT will evaluate. One cell might contain something like this.
What is ${rnd.nextInt(v1Max - v1Min + 1) + v1Min}% of ${rnd.nextInt(v2Max - v2Min + 1) + v2Min}?
Next, create beans to be supplied to JETT. These beans are the named objects that are available to JEXL expressions in your spreadsheet template.
Map<String, Object> beans = new HashMap<String, Object>();
beans.put("v1Min", 15);
beans.put("v1Max", 58);
beans.put("v2Min", 30);
beans.put("v2Max", 100);
beans.put("rnd", new Random());
Next, create your code that invokes the JETT ExcelTransformer.
try
{
ExcelTransformer transformer = new ExcelTransformer();
// template file name, destination file name, beans
transformer.transform("Content.xls", "Populated.xls", beans);
}
catch (IOException e)
{
System.err.println("IOException caught: " + e.getMessage());
}
catch (InvalidFormatException e)
{
System.err.println("InvalidFormatException caught: " + e.getMessage());
}
In the resultant spreadsheet, you will see the expressions evaluated. In the cell that contained the expressions above, you will see for example:
What is 41% of 38?
(Or you will see different numbers, depending on the random numbers generated.)

Categories