我正在使用AWK来计算CSV文件中一列的总和。数据格式类似:

id, name, value
1, foo, 17
2, bar, 76
3, "I am the, question", 99

我正在使用此尴尬脚本来计算总和:

awk -F, '{sum+=$3} END {print sum}'

名称字段中的某些值包含逗号,这打破了我的尴尬脚本。我的问题是:Awk可以解决这个问题吗?如果是,我该怎么做?

谢谢你。

有帮助吗?

解决方案

您在尴尬中写下一个函数,如下所示:

$ awk 'func isnum(x){return(x==x+0)}BEGIN{print isnum("hello"),isnum("-42")}'
0 1

您可以在脚本中合并此功能,并检查第三字段是否是数字。如果不是数字,则转到第4个字段,如果第四字段Inturn不是数字为第5个……直到您达到数字值。循环可能会在这里提供帮助,并将其添加到总和中。

其他提示

一种方法 GNU awkFPAT

awk 'BEGIN { FPAT = "([^, ]+)|(\"[^\"]+\")" } { sum+=$3 } END { print sum }' file.txt

结果:

192

您最好使用Text :: CSV在Perl中进行操作,因为这是一个快速而强大的解决方案。

您可以通过使用我编写的名为CSVQuote的小脚本来帮助尴尬地处理包含逗号(或新线)的数据字段。它用非打印字符代替了引用字段中的有问题的逗号。如果需要的话,您可以稍后再恢复这些逗号 - 但是在这种情况下,您不需要。

这是命令:

csvquote inputfile.csv | awk -F, '{sum+=$3} END {print sum}'

https://github.com/dbro/csvquote 对于代码

我在用

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

用gawk定义田野。我发现当字段为null时,这将无法识别正确的字段数。因为“+”需要至少1个字符。我将其更改为:

`FPAT="([^,]*)|(\"[^\"]*\")"`

并更换 "+""*". 。它正常工作。

我还发现GNU AWK用户指南也有此问题。https://www.gnu.org/software/gawk/manual/html_node/splitting-by-content.html

对于简单的输入文件,您可以只编写一个小函数将引号之外的所有真实FSS转换为其他值(我选择了RS,因为记录分隔符不能成为记录的一部分),然后将其使用为FS,例如:

$ cat decsv.awk
BEGIN{ fs=FS; FS=RS }

{
   decsv()

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

function decsv(         curr,head,tail)
{
   tail = $0
   while ( match(tail,/"[^"]+"/) ) {
       head = substr(tail, 1, RSTART-1);
       gsub(fs,RS,head)
       curr = curr head substr(tail, RSTART, RLENGTH)
       tail = substr(tail, RSTART + RLENGTH)
   }
   gsub(fs,RS,tail)
   $0 = curr tail
}

$ cat file
id, name, value
1, foo, 17
2, bar, 76
3, "I am the, question", 99

$ awk -F", " -f decsv.awk file
Record 1, Field 1 is <id>
Record 1, Field 2 is <name>
Record 1, Field 3 is <value>

Record 2, Field 1 is <1>
Record 2, Field 2 is <foo>
Record 2, Field 3 is <17>

Record 3, Field 1 is <2>
Record 3, Field 2 is <bar>
Record 3, Field 3 is <76>

Record 4, Field 1 is <3>
Record 4, Field 2 is <"I am the, question">
Record 4, Field 3 is <99>

只有当您必须处理嵌入式新线和嵌入式逃脱的引号时,它才会变得复杂,即使那样,它也不是太难了,这一切都已经完成了...

有效地使用AWK进行有效解析CSV的最强大方法是什么? 了解更多信息。

您总是可以从源头解决问题。在名称字段周围引号,就像“我是,问题”的字段。这要比花费时间编码解决方案要容易得多。

更新(按照丹尼斯的要求)。一个简单的例子

$ s='id, "name1,name2", value 1, foo, 17 2, bar, 76 3, "I am the, question", 99'

$ echo $s|awk -F'"' '{ for(i=1;i<=NF;i+=2) print $i}'
id,
, value 1, foo, 17 2, bar, 76 3,
, 99

$ echo $s|awk -F'"' '{ for(i=2;i<=NF;i+=2) print $i}'
name1,name2
I am the, question

如您所见,通过将定界符设置为双引号,属于“引号”的字段始终在数字上。由于OP没有修改源数据的奢侈,因此此方法不适合他。

如果您确定“值”列始终是最后一列:

awk -F, '{sum+=$NF} END {print sum}'

NF表示字段数,因此$ nf是最后一列

本文确实帮助我解决了同样的数据字段问题。大多数CSV都会在其中带有空间或逗号的字段上提到报价。除非您将其过滤掉,否则这会使场数弄乱。

如果您需要那些包含垃圾的字段中的数据,则不适合您。 ghostdog74 提供了答案,该答案最终清空了该字段,但最终保持了总字段计数,这是保持数据输出一致的关键。我不喜欢该解决方案如何引入新线条。这是我使用的该解决方案的版本。拳头三个领域从未在数据中遇到这个问题。包含客户名称的第四个字段经常这样做,但我需要该数据。其余的领域表现出我可能会丢弃的问题,因为我的报告输出不需要它。因此,我非常具体地将第四田的垃圾销毁,然后删除前两个引号。然后我运用什么 ghostdog74赋予它们内部有逗号的其余字段 - 这也取消了报价,但我使用 printf将数据保留在单个记录中。我从85个字段开始,最终从我8000多个混乱的数据行中获得了85个字段。一个完美的分数!

grep -i $1 $dbfile | sed 's/\, Inc.//;s/, LLC.//;s/, LLC//;s/, Ltd.//;s/\"//;s/\"//' | awk -F'"' '{ for(i=1;i<=NF;i+=2) printf ($i);printf ("\n")}' > $tmpfile

当然,用逗号在其中清空域但也保持记录的解决方案是:

awk -F'"' '{ for(i=1;i<=NF;i+=2) printf ($i);printf ("\n")}

感谢GhostDog74提供的出色解决方案!

netsguy256/

FPAT是优雅的解决方案,因为它可以在引号问题中处理可怕的逗号,但是要在上一列中总结一列,无论前面的分隔符数量如何,$ nf都可以很好地工作:

awk -F"," '{sum+=$NF} END {print sum}'

要访问第二列到最后一列,您将使用以下方式:

awk -F"," '{sum+=$(NF-1)} END {print sum}'

完全刚刚起步的CSV解析器,例如Perl Text::CSV_XS 是专为处理这种怪异的目的。

perl -MText::CSV_XS -lne 'BEGIN{$csv=Text::CSV_XS->new({allow_whitespace => 1})} if($csv->parse($_)){@f=$csv->fields();$sum+=$f[2]} END{print $sum}' file

allow_whitespace 由于输入数据周围具有隔离仪分离器的空间,因此需要。非常古老的版本的 Text::CSV_XS 可能不支持此选项。

我提供了更多的解释 Text::CSV_XS 在我的回答中: 使用gawk解析CSV文件

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top