使用 awk 解析 csv 并忽略字段内的逗号

问题描述 投票:0回答:8

我有一个 csv 文件,其中每一行定义给定建筑物中的一个房间。除了房间之外,每行都有一个地板区域。我想要提取的是所有建筑物的所有楼层。

我的文件看起来像这样...

"u_floor","u_room","name"
0,"00BDF","AIRPORT TEST            "
0,0,"BRICKER HALL, JOHN W    "
0,3,"BRICKER HALL, JOHN W    "
0,5,"BRICKER HALL, JOHN W    "
0,6,"BRICKER HALL, JOHN W    "
0,7,"BRICKER HALL, JOHN W    "
0,8,"BRICKER HALL, JOHN W    "
0,9,"BRICKER HALL, JOHN W    "
0,19,"BRICKER HALL, JOHN W    "
0,20,"BRICKER HALL, JOHN W    "
0,21,"BRICKER HALL, JOHN W    "
0,25,"BRICKER HALL, JOHN W    "
0,27,"BRICKER HALL, JOHN W    "
0,29,"BRICKER HALL, JOHN W    "
0,35,"BRICKER HALL, JOHN W    "
0,45,"BRICKER HALL, JOHN W    "
0,59,"BRICKER HALL, JOHN W    "
0,60,"BRICKER HALL, JOHN W    "
0,61,"BRICKER HALL, JOHN W    "
0,63,"BRICKER HALL, JOHN W    "
0,"0006M","BRICKER HALL, JOHN W    "
0,"0008A","BRICKER HALL, JOHN W    "
0,"0008B","BRICKER HALL, JOHN W    "
0,"0008C","BRICKER HALL, JOHN W    "
0,"0008D","BRICKER HALL, JOHN W    "
0,"0008E","BRICKER HALL, JOHN W    "
0,"0008F","BRICKER HALL, JOHN W    "
0,"0008G","BRICKER HALL, JOHN W    "
0,"0008H","BRICKER HALL, JOHN W    "

我想要的是所有建筑物的所有楼层。

我正在使用 cat、awk、sort 和 uniq 来获取此列表,尽管我在建筑物名称字段中遇到“,”问题,例如“BRICKER HALL,JOHN W”,并且它使我的整个 csv 生成失败。

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

如何让 awk 使用逗号但忽略字段“”之间的逗号?或者,有人有更好的解决方案吗?

根据提供的答案建议使用 awk csv 解析器,我能够得到解决方案:

cat Buildings.csv | awk -f csv.awk | awk -F" -> 2|"  '{print $2}' | awk -F"|" '{print $2","$3}' | sort | uniq > floors.csv 

我们想使用 csv awk 程序,然后从那里我想使用“ -> 2|”这是基于 csv awk 程序的格式化。那里的 print $2 仅打印 csv 解析的内容,这是因为程序打印原始行,后跟“ -> #”,其中 # 是从 csv 解析的计数。 (即列。)从那里我可以将这个 awk csv 结果拆分为“|” whcih 是它替换逗号的内容。然后排序、uniq 并通过管道输出到文件并完成!

感谢您的帮助。

csv awk
8个回答
59
投票
gawk -vFPAT='[^,]*|"[^"]*"' '{print $1 "," $3}' | sort | uniq

