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

Apache POI将制表符分隔TXT当作XLSX处理的异常问题咨询

解决思路:无法直接将TXT当作XSSFWorkbook处理,但可以复用对比逻辑

首先得明确:你没法直接把制表符分隔的TXT文件当作XSSFWorkbook来处理——XSSFWorkbook是Apache POI专门为OOXML格式(也就是标准的.xlsx文件)设计的解析器,而TXT是纯文本格式,和OOXML的压缩二进制结构完全不兼容,所以抛出NotOfficeXmlFileException是必然的结果。

但你的核心需求是复用现有的XLSX对比逻辑,不用单独写TXT的对比代码,这个是可以实现的,核心思路是抽象统一的数据访问层,让对比逻辑只和通用的“工作表”接口交互,不用关心底层是XLSX还是TXT。

具体实现步骤

1. 定义通用的工作表访问接口

先写一个接口,封装获取行数、列数、单元格值的核心方法,让XLSX和TXT的读取逻辑都实现这个接口:

public interface SheetAccessor {
    // 获取总行数
    int getRowCount();
    // 获取指定行的列数
    int getColumnCount(int rowIndex);
    // 获取指定行、列的单元格值(统一返回字符串,方便对比)
    String getCellValue(int rowIndex, int columnIndex);
}

2. 为XLSX实现接口

直接复用你现有的XSSFWorkbook逻辑,把它包装到接口实现里:

import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class XlsxSheetAccessor implements SheetAccessor {
    private XSSFSheet sheet;

    public XlsxSheetAccessor(File xlsxFile) throws IOException {
        try (XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(xlsxFile))) {
            // 假设只处理第一个工作表,你可以根据需求调整
            this.sheet = workbook.getSheetAt(0);
        }
    }

    @Override
    public int getRowCount() {
        return sheet.getLastRowNum() + 1; // 注意POI的行索引从0开始,LastRowNum是最后一行的索引
    }

    @Override
    public int getColumnCount(int rowIndex) {
        XSSFRow row = sheet.getRow(rowIndex);
        return row != null ? row.getLastCellNum() : 0;
    }

    @Override
    public String getCellValue(int rowIndex, int columnIndex) {
        XSSFRow row = sheet.getRow(rowIndex);
        if (row == null) return "";
        XSSFCell cell = row.getCell(columnIndex);
        if (cell == null) return "";

        // 统一把单元格值转成字符串,方便对比(可根据你的需求调整格式,比如日期的显示方式)
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                } else {
                    return String.valueOf(cell.getNumericCellValue());
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            default:
                return "";
        }
    }
}

3. 为制表符TXT实现接口

读取TXT文件,按行拆分,再按制表符拆分单元格,同样实现接口:

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import java.nio.file.Files;

public class TsvSheetAccessor implements SheetAccessor {
    private List<String[]> rows;

    public TsvSheetAccessor(File tsvFile) throws IOException {
        // 读取所有行,按制表符拆分单元格(-1参数保留末尾的空制表符,避免丢失空列)
        rows = Files.lines(tsvFile.toPath())
                .map(line -> line.split("\t", -1))
                .collect(Collectors.toList());
    }

    @Override
    public int getRowCount() {
        return rows.size();
    }

    @Override
    public int getColumnCount(int rowIndex) {
        if (rowIndex < 0 || rowIndex >= rows.size()) return 0;
        return rows.get(rowIndex).length;
    }

    @Override
    public String getCellValue(int rowIndex, int columnIndex) {
        if (rowIndex < 0 || rowIndex >= rows.size()) return "";
        String[] row = rows.get(rowIndex);
        if (columnIndex < 0 || columnIndex >= row.length) return "";
        return row[columnIndex];
    }
}

4. 修改对比逻辑,复用核心代码

把你原来的XLSX对比逻辑改成接收SheetAccessor参数,这样不管是XLSX还是TXT,对比逻辑完全一样:

import java.util.ArrayList;
import java.util.List;

public class SheetComparator {
    public List<String> compareSheets(SheetAccessor controlSheet, SheetAccessor testSheet) {
        List<String> mismatches = new ArrayList<>();
        // 取两个表的最大行数和最大列数,避免遗漏行/列
        int maxRows = Math.max(controlSheet.getRowCount(), testSheet.getRowCount());
        
        for (int row = 0; row < maxRows; row++) {
            int maxCols = Math.max(controlSheet.getColumnCount(row), testSheet.getColumnCount(row));
            
            for (int col = 0; col < maxCols; col++) {
                String controlVal = controlSheet.getCellValue(row, col);
                String testVal = testSheet.getCellValue(row, col);
                
                if (!controlVal.equals(testVal)) {
                    // 这里的行号列号加1,是因为用户习惯从1开始计数
                    mismatches.add(String.format("不匹配:行%d,列%d → 控制文件值='%s',测试文件值='%s'", 
                            row + 1, col + 1, controlVal, testVal));
                }
            }
        }
        return mismatches;
    }
}

5. 调用时自动适配文件类型

最后在主逻辑里,根据文件后缀判断用哪个SheetAccessor实现:

import java.io.File;
import java.io.IOException;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        File controlFile = new File("path/to/control/file");
        File testFile = new File("path/to/test/file");

        SheetAccessor controlSheet = createSheetAccessor(controlFile);
        SheetAccessor testSheet = createSheetAccessor(testFile);

        SheetComparator comparator = new SheetComparator();
        List<String> results = comparator.compareSheets(controlSheet, testSheet);

        // 输出结果
        for (String mismatch : results) {
            System.out.println(mismatch);
        }
    }

    private static SheetAccessor createSheetAccessor(File file) throws IOException {
        String fileName = file.getName().toLowerCase();
        if (fileName.endsWith(".xlsx")) {
            return new XlsxSheetAccessor(file);
        } else if (fileName.endsWith(".txt")) {
            return new TsvSheetAccessor(file);
        } else {
            throw new IllegalArgumentException("不支持的文件类型:" + fileName);
        }
    }
}

额外说明:要不要把TXT转成XSSFWorkbook?

如果你非要“把TXT当作XSSFWorkbook处理”,理论上可以读取TXT内容后,在内存中创建一个XSSFWorkbook,逐行逐单元格写入数据,但这样完全是额外的内存开销,没有任何必要——上面的接口方式更高效,而且完全复用了你的对比逻辑,是更优的解决方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:53:24