Leer una hoja de cálculo es fácil hasta que te das cuenta de dos cosas: en Excel todo número es un decimal y las celdas vacías no existen. Las dos tienen solución, pero hay que conocerlas.
También te puede interesar
La dependencia
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.5.1</version> </dependency>
Recorrer la hoja
import org.apache.poi.ss.usermodel.*;
import java.io.File;
import java.time.format.DateTimeFormatter;
public class LeerExcel {
public static void main(String[] args) throws Exception {
var f = DateTimeFormatter.ofPattern("dd/MM/yyyy");
try (Workbook libro = WorkbookFactory.create(new File("ventas.xlsx"))) {
Sheet hoja = libro.getSheetAt(0);
System.out.println("hoja \"" + hoja.getSheetName()
+ "\", última fila: " + hoja.getLastRowNum());
FormulaEvaluator ev = libro.getCreationHelper().createFormulaEvaluator();
for (Row fila : hoja) {
var sb = new StringBuilder();
for (Cell cel : fila) sb.append(valor(cel, ev, f)).append(" | ");
System.out.println(" " + sb);
}
}
}
static String valor(Cell c, FormulaEvaluator ev, DateTimeFormatter f) {
return switch (c.getCellType()) {
case STRING -> c.getStringCellValue();
case BOOLEAN -> String.valueOf(c.getBooleanCellValue());
case NUMERIC -> DateUtil.isCellDateFormatted(c)
? c.getLocalDateTimeCellValue().toLocalDate().format(f)
: String.valueOf(c.getNumericCellValue());
case FORMULA -> c.getCellFormula() + " = " + ev.evaluate(c).getNumberValue();
case BLANK -> "";
default -> "?";
};
}
}salidahoja "Ventas", última fila: 4 Producto | Unidades | Precio | Importe | Fecha | Camiseta | 12.0 | 25.0 | B2*C2 = 300.0 | 01/10/2026 | Bufanda | 3.0 | 9.0 | B3*C3 = 27.0 | 02/10/2026 | Mochila | 1.0 | 34.0 | B4*C4 = 34.0 | 03/10/2026 | Total | SUM(D2:D4) = 361.0 |
Los cuatro detalles que importan
1. WorkbookFactory.create adivina el formato. Devuelve un XSSFWorkbook o un HSSFWorkbook según lo que encuentre, así que el mismo código lee .xlsx y .xls.
2. Todo número es un double. Fíjate en el 12.0: en la hoja escribimos un entero, pero en Excel no hay enteros. Para recuperarlo hay que convertir, y conviene hacerlo con cuidado si el número es grande: (int) celda.getNumericCellValue().
3. Una fecha es un número con formato. No hay tipo de fecha: por eso DateUtil.isCellDateFormatted, que mira el formato de la celda para decidir. Si alguien le quita el formato en Excel, tu programa lee un 46296.
4. Una fórmula no trae su resultado a menos que lo pidas. El FormulaEvaluator la calcula; getCellFormula() devuelve el texto. También puedes leer el último valor que Excel guardó con celda.getNumericCellValue() sobre una celda de fórmula.
for (Cell cel : fila) se salta las celdas que nunca se crearon, de modo que las columnas dejan de coincidir. Si necesitas las posiciones, recorre por índice:for (int c = 0; c < fila.getLastCellNum(); c++) {
Cell cel = fila.getCell(c, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
// aquí c siempre es la columna real
}Lo mismo pasa con las filas: hoja.getLastRowNum() cuenta hasta la última con datos, pero hoja.getRow(i) puede devolver null para una fila intermedia vacía.
ERROR Log4j API could not find a logging provider y sigue funcionando. Se quita añadiendo log4j-core o log4j-to-slf4j. Y poi-ooxml arrastra unas cuantas dependencias —xmlbeans, commons-compress, commons-collections4—: con Maven vienen solas, pero si compilas a mano hay que bajarlas todas.XSSF con SAX), que recorre el XML interno sin cargar la hoja en memoria. Es bastante más código, pero es la única forma de leer un libro de 200 MB.Más información en inglés: Read Excel file with Apache POI.
Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.
Muchas gracias amigo, me funcionó con
// Instancia workbook nos permite leer una hoja de excel
workbook = new HSSFWorkbook(inputStream);