You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Apache POI处理.xlsx文件时遇“无法从字符串单元格获取数值”错误

解决Apache POI处理Excel时的"Cannot get a NUMERIC value from a STRING cell"错误

错误原因

你遇到的异常是因为Excel单元格的实际类型与调用的取值方法不匹配:比如sharePriceCell或shareBoughtCell中存在字符串类型的单元格(可能是单元格格式设为文本,或输入了非数字内容),但直接调用getNumericCellValue(),导致类型转换失败。

解决方案

需要根据单元格的实际类型,安全提取对应的值。可以编写通用工具方法处理不同类型的单元格,避免硬编码调用特定取值方法,同时优化线程逻辑提升健壮性。

修改后的完整代码

import java.io.FileInputStream;
import org.apache.poi.ss.usermodel.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DataSetTest {
    private static final String EXCEL_FILE_PATH = "C:/Users/assignment/src/dataset.xlsx";
    private static final int NUM_THREADS = 4;

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(NUM_THREADS);

        Map<String, Double> userSpendingMap = new HashMap<>();
        double totalMoneySpent = 0.0;

        try (InputStream fis = new FileInputStream(EXCEL_FILE_PATH);
             Workbook workbook = new XSSFWorkbook(fis)) {

            Sheet sheet = workbook.getSheetAt(0);

            // 跳过表头(根据你的Excel结构调整,无表头可删除这段)
            boolean isFirstRow = true;
            for (Row row : sheet) {
                if (isFirstRow) {
                    isFirstRow = false;
                    continue;
                }
                executorService.execute(() -> processTransaction(row, userSpendingMap));
            }

            executorService.shutdown();
            // 用awaitTermination替代循环sleep,更优雅高效
            executorService.awaitTermination(1, java.util.concurrent.TimeUnit.MINUTES);

            for (Double amount : userSpendingMap.values()) {
                totalMoneySpent += amount;
            }

            System.out.println("User ID\tTotal Money Spent");
            for (Map.Entry<String, Double> entry : userSpendingMap.entrySet()) {
                System.out.println(entry.getKey() + "\t" + entry.getValue());
            }

            System.out.println("Total money spent by all users: " + totalMoneySpent);

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void processTransaction(Row row, Map<String, Double> userSpendingMap) {
        Cell userIdCell = row.getCell(1);
        Cell sharePriceCell = row.getCell(2);
        Cell shareBoughtCell = row.getCell(3);

        if (userIdCell == null || sharePriceCell == null || shareBoughtCell == null) {
            return;
        }

        // 安全获取各单元格的值
        String userId = getCellStringValue(userIdCell);
        Double sharePrice = getCellNumericValue(sharePriceCell);
        Integer shareBought = getCellIntegerValue(shareBoughtCell);

        if (userId == null || sharePrice == null || shareBought == null) {
            return;
        }

        double transactionAmount = sharePrice * shareBought;

        synchronized (userSpendingMap) {
            userSpendingMap.put(userId, userSpendingMap.getOrDefault(userId, 0.0) + transactionAmount);
        }
    }

    // 工具方法:获取单元格的字符串值
    private static String getCellStringValue(Cell cell) {
        if (cell == null) return null;
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue().trim();
            case NUMERIC:
                // 数字类型转为字符串(适配数字格式的用户ID)
                return String.valueOf((long) cell.getNumericCellValue());
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case BLANK:
                return null;
            default:
                return null;
        }
    }

    // 工具方法:获取单元格的数值(Double类型)
    private static Double getCellNumericValue(Cell cell) {
        if (cell == null) return null;
        switch (cell.getCellType()) {
            case NUMERIC:
                return cell.getNumericCellValue();
            case STRING:
                // 字符串类型的数字尝试转换为Double
                try {
                    return Double.parseDouble(cell.getStringCellValue().trim());
                } catch (NumberFormatException e) {
                    System.err.println("无法将字符串转换为数字:" + cell.getStringCellValue());
                    return null;
                }
            case BLANK:
                return null;
            default:
                return null;
        }
    }

    // 工具方法:获取单元格的整数(Integer类型)
    private static Integer getCellIntegerValue(Cell cell) {
        Double numericValue = getCellNumericValue(cell);
        return numericValue != null ? numericValue.intValue() : null;
    }
}

关键优化点

  • 单元格类型安全处理:新增三个工具方法,根据CellType分支处理,支持数字转字符串、字符串转数字的兼容场景,彻底避免类型不匹配异常。
  • 表头跳过:默认跳过第一行(适配带表头的Excel结构,无表头可删除),避免处理表头数据导致错误。
  • 优雅的线程等待:用executorService.awaitTermination()替代循环Thread.sleep(),符合并发编程规范且更高效。
  • 空值校验增强:在获取值后再次校验,避免后续计算出现空指针。

额外建议

  • 频繁处理Excel时,可使用Apache POI提供的DataFormatter类,它能自动格式化不同类型的单元格并返回字符串,再按需转换为对应类型。
  • 考虑用ConcurrentHashMap替代HashMap,可去掉synchronized块,提升并发性能。

内容的提问来源于stack exchange,提问作者QasyeefSunnymizer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:45:28