如果您的CSV不能包含换行符或转义双引号,那么所有你需要的是(与GNU AWK为FPAT
):
$ echo 'foo,"field,with,commas",bar' |
awk -v FPAT='[^,]*|"[^"]+"' '{for (i=1; i<=NF;i++) print i, "<" $i ">"}'
1 <foo>
2 <"field,with,commas">
3 <bar>
否则,不过,更普遍的,强大的,便携的解决方案,将与工作任何现代的awk是:
$ cat decsv.awk
function buildRec( i,orig,fpat,done) {
$0 = PrevSeg $0
if (gsub(/"/,"&") % 2) {
PrevSeg = $0 RS
done = 0
}
else {
PrevSeg = ""
gsub(/@/,"@A"); gsub(/""/,"@B") # <"[email protected]""bar"> -> <"[email protected]@Bbar">
orig = $0; $0 = "" # Save $0 and empty it
fpat = "([^" FS "]*)|(\"[^\"]+\")" # Mimic GNU awk FPAT meaning
while ((orig!="") && match(orig,fpat)) { # Find the next string matching fpat
$(++i) = substr(orig,RSTART,RLENGTH) # Create a field in new $0
gsub(/@B/,"\"",$i); gsub(/@A/,"@",$i) # <"[email protected]@Bbar"> -> <"[email protected]"bar">
gsub(/^"|"$/,"",$i) # <"[email protected]"bar"> -> <[email protected]"bar>
orig = substr(orig,RSTART+RLENGTH+1) # Move past fpat+sep in orig $0
}
done = 1
}
return done
}
BEGIN { FS=OFS="," }
!buildRec() { next }
{
printf "Record %d:\n", ++recNr
for (i=1;i<=NF;i++) {
# To replace newlines with blanks add gsub(/\n/," ",$i) here
printf " $%d=<%s>\n", i, $i
}
print "----"
}
。
$ awk -f decsv.awk file.csv
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>
----
以上假设UNIX行结尾为\n
。由于Windows \r\n
行结尾更简单,因为每个字段中的“换行符”实际上只是换行符(即\n
s),所以您可以设置RS="\r\n"
,然后在字段内设置\n
不会被视为行结束符。
它通过简单地计算有多少"
s为存在于当前纪录至今每当遇到RS
- 如果它是一个奇数,则RS
(大概\n
但不必须)是中场所以我们继续构建当前记录,但是如果它甚至是当前记录的结尾,所以我们可以继续处理现在完整记录的脚本的其余部分。
的gsub(/@/,"@A"); gsub(/""/,"@B")
转换每对双引号axcross全程实录(记住这些""
对只能援引领域内应用)将字符串@B
不包含双引号,这样,当我们记录拆分成田match()不会被出现在字段内的引号绊倒。 gsub(/@B/,"\"",$i); gsub(/@A/,"@",$i)
单独恢复每个字段内的引号,并将""
s转换为它们实际表示的"
。