从 CSV 文件中提取前 2 个字段(可能包含逗号或引号)[重复]

Extract the first 2 fields from a CSV file (may contain commas or quotes) [duplicate]

提问人:Uri 提问时间:11/17/2023 最后编辑:Uri 更新时间:11/17/2023 访问量:106

问:

这个问题在这里已经有答案了:
5天前关闭。

这篇文章在 5 天前经过编辑并提交审核。

我有一个包含 3 个或更多字段的 CSV 文件,我想按原样提取前 2 个字段,并将它们保存到一个新文件中。请注意,这些字段可能被引号或不引号引号,并且可能包含逗号或引号(加倍)。我想完全按原样提取前 2 个字段(无论是否引用),并忽略第三个字段和其他字段(如果是)。这应该从命令行完成。目前我有这个命令:

cat 1.csv | awk -F, '{print $1","$2}' > 2.csv

但是,如果字段中有逗号,则不起作用。

字段可以是空的(不包含任何内容,甚至不包含引号)。

(我还用awk检查了忽略CSV文件中的逗号,但那里的答案对我不起作用)

更新:这个问题是不同的,因为它要求CSV格式与原始格式相同 - 带引号或不带引号的字段。我有一个解决方案,我想提交。

bash csv 解析 awk 命令行

评论

3赞 anubhava 11/17/2023
应提供示例输入 CSV 和预期输出。
0赞 user1934428 11/17/2023
CSV 字段可以包含 CSV 分隔符、引号和换行符。要处理这种情况,您可以重新发明轮子并编写另一个 CSV 解析器(除了已经存在的许多解析器之外),或者使用现有的解析器。我正在使用 Ruby 附带的那个,过去也使用过 Perl 中的那个。可能还有一种适合您最喜欢的编程语言。
0赞 Zach Young 11/17/2023
您可以使用非标准的 CSV 感知命令行工具吗?
1赞 Uri 11/17/2023
@ZachYoung是的。
0赞 tripleee 11/17/2023
第二个副本有许多答案不适用于任何 CSV 文件,但也有许多答案适用于任何 CSV 文件。

答:

1赞 Ed Morton 11/17/2023 #1

给定此输入文件,其中包含包含嵌入引号、逗号和换行符的带引号的字段:

$ cat file.csv
"foo,""bar""",2,3
1,"foo,bar",3
1,"foo,
bar",3

然后使用 GNU awk 5.3 或更高版本进行 CSV 处理:

$ awk --csv -v OFS=',' '{for (i=1; i<=NF; i++) { gsub(/"/,"\"\"",$i); if ($i ~ /[,\n"]/) $i="\"" $i "\"" } print $1, $2}' file.csv
"foo,""bar""",2
1,"foo,bar"
1,"foo,
bar"

读取输入时,CSV格式保护字段内容所需的引号将被剥离,因此我们必须在打印之前将它们添加回去,否则我们将得到以下输出:

$ awk --csv -v OFS=',' '{print $1, $2}' file.csv
foo,"bar",2
1,foo,bar
1,foo,
bar

这不是有效的 CSV。

另请参阅使用 awk 有效解析 CSV 的最可靠方法是什么?

0赞 Zach Young 11/17/2023 #2

GoCSV 具有 select 子命令,该子命令允许使用从指定的(或管道传入的)CSV 中选择要保留(或排除)的列。GoCSV 是为许多现代平台预构建的。

从以下输入 CSV 开始:

Col1,Col2,Col3
"foo, bar",baz,baker
"Foo, Bar",Baz,baker

您可以直接在文件上调用 gocsv:

gocsv select -c 1,2 input.csv

或将 CSV 通过管道传递到:

cat input.csv | gocsv select -c 1,2

要获取:

Col1,Col2
"foo, bar",baz
"Foo, Bar",Baz

GoCSV 始终将第一行解释为标题(并使用标题,以便您可以根据需要按名称标注列)。如果原始文件没有标头,则可以从 cap 子命令开始添加临时标头,将其通过管道传递到 select 中,然后通过管道将其传输到 behead 子命令中以删除临时标头。

添加盖子:

echo \"foo, bar\",baz,baker\\n\"Foo, Bar\",Baz,Baker | gocsv cap -names Col1,Col2,Col3

Col1,Col2,Col3
"foo, bar",baz,baker
"Foo, Bar",Baz,Baker
... | gocsv select -c 1,2 | gocsv behead

"foo, bar",baz
"Foo, Bar",Baz

cap 子命令具有 -default-name 选项,但如果至少在显式列名上提供 -names 和 -names,它就无法工作。