我正在使用许多不同格式的CSV文件,通常由普通用户手动编写,因此字段中有很多;
,
n
和t
之类的,这种字符通常在解析数据或生成新的CSV文件时会引起麻烦, 而且我通常会事先删除这些字符,但今天我在旧程序中发现了以下代码:
它从resultSet
中读取数据并将每个值连接成一个String
形成一行,并处理分隔符(我们通常使用;
(,这段代码首先使用类似#TempSeparator#
的临时标志来划分每个字段,然后,当行完成后,应用replaceAll(";","")
来删除假分隔符, 之后,replaceAll("#TempSeparator#", ";")
形成有效行
我实际上认为这是一种聪明的方法,可以避免对每个字段进行不必要的调用,并且只对整行进行替换,但我认为这不是最好甚至最佳的方法。
这真的是一种不好的做法吗?
这不是很糟糕,但也不是最好的方法。
尽可能使用标准库。以下是优秀库的列表,其中SuperCSV在支持CSV变体方面特别强大。这些库遵循最佳实践:在字段中使用特殊字符时进行转义,或者将字段换行(通常使用引号(。
如果 CSV 格式已经不正确,以至于特殊字符出现在字段中而没有正确转义或换行,那么您手上有一个数据清理问题,需要通过其他方式解决。用临时占位符替换字符不会解决这个问题,因为占位符同样会出现在字段内部和字段之间。
这种方法对我来说听起来不是很优雅。您描述:
- 每个输入字段,使用
#TempSeparator#
将其连接到单个字符串 - 从生成的字符串中消除所有
;
- 将所有出现的
#TempSeparator#
替换为;
这是 3 个步骤。不如说:
- 每个输入字段,去除所有
;
- 然后,使用
;
将其连接到单个字符串上
少了一步。因此,更好。
一些示例代码来说明为什么这更简单:
流样式:
collection.stream().collect(Collectors.joining("#TempSeparator#"))
.replace(";", "").replace("#TempSeparator#", ";");
与。
collection.stream().map(x -> x.replace(";", ""))
.collect(Collectors.joining(";"));
它被过度设计,是一个不必要的伪影。
至少使用常量字符:
private static final char TEMP_SEPARATOR = 'u001f';
- 通常,制表符不会出现在文本
t
中,它是一个理想的分隔符。 - 否则,控制字符可以:单位分隔符
u001f
。可能是Unicode的。 - 几乎无法使用的字符是
u0000
.
优点是其效率:
s = s.replace(';', ',').replace('u0000', ';');
最好的解决方案仍然是更换单元格值本身的分隔符。 就像换行符可能必须做的那样。
关于最后一个分隔符:恕我直言,制表符是比分号更好的选择,因为用空格替换制表符确实只会更改文本 w.r.t. 空格。也就是说:不能产生任何要求。像房间号必须是楼层";"号。