这个问题的目的是提供一个规范的答案。
给定Excel或其他工具生成的CSV,字段中嵌入换行符和/或双引号和/或逗号,空字段如下:
$ cat file.csv
"rec1, fld1",,"rec1"",""fld3.1
"",
fld3.2","rec1
fld4"
"rec2, fld1.1
fld1.2","rec2 fld2.1""fld2.2""fld2.3","",rec2 fld4
"""""","""rec3,fld2""",
使用awk来识别单独的记录和字段,最有效的方法是什么?
Record 1:
$1=<rec1, fld1>
$2=<>
$3=<rec1","fld3.1
",
fld3.2>
$4=<rec1
fld4>
----
Record 2:
$1=<rec2, fld1.1
fld1.2>
$2=<rec2 fld2.1"fld2.2"fld2.3>
$3=<>
$4=<rec2 fld4>
----
Record 3:
$1=<"">
$2=<"rec3,fld2">
$3=<>
----
因此它可以被awk脚本的其余部分内部用作那些记录和字段。
有效的CSV应符合RFC 4180或可由MS-Excel生成。
该解决方案必须允许记录结尾仅为LF(\n
),这是UNIX文件的典型值,而不是CRLF(\r\n
),这是该标准所要求的,Excel或其他Windows工具将生成。它还允许无引号字段与带引号字段混合使用。特别是,它不需要允许在"
s前使用反斜杠进行转义(即\"
而不是""
),因为一些其他的CSV格式允许-如果你有,然后添加一个gsub(/\\"/,"\"\"")
的前面将处理它,并试图处理两个转义机制自动在一个脚本将使脚本不必要的脆弱和复杂。
6条答案
按热度按时间ccrfmcuu1#
如果你的CSV不能包含换行符,那么你所需要的就是(使用GNU awk for FPAT):
或使用任何awk的等效物:
有关https://www.gnu.org/software/gawk/manual/gawk.html#More-CSV我上面使用的特定
FPAT
设置的信息,请参见www.example.com。如果你实际上想做的只是将CSV转换成单独的行,比如说,在带引号的字段中用空格替换换行符,用分号替换逗号,那么你所需要做的就是这样,再次使用GNU awk来处理多字符RS和RT:
但是,在其他方面,识别适用于任何现代awk* 的字段的通用、健壮、可移植的解决方案是:
。
上面假设UNIX行尾为
\n
,使用Windows\r\n
行尾要简单得多,因为每个字段中的“换行符”实际上只是换行符(即\n
s),所以您可以设置RS="\r\n"
(使用GNU awk实现多字符RS),然后字段中的\n
s将不会被视为行尾。它的工作原理是,每当遇到
RS
时,只需计算当前记录中迄今为止存在多少个"
-如果是奇数,则为RS
(大概是\n
,但不一定是)是中场,所以我们继续创造目前的记录,但如果它甚至那么它'It“这是当前记录的结尾,因此我们可以继续执行脚本的其余部分,处理现在已完成的记录。gsub()
实现中有bug,这样gsub(/^"|"$/,"",fldStr)
就不会从fldStr
中删除开始/结束"
。如果你正在使用其中的一个,那么**得到一个新的awk,最好是gawk,**因为它们也可能有其他问题,但如果这不是一个选项,那么我希望您可以通过更改以下内容来解决该特定错误:改为:
感谢以下人员确定并建议解决本答案原始版本中所述问题的解决方案:
1.@mosvy表示字段内的转义双引号。
1.@datatraveller1表示字段中的多个连续转义引号对和记录末尾的空字段。
相关:另请参见How do I use awk under cygwin to print fields from an excel spreadsheet?了解如何从Excel电子表格生成CSV。
bqucvtff2#
对@EdMorton的
FPAT
解决方案的改进,该解决方案应该能够处理通过加倍(""
--CSV standard允许)转义的双引号("
)。这仍然
1.不能处理带引号的字段中的换行符,而这在标准CSV文件中是完全合法的。
1.假设GNU awk(
gawk
),标准的awk就不行了。示例:
ru9i0ody3#
这正是csvquote的用途--它使awk和其他命令行数据处理工具的工作变得简单。
有些东西很难用awk来表达,不是运行单个awk命令并试图让awk来处理带引号的、嵌入逗号和换行符的字段,而是通过csvquote来为awk准备数据,这样awk就可以始终将它找到的逗号和换行符解释为字段分隔符和记录分隔符,这使得管道的awk部分变得更简单,一旦awk处理完数据,它返回
csvquote -u
以恢复带引号的字段中嵌入的逗号和换行符。编辑:
有关
csvquote
的完整说明,请参见:它是如何工作的。这也解释了在有回车的地方显示的字符。(for
decsv.awk
来源参见Ed Morton的回答)输出:j9per5c44#
我发现csvkit是一个非常有用的工具包,可以在命令行中处理csv文件。
它还包含
csvstat
,csvstack
等工具,这些工具也非常方便。一个二个一个一个
rm5edbpk5#
Awk(gawk)实际上提供了一些扩展,其中之一是csv processing,在我看来,这是使用 gawk 最健壮的方式,它可以处理许多陷阱,并为您解析csv。
假设安装了该扩展,您可以使用awk显示特定csv字段与
123
匹配的所有行。假设
test.csv
包含以下内容:以下代码将打印
Phone
(也称为第二个字段)等于123
的所有行:输出为:
它是如何工作的?
-l csv
要求gawk通过在$AWKLIBPATH
中查找csv
扩展来加载它;csvsplit($0, a)
拆分当前行,并将每个字段存储到名为a
的新数组中&& a[2] == 123
检查第二个字段是否为123
{ print a[1] }
,aka打印该行的第一个csv字段。hvvq6cgz6#
如果你使用的是一个常见的AWK解释器(Gawk、onetrueawk、mawk),那么其他的解决方案是你最好的选择。然而,如果你能够使用不同的解释器,frawk和GoAWK都有内置的CSV支持。
frawk是用Rust编写的一个非常快的AWK实现。使用
-i csv
来处理CSV模式下的输入。注意,frawk并不完全兼容POSIX(查看差异)。GoAWK是一个用Go语言编写的POSIX兼容的AWK实现,也支持
-i csv
模式,以及@"named_field"
语法(read more)的-H
(解析头行)。* 免责声明:我是GoAWK的作者。对于问题中的
file.csv
,您可以简单地使用AWK脚本,在字段上执行常规for
循环,如下所示:然后使用
frawk -i csv
或goawk -i csv
获取预期输出。例如: