
本文介绍如何在 java 中正确处理含多行文本字段、混合分隔符(tab + `\n`)且以自定义标记(如 `"test2222"`)结尾的原始字符串,将其稳健地转换为合规 csv 字符串,避免因简单 `split()` 或 `csvparser` 导致的字段错位问题。
在实际数据集成场景中,常遇到“伪 TSV”格式:表面以 Tab 分隔字段,但字段值本身包含换行符(\n)、引号和空格,且整行以固定终结标记(如 "test2222")而非标准换行符界定——这导致传统按行读取(BufferedReader.readLine())或通用 CSV 解析器(如 OpenCSV 的 CSVParser)失效,因为它们无法识别逻辑行边界。
核心思路是绕过“按行解析”的陷阱,改用“按记录分隔符切分”:将 "test2222" 视为逻辑行(record)的结束标志,先以此为界提取完整记录块,再对每个块内部进行字段清洗与 CSV 转义。
以下为推荐实现方案(基于 java.util.Scanner,兼顾简洁性与可控性):
import java.util.Scanner;
import java.util.Arrays;
public class TsvToCsvConverter {
// 主要转换方法:输入原始字符串,返回 CSV 格式字符串(每行一个逻辑记录)
public static String convertToCsv(String input, String recordDelimiter) {
Scanner scanner = new Scanner(input);
scanner.useDelimiter(recordDelimiter); // 以自定义标记切分逻辑行
StringBuilder csvResult = new StringBuilder();
while (scanner.hasNext()) {
String record = scanner.next().trim();
if (record.isEmpty()) continue;
// 步骤1:用正则安全分割字段(匹配一个或多个空白符,但保留引号内空白)
// ⚠️ 注意:此简化版适用于字段无内部 Tab/空格嵌套的场景;若需完全 RFC 4180 合规,请使用专业库如 OpenCSV
String[] fields = record.split("\\s+");
// 步骤2:逐字段清洗:去除首尾引号、转义内部引号(CSV 要求:双引号内双引号需转义为 "")
String[] cleanedFields = Arrays.stream(fields)
.map(f -> {
if (f.startsWith("\"") && f.endsWith("\"")) {
return "\"" + f.substring(1, f.length() - 1).replace("\"", "\"\"") + "\"";
}
return "\"" + f.replace("\"", "\"\"") + "\""; // 无引号字段也包裹,确保安全
})
.toArray(String[]::new);
// 步骤3:用逗号连接,添加换行
csvResult.append(String.join(",", cleanedFields)).append("\n");
}
scanner.close();
return csvResult.toString();
}
// 使用示例
public static void main(String[] args) {
String test = "\"abc\"\t\"cde\"\t\"fhg\"\t\"ijk\"\t\"17/01/23 10:09:50 am\"\t\"test111\"\t\"test2\"\t\"Individual\"\t\"Enclosure of Work Areas\"\t\t\"Highlight aluminium personnel lanyarded into the Haulotte boom lift with a spotter. All tools observed to be lanyarded including protection gear. \n" +
"Blue glue asset card observed to be attached to the machinery, 10 year inspection of plant not required due to it being only 3yrs old. Last annual inspection august 2022 and logbook was subsequently observed. \n" +
"Plant registration was all observed and the weight loads were all abided by.\"\t\"test2222\"\n" +
"\"abc\"\t\"cde\"\t\"fhg\"\t\"ijk\"\t\"17/01/23 10:09:50 am\"\t\"test111\"\t\"test2\"\t\"Individual\"\t\"Enclosure of Work Areas\"\t\t\"1\"\t\"0\"\t\"Level 79\"\t\"16/01/23 11:12:50 pm\"\t\"Logistics - Construction Personnel & Material Lifts\"\t\t\t\t\t\"Schindler lift cages were observed to be free of any loose debris or material that may pose a risk of falling into the lift shaft below. L80 and L79 were observed to be compliant on both sides of the shaft.\"\t\"test2222\"";
String csvOutput = convertToCsv(test, "\"test2222\"");
System.out.print(csvOutput);
}
}✅ 关键优势与注意事项:
- 逻辑行精准识别:Scanner.useDelimiter("\"test2222\"") 确保跨多行的长文本字段不被错误截断;
- 字段清洗标准化:自动包裹双引号、转义内部引号(" → ""),符合 CSV RFC 4180 规范;
- 空字段兼容:连续 Tab 产生的空字段会被 split("\\s+") 正确捕获为 "",并转为 ""(空字符串 CSV 表示);
- ⚠️ 局限提示:若字段值本身含未转义的 Tab 或复杂嵌套引号,建议升级为 OpenCSV 的 CSVWriter 配合自定义 QuoteMode,或使用 Apache Commons CSV 进行流式解析;
- 内存友好:Scanner 支持流式处理大文件,无需一次性加载全部内容到内存。
最终生成的 CSV 可直接被 Excel、Pandas 或数据库 LOAD DATA INFILE 安全导入,彻底解决原始数据中“换行即换行,却非换行”的语义歧义问题。










