awk处理带引号的字段中包含逗号的CSV文件

众所周知,CSV是以逗号(“,”)分割的文本文件,而号称“Linux三剑客”之一的awk则是处理此类文件的重要而有效的工具。笔者在利用此工具处理一个比较大的CSV文件的时候,碰到了一个也许会比较常见,经过查找,最后比较有效地解决了这个问题。加之,这个问题来自实际的数据来源。因此,记录、整理并发布出来,供大家参考和讨论。

一、问题的描述

需要处理的CSV文件是一个“啤酒评级”(Beer Ratings)的结果,该文件来自于RateBeer.com(https://www.ratebeer.com/),大小118M,总记录数将近160万,记录内容包括啤酒厂商编号、啤酒厂商名称、啤酒名称、啤酒类别、酒精度数等13项内容。

我们的任务是:从这个文件中取出厂商名称、啤酒名称、酒精度数等5项内容,然后,再找出酒精度数最高的啤酒名称和厂商名称以及记录所在的行数。完成这个任务的第一步是简单的,那就是按照awk的要求找到对应的字段编号,只要检视一下该文件第一行就可以了,得到要取出的列数分别是:$2,$4,$8,$11,$12。问题难度在于某些记录是这样的:

281,"Tsingtao Brewery Co., Ltd.",1318061806,3,3.5,3,crusian,Foreign / Export Stout,2,2,Tsingtao Stout,7.5,52200

如果简单地用-F”,”来分割数据,就会导致后面的数据错位,而且数据量巨大;同时,其他字段也许还会有这种形式出现。因此,必须找到一个通用而有效的解决方案。

二、解决方案

如果这些数据的每个部分都是用双引号(")引起来的,那么,就可以简单地用

-F'\"*,\"*'

进行处理。但是,这里只有部分数据用到了双引号,就必须要使用到awk的另外一个参数FPAT。按照手册中对此参数的解释是:

FS:定义了参数不是什么;而FPAT则定义了参数是什么

针对这种情况,FPAT可以写成一个正则表达式

FPAT = "([^,]+)|(\"[^\"]+\")"

我们对上面的那行数据来进行一个简单的测试。首先,把上面的那个数据放到一个CSV文件中,然后再编写一个.awk的文件(这个后缀是任何字符都可以,只要你自己能明白就好),内容如下:

BEGIN {
    FPAT = "([^,]+)|(\"[^\"]+\")"
}

{
    print "NF = ", NF
    for (i = 1; i <= NF; i++) {
        printf("$%d = <%s>\n", i, $i)
    }
}

然后,进行测试

awk -f ftap_test.awk ftap_test.csv

我们就会看到如下结果

在这里插入图片描述

这样就近乎完美地解决了这个问题。

按照这个思路,我们就可以着手解决“啤酒评级”那个文件了,为了避免以后再出现这个问题,因此,在结果输出的时候,用tab(\t)做了分隔符。

awk 'BEGIN{OFS="\t";FPAT="([^,]*)|(\"[^\"]+\")"} {print $2,$4,$8,$11,$12}' reviews.csv > reviews.tsv

这里有一点差异是,FPAT参数的第一个“+”被改成了“*”,是因为,在reviews.csv文件中,有些字段是缺失的,如果用“+”的话,会导致字段数量变少,引致新的问题。这也就是为什么上文说“近乎完美”的原因。

现在我们就可以来寻找一下“酒精度数”最高的那款啤酒了。这里之所以要加上一个if语句,就是因为有空数据的存在,会导致比较的时候出现问题。

awk -F"\t" 'NR > 1 && $5 > maxabv { if ($5~/[0-9]+/){maxabv = $5; brewery = $1; name = $4; num = NR} } END { print maxabv, "##",  brewery, "##", name, "##", num }' reviews.tsv

结论应该非常出乎很多人的意料吧

57.7 ## Schorschbräu ## Schorschbräu Schorschbock 57% ## 12921

居然有将近60度的啤酒?!你是不是有兴趣找点来喝喝,然后和我分享一下感受呢。哈哈哈哈!

三、几句题外话

需要注意的是,FPAT参数是会覆盖FS参数的。而且awk中还有其他参数可以来完成这个目标,希望大家能一起来探索和分享。另外,这样处理后的字符串是带有双引号的,也可以在得到结果的时候,用substr函数进行处理。再有就是如果有两个双引号的话,这个应该是无法处理的,希望大家在使用的时候多加注意。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值