Tika 2.x无法正确检测分号分隔的Excel格式CSV问题求助
Tika无法识别分号分隔CSV的问题解答
问题描述
在内容管理系统(CMS)中集成Tika进行文件类型检测时,发现Tika无法正确识别分号分隔的CSV格式:它可以正常识别逗号或制表符分隔的CSV,但对作为Excel标准的分号分隔CSV只会识别为text/plain。
验证代码
for (char delimiter : new char[]{'\t', ';', ','}) { System.out.println("With delimiter: <" + delimiter+">"); CSVFormat csvFormat = CSVFormat.DEFAULT.builder().setDelimiter(delimiter).setHeader("Name","Value","Count").build(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); CSVPrinter printer = new CSVPrinter(new OutputStreamWriter(baos), csvFormat); for (int i=1;i<=100;i++) { printer.printRecord("Name"+i,"Value"+i,i); } printer.flush(); ContentHandler handler = new BodyContentHandler(); ParseContext context = new ParseContext(); Metadata metadata = new Metadata(); Parser parser = new TextAndCSVParser(); parser.parse(new ByteArrayInputStream(baos.toByteArray()), handler, metadata, context); System.out.println(metadata); }
输出结果
With delimiter: < > csv:num_rows=101 csv:num_columns=3 Content-Encoding=windows-1252 csv:delimiter=tab Content-Type=text/tsv; charset=windows-1252; delimiter=tab With delimiter: <;> Content-Encoding=windows-1252 Content-Type=text/plain; charset=windows-1252 With delimiter: <,> csv:num_rows=101 csv:num_columns=3 Content-Encoding=windows-1252 csv:delimiter=comma Content-Type=text/csv; charset=windows-1252; delimiter=comma
提问:这是Bug,还是因多CSV格式支持难度大而被放弃?能否让Tika支持该格式?使用版本为2.9.2。
回答
这不是Bug,而是Tika的设计决策导致的。
原因分析
Tika的TextAndCSVParser默认仅针对逗号(CSV)和制表符(TSV)实现了专门的格式检测逻辑。分号分隔的CSV虽在部分地区(如欧洲)的Excel中是默认格式,但由于CSV本身没有严格统一的行业标准,不同场景下分隔符差异极大,Tika无法覆盖所有可能的分隔符类型,因此只优先支持了最通用的逗号和制表符两种。
解决方法
你可以通过自定义扩展让Tika支持分号分隔的CSV,具体有两种可行方式:
- 自定义CSV解析逻辑:继承
TextAndCSVParser并重写分隔符检测逻辑,加入分号的判断;或者在解析前手动检测内容中的分号出现频率,当符合CSV结构特征时,强制设置对应的Content-Type。 - 调整Tika配置:在Tika的配置文件中修改
csv.detectors相关参数,添加分号作为候选分隔符。2.9.2版本可尝试在初始化TextAndCSVParser时,传入包含分号的分隔符列表。
以下是一个简单的手动检测示例:
// 先读取内容并统计分号与行数 byte[] content = baos.toByteArray(); String contentStr = new String(content, StandardCharsets.UTF_8); long semicolonCount = contentStr.chars().filter(c -> c == ';').count(); long lineCount = contentStr.lines().count(); // 若每行平均分号数稳定,判定为分号分隔CSV if (lineCount > 0 && semicolonCount / lineCount >= 2) { metadata.set(Metadata.CONTENT_TYPE, "text/csv; charset=windows-1252; delimiter=semicolon"); } // 执行解析 parser.parse(new ByteArrayInputStream(content), handler, metadata, context);
内容的提问来源于stack exchange,提问作者Cjxcz Odjcayrwl
相关产品推荐
相关产品推荐