这是一个很棒的 GNU Awk 4 扩展,您可以在其中定义字段模式而不是字段分隔符模式。对 CSV 有奇效。 (文档

预计到达时间(感谢 mitchus): 要删除周围的引号,

gsub("^\"|\"$","",$3)
;如果有更多字段需要以这种方式处理,只需循环遍历它们即可。
请注意,这种简单的方法不能容忍格式错误的输入,也不能容忍引号之间的某些可能的特殊字符 - 覆盖所有这些将超出整洁的单行代码的范围。


12
投票
$3

获得的额外输出来自演示代码。您的目的是使用脚本中的函数来进行解析,然后按照您想要的方式输出。


csv.awk

的末尾是

csv.awk
循环,它演示了其中一个功能。正是该代码输出了
{ ... }

相反,只需调用解析函数并执行

-> 2|

即可。


这部分代码将如下所示:

print csv[1], csv[2]

将其另存为 
{ num_fields = parse_csv($0, csv, ",", "\"", "\"", "\\n", 1); if (num_fields < 0) { printf "ERROR: %s (%d) -> %s\n", csverr, num_fields, $0; } else { # printf "%s -> ", $0; # printf "%s", num_fields; # for (i = 0;i < num_fields;i++) { # printf "|%s", csv[i]; # } # printf "|\n"; print csv[1], csv[2] } }

(例如)。


your_script


并且

chmod +x your_script

是不必要的。另外,您可以做

cat
而不是
sort -u

您的命令将如下所示:

sort | uniq



7
投票

./yourscript Buildings.csv | sort -u > floors.csv

也就是说,首先用“((”替换左引号,用“))”替换右引号,然后用“whateverwhatever”替换“((“whatever,whatever”))”,然后更改“((”的所有剩余实例)和“))”回到“。


6
投票

http://lorance.freeshell.org/csv/


3
投票

decommaize () { cat $1 | sed 's/"[^"]*"/"((&))"/g' | sed 's/\(\"((\"\)\([^",]*\)\(,\)\([^",]*\)\(\"))\"\)/"\2\4"/g' | sed 's/"(("/"/g' | sed 's/"))"/"/g' > $2 }

这个 gawk 脚本 (replace-comma.awk) 的作用是:

0,"00BDF","AIRPORT TEST " 0,0,"BRICKER HALL<comma> JOHN W "

这使用了 gawk 功能,将实际的记录分隔符捕获到名为 
BEGIN { RS = "(.)" } RT == "\x022" { inside++; } { if (inside % 2 && RT == ",") printf("<comma>"); else printf(RT); }

的变量中。它将每个字符拆分为一条记录,当我们阅读记录时,我们将引号 (

RT
) 中遇到的逗号替换为
\x022

FPAT

解决方案在一种特殊情况下失败,即您同时有转义引号和引号内的逗号,但此解决方案在所有情况下都有效,即 <comma>

作为一行文字,方便复制粘贴:

§ echo '"Adams, John ""Big Foot""",1' | gawk -vFPAT='[^,]*|"[^"]*"' '{ print $1 }' "Adams, John " § echo '"Adams, John ""Big Foot""",1' | gawk -f replace-comma.awk | gawk -F, '{ print $1; }' "Adams<comma> John ""Big Foot""",1



2
投票

gawk 'BEGIN { RS = "(.)" } RT == "\x022" { inside++; } { if (inside % 2 && RT == ",") printf("<comma>"); else printf(RT); }'

并且 cut 可能比 awk 更容易:

csvquote Buildings.csv | awk -F, '{print $1","$2}' | sort | uniq | csvquote -u > Floors.csv

您可以在此处找到 csvquote 代码:
https://github.com/dbro/csvquote


2
投票
csvquote Buildings.csv | cut -d, -f1,2 | sort | uniq | csvquote -u > Floors.csv

)是专门为处理这种奇怪的情况而构建的。


Text::CSV_XS


输入行被分割成数组

perl -MText::CSV_XS -lne 'BEGIN{$csv=Text::CSV_XS->new()} if($csv->parse($_)){ @f=$csv->fields(); print "$f[0],$f[1]" }' file

字段 1 是 
@f
,因为 Perl 从 0 开始索引

输出:

$f[0]

我在此处的答案中提供了更多关于
u_floor,u_room 0,00BDF 0,0 0,3 0,5 0,6 0,7 0,8 0,9 0,19 0,20 0,21 0,25 0,27 0,29 0,35 0,45 0,59 0,60 0,61 0,63 0,0006M 0,0008A 0,0008B 0,0008C 0,0008D 0,0008E 0,0008F 0,0008G 0,0008H

的解释:

使用gawk解析csv文件


0
投票

Text::CSV_XS

$ lsb_release -a | grep ^Description
Description:    Ubuntu 20.04.2
$ awk --version                                                                          
GNU Awk 5.3.0, API 4.0, PMA Avon 8-g1, (GNU MPFR 4.2.1, GNU MP 6.3.0)
$ cat test

0,"00BDF","AIRPORT TEST            "
0,0,"BRICKER HALL, JOHN W    "
0,3,"BRICKER HALL, JOHN W    "
0,5,"BRICKER HALL, JOHN W    " $

机场测试

布里克·霍尔,约翰·W

布里克·霍尔,约翰·W
布里克·霍尔,约翰·W
...

$ awk --csv '{print $3}' test

© www.soinside.com 2019 - 2024. All rights reserved.