如何在 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?

【问题讨论】:

如果要忽略引用字符串中的制表符/逗号,这有点复杂。 原始文件不使用带引号的字符串,输出文件也不需要添加它们。 @Village tr 将是上述工作的正确工具,但您担心 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 ~ /[,"]/ &amp;&amp; $i !~ /^".*"$/ 检测到任何包含 , 和/或 " 且尚未用双引号括起来的字段

gsub("\"", "\"\"", $i) 转义嵌入的 " 字符。将它们翻倍

$i = "\"" $i "\"" 通过将结果括在双引号中来更新结果

如前所述,更新任何字段会导致awk 重建字段中的行具有OFS,即在这种情况下为, ,相当于有效的 TSV -> CSV 转换;标志rebuilt 用于确保每个输入记录至少重建一次

【讨论】:

wrt The caveat with awk is that FS - the input field separator - must be set to \t explicitly - 这对 awk 的警告与对 trsed 的警告一样。在所有 3 个工具中,您需要指定输入字段分隔符以及您希望将其转换为的内容,如果您不这样做,那么显然您将无法获得您想要的行为。 @EdMorton:由于\t awk 视为输入字段分隔符的字符默认情况下,因此可能会导致假设这里不需要设置输入字段分隔符 - 由于我指出的原因,这是一个谬误;因此需要注意。至于trsed:输入字段分隔符的概念不适用。 当我说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 &gt; 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 文件

.Tsv . Csv 文件

Spark-SQL:如何将 TSV 或 CSV 文件读入数据框并应用自定义模式?

数据文件的内容中,字段中间用制表符Tab键分隔。

如何让pandas停止跳过TSV文件中的第一个空白列?

在 Ruby 中解析制表符分隔文件的最佳方法是啥?