假设我有一个包含以下形式记录的文本文件,其中
FS
通常是逗号,RS
通常是换行符。
但是,此规则的例外是,如果字段用引号引起来,则应将换行符和逗号视为字段的一部分。
"This field contains
line breaks and is
quoted but it
should be treated as a
single field",1,2,3,"another field"
如何使用 awk 正确解析这样的文件,我仍然可以像平常一样访问
$1,$2...
,但具有上述字段解释?
我已经看过这个wiki页面,但是那里提出的解决方案并没有解决换行问题。
一个可能的解决方案,尽管并不完美,是这样的:
awk 'BEGIN{RS="\""}{...}'
。通过执行此操作,您可以将记录分隔符重置为 "
,而字段分隔符仍为空格。问题是,这会向您的文件添加两个空记录,因为第一个和最后一个 "
也将被匹配以分隔某些记录。
示例:
awk 'BEGIN{RS="\""} {print $0,"END OF RECORD",$1,"-",$2}'
应用于您的数据时将产生此结果
END OF RECORD -
This field contains
line breaks and is
quoted but it
should be treated as a
single field END OF RECORD This - field
,1,2,3, END OF RECORD ,1,2,3, -
another field END OF RECORD another - field
END OF RECORD -
您可以通过添加条件
NR>1
来跳过第一个。不过,最后一个有点棘手,因为您不知道文件中有多少条记录。您可以将要打印的值保存在数组中,并在 for
语句中使用 END
循环打印它们,跳过文件中的第一条和最后一条记录。
为了让 awk 正确解析文件,您可以使用我编写的一个名为 csvquote 的程序,该程序临时用不会混淆 awk 的非打印字符替换出现在引用字段内的逗号和换行符。该程序将数据净化为 awk 可以依赖始终代表字段分隔符的逗号和始终代表记录分隔符的换行符的格式。
要使用它,您需要将涉及 cut/awk/... 的管道包装起来,如下所示:
csvquote /tmp/foo.csv | tail +2 | awk -F, '{print $3 $2}' | csvquote -u
您可以在这里找到代码:https://github.com/dbro/csvquote
需要注意的是,如果您想在字段内搜索逗号和换行符,这会使该任务变得更加复杂,因为您需要搜索非打印字符。如果您正在寻找一种更轻松地完成此操作的方法,您应该研究 csvfix 工具。
另一个选项是使用 awk 的 FPAT,但如果字段包含转义引号,则该选项不起作用。请参阅 http://www.gnu.org/software/gawk/manual/html_node/Splitting-By-Content.html
您可以使用双换行符作为记录分隔符。如果您还设置逗号作为字段分隔符,那么这允许您将每个文本块作为字段处理:
awk -v RS="\n\n" -v FS="," '...' file
对于您给定的文件,让我们显示文件编号以及文件本身:
$ awk -v RS="\n\n" -v FS="," '{for (i=1; i<=NF; i++) print i, $i}' file
1 "This field contains
line breaks and is
quoted but it
should be treated as a
single field"
2 1
3 2
4 3
5 "another field"
Brian Kernighan 的
awk
和 GNU gawk
现在都直接支持 CSV 文件。
这是 GNU gawk 手册的内容:
几十年来,任何希望使用 CSV 文件和 awk 的人都必须“推出自己的”解决方案。 (有关示例,请参阅按内容定义字段)。 2023 年,Brian Kernighan 决定在他的 awk 版本中添加 CSV 支持。为了跟上,gawk 也提供了与他的版本相同的支持。要使用 CSV 数据,请使用 -k 或 --csv 选项调用 gawk。
echo '"This field contains
line breaks and is
quoted but it
should be treated as a
single field",1,2,3,"another field"' |
awk --csv '
{
print NR, $1
}
'
从 2023 年起,您将需要 gawk ≥ v5.3 或 BWK 的 awk。
CSV 严格按照 RFC 4180 进行解释,它仅封装了最常见的做法,而不是使用分号作为分隔符等变体。