如何在 BASH 中将制表符分隔值 (TSV) 文件转换为逗号分隔值 (CSV) 文件?
Posted
技术标签:
【中文标题】如何在 BASH 中将制表符分隔值 (TSV) 文件转换为逗号分隔值 (CSV) 文件?【英文标题】:How do I convert a tab-separated values (TSV) file to a comma-separated values (CSV) file in BASH? 【发布时间】:2014-04-20 15:05:01 【问题描述】:我有一些 TSV 文件需要转换为 CSV 文件。 BASH中是否有任何解决方案,例如使用awk
,来转换这些?我可以像这样使用sed
,但我担心它会出错:
sed 's/\t/,/g' file.tsv > file.csv
无需添加引号。
如何将 TSV 转换为 CSV?
【问题讨论】:
如果要忽略引用字符串中的制表符/逗号,这有点复杂。 原始文件不使用带引号的字符串,输出文件也不需要添加它们。 @Villagetr
将是上述工作的正确工具,但您担心 sed 会犯什么样的错误?您能否更新您的问题以显示一些您认为 sed 可能无法正确处理的示例输入?
【参考方案1】:
更新:以下解决方案一般来说并不可靠,尽管它们确实适用于 OP 的特定用例;请参阅底部了解基于awk
的强大解决方案。
总结一下这些选项(有趣的是,它们的表现都差不多):
tr:
devnull的解决方案(在问题的评论中提供)是最简单的:
tr '\t' ',' < file.tsv > file.csv
sed:
鉴于输入不包含带引号的字符串(可能嵌入 \t
字符),OP 自己的 sed
解决方案非常好:
sed 's/\t/,/g' file.tsv > file.csv
唯一需要注意的是,在某些平台(例如 macOS)上,不支持转义序列 \t
,因此是文字制表符。必须使用 ANSI 引用 ($'\t'
) 拼接到命令字符串中:
sed 's/'$'\t''/,/g' file.tsv > file.csv
awk:
awk
的警告是 FS
- 输入字段分隔符 - 必须设置为 \t
明确 - 否则默认行为会去除前导和尾随制表符并替换内部跨度只有一个,
的多个选项卡:
awk 'BEGIN FS="\t"; OFS="," $1=$1; print' file.tsv > file.csv
请注意,简单地将$1
分配给自身会导致awk
使用OFS
重建输入行 - 输出 字段分隔符;这有效地替换了所有 \t
字符。带有,
字符。 print
然后简单地打印重建的行。
强大的awk
解决方案:
正如A. Rabus 指出的那样,上述解决方案不能正确处理本身包含,
字符的未加引号的输入字段 - 您最终会得到额外的 CSV 字段。
以下awk
解决方案通过按需将这些字段包含在"..."
中来解决此问题(有关该方法的部分说明,请参见上面的非稳健awk
解决方案)。
如果这些字段也嵌入了"
字符。这些字符将转义为""
,与RFC 4180 一致。谢谢Wyatt Israel。
awk 'BEGIN FS="\t"; OFS=","
rebuilt=0
for(i=1; i<=NF; ++i)
if ($i ~ /,/ && $i !~ /^".*"$/)
gsub("\"", "\"\"", $i)
$i = "\"" $i "\""
rebuilt=1
if (!rebuilt) $1=$1
print
' file.tsv > file.csv
$i ~ /[,"]/ && $i !~ /^".*"$/
检测到任何包含 ,
和/或 "
且尚未用双引号括起来的字段
gsub("\"", "\"\"", $i)
转义嵌入的 "
字符。将它们翻倍
$i = "\"" $i "\""
通过将结果括在双引号中来更新结果
如前所述,更新任何字段会导致awk
重建字段中的行具有OFS
值,即在这种情况下为,
,相当于有效的 TSV -> CSV 转换;标志rebuilt
用于确保每个输入记录至少重建一次。
【讨论】:
wrtThe caveat with awk is that FS - the input field separator - must be set to \t explicitly
- 这对 awk 的警告与对 tr
或 sed
的警告一样。在所有 3 个工具中,您需要指定输入字段分隔符以及您希望将其转换为的内容,如果您不这样做,那么显然您将无法获得您想要的行为。
@EdMorton:由于\t
是在 awk
视为输入字段分隔符的字符默认情况下,因此可能会导致假设这里不需要设置输入字段分隔符 - 由于我指出的原因,这是一个谬误;因此需要注意。至于tr
和sed
:输入字段分隔符的概念不适用。
当我说input field separator
时,我只是指the character that separates your input into fields (values)
,它适用于规定的输入格式(tab-separated-values
),而不是任何特定的工具。我只是不认为必须为 awk 指定它来告诉它如何按照需要进行操作,而不是为其他工具指定它 - 这在所有 3 中都是完全相同的概念,如果你不这样做那么您将无法获得所需的行为。无论如何,tr
是上述工作的正确工具,所以这可能是一个有争议的问题。【参考方案2】:
这也可以用 Perl 来实现:
要将结果通过管道传输到新的输出文件,您可以使用以下命令:perl -wnlp -e 's/\t/,/g;' input_file.tsv > output_file.csv
如果您想就地编辑文件,可以调用 -i 选项:perl -wnlpi -e 's/\t/,/g;' input_file.txt
如果您偶然发现您正在处理的实际上不是制表符,而是多个空格,您可以使用以下命令将每个出现的两个或多个空格替换为逗号:perl -wnlpi -e 's/\s+/,/g;' input_file
请记住,\s
代表任何空白字符,包括空格、制表符或换行符,不能在替换字符串中使用。
【讨论】:
你也可以使用 vim。只需在命令模式下使用以下搜索和替换::%s/\t/,/g
这使您可以立即查看结果并在需要时通过按 (u) 键撤消它们。
如果你已经在使用 Perl,你也可以使用metacpan.org/pod/Text::CSV【参考方案3】:
tr 命令:
tr '\t' ',' < file.tsv > file.csv
很简单,即使在一个非常大的文件(大约 10 GB)上,也能为我提供绝对正确且非常快速的结果。
【讨论】:
【参考方案4】:使用 awk 对我有用
将 tsv 转换为 csv
awk 'BEGIN FS="\t"; OFS="," $1=$1; print' file.tsv > file.csv
或将 csv 转换为 tsv
awk 'BEGIN FS=","; OFS="\t" $1=$1; print' file.csv > file.tsv
【讨论】:
【参考方案5】:您可以简单地在 shell 中使用sed
的强大功能:
sed -r 's/\t/","/g' file.tsv|sed -r 's/(^|$)/"/g' > file.csv
通常,上述命令会将您的tsv
文件转换为csv
。但是tsv
文件可能包含数字字段。在这种情况下,它们不应该像"123456"
那样被"
包围。所以我们需要另一个阶段来删除这样的双引号。最终解决方案:
sed -r 's/\t/","/g' file.tsv|sed -r 's/(^|$)/"/g'|sed -r 's/"([0-9]+)"/\1/g' > file.csv
【讨论】:
以上是关于如何在 BASH 中将制表符分隔值 (TSV) 文件转换为逗号分隔值 (CSV) 文件?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用制表符分隔符 sep = "\t" 在 R 中编写 .tsv 文件