仅保留包含定界符确切数目的行


9

我有一个巨大的csv文件,其中10个字段用逗号分隔。不幸的是,某些行格式错误,并且不完全包含10个逗号(当我想将文件读入R时,这会引起一些问题)。如何仅过滤出恰好包含10个逗号的行?


1
您的问题和链接的问题不是同一问题。您问如何处理不超过或少于一定数量的匹配项的行,而该问题仅需要最小匹配数。现实情况是,这个问题更容易回答-它不需要完全扫描一行,或者(至少,就像sed这里所做的一样)扫描比寻找的匹配项还要远的匹配项,尽管此问题可以。您不应该关闭此窗口。
mikeserv '16

1
实际上,往近看,询问者确实希望火柴多多少少。该问题需要一个新标题。但是grep答案没有一个可以接受的答案……
mikeserv

Answers:


21

另一个POSIX:

awk -F , 'NF == 11' <file

如果该行有10个逗号,则该行中将有11个字段。因此,我们只需让awk使用,作为字段分隔符。如果字段数为11,则条件NF == 11为true,awk然后执行默认操作print $0


5
实际上,这是我想到的第一件事。我以为这太过分了,但是看一下代码……肯定更清楚了。为了他人的利益:-F设置字段分隔符并NF引用给定行中的字段数。由于没有代码块{statement}附加到该条件NF == 11,因此默认操作是打印该行。(@cuonglm,如果愿意,可以随时添加此说明。)
通配符

4
+1:非常优雅且易读的解决方案,也很通用。例如,我可以通过awk -F , 'NF != 11' <file
Miroslav Sabo

@gardenhead:很容易得到它,就像您在OP的评论中看到的那样。我有时会通过手机回答,因此很难添加详细说明。
cuonglm '16

1
@mikeserv:不,对不起,如果我让您感到困惑,那只是我的英语不好。您不能有11个带有1-9个逗号的字段。
cuonglm '16

1
@OlivierDulac:防止您以-或开头的文件-
cuonglm '16

8

使用egrep(或grep -E在POSIX中):

egrep "^([^,]*,){10}[^,]*$" file.csv

这会过滤掉不包含10个逗号的所有内容:它匹配整行(^在开头和$结尾),完全包含{10}序列“除','以外的任何数量的字符,后跟单个','”的十个重复()。 (([^,]*,)),然后再加上除“,”([^,]*)以外的任意数量的字符。

您还可以使用-x参数来删除锚点:

grep -xE "([^,]*,){10}[^,]*" file.csv

但是,这比cuonglmawk解决方案效率低。对于大约10个逗号的行,后者在我的系统上通常快六倍。更长的线会导致巨大的减速。


5

最有效的grep代码:

grep -xE '([^,]*,){10}[^,]*'

说明:

-x确保模式必须与行匹配,而不仅仅是部分匹配。这很重要,因此您不要匹配超过10个逗号的行。

-E 表示“扩展的正则表达式”,这可以减少正则表达式中的反斜杠转义。

括号用于分组,{10}其后意味着括号中的模式行中必须恰好有十个匹配项。

[^,]是一个字符类,例如,[c-f]将与c,a d,an e或an的任何单个字符匹配f,并且[^A-Z]将与非大写字母的任何单个字符匹配。因此[^,]匹配除逗号以外的任何单个字符。

*字符类的after表示“其中零个或多个”。

因此,正则表达式部分的([^,]*,)意思是“除逗号以外的任何字符任何次数(包括零次),后跟一个逗号”,并{10}指定其中的10个。然后[^,]*将其余的非逗号字符匹配到该行的末尾。


5
sed -ne's/,//11;t' -e's/,/&/10p' <in >out

首先,用11个或更多逗号分隔出任何行,然后打印仅匹配10个逗号的行。

显然,我之前已经回答了这个问题……这是的一个抄袭问题,该问题恰好出现了某种模式的4次出现:

您可以[num]通过将sed s///ubstitution命令添加[num]到命令中来确定模式的出现。当您t估算成功替换并且不指定目标:标签时,test分支出脚本。这意味着您需要做的就是测试一个s///5或多个逗号,然后打印剩余的内容。

或者,至少,它可以处理超过您的最大值4的行。显然,您也有最低要求。幸运的是,这很简单:

sed -ne 's|,||5;t' -e 's||,|4p'

...只需,用自身替换一行中的第4次出现,然后ps///lint标记在ubstitution标志上。因为匹配,5次或更多次的任何行都已被修剪,所以包含4个,匹配项的行包含4个。


1
@cuonglm-起初我实际上就是这样,但是人们总是告诉我我应该编写更具可读性的代码。因为我可以阅读别人认为不可读的东西,所以我不确定要保留什么和丢弃什么...?所以我把第二个逗号。
mikeserv '16

@cuonglm-您可以嘲笑我-它不会伤害我的感觉。我可以开个玩笑。如果你在嘲笑我,那会有点有趣。没关系-我不确定并想知道。我认为人们应该能够嘲笑自己。无论如何,我还是不明白!
mikeserv '16

哈哈,对,这是一个非常积极的想法。无论如何,与您聊天非常有趣,有时,您会紧张我的大脑。
cuonglm '16

有趣的是,在这个答案中,如果我替换s/hello/world/2s//world/2,那么GNU sed可以正常工作。sed从祖传/usr/5bin/posix/sed遗物中拿出两个,加高segfault,/usr/5bin/sed进入不定式循环。
cuonglm '16

@mikeserv,参考我们之前有关sed和的讨论awk(在注释中)—我喜欢并赞成此答案,但请注意,接受的awk答案的翻译为:“打印11行的行”,而该sed答案的翻译为:“尝试删除第11个逗号;如果失败,请跳至下一行。尝试用其自身替换第10个逗号;如果成功,请打印行。” 的awk回答给人以计算机指令只是你会表达他们在英语的方式。(awk适用于基于字段的数据。)
通配符

4

抛出一些短python

#!/usr/bin/env python2
with open('file.csv') as f:
    print '\n'.join(line for line in f if line.count(',') == 10)

这将读取每一行,并检查该行中的逗号数是否等于10 line.count(',') == 10,如果这样,将打印该行。


2

这是一种Perl方式:

perl -F, -ane 'print if $#F==10'

-n导致perl按行读入其输入文件中的行并执行给定的脚本-e,每行。在-a自动分割轮流:每个输入线将在由给定的值进行分割-F(在此为逗号)并保存为阵列@F

$#F(或者更一般地$#array),是阵列的最高索引@F。因为数组在开始0,与11个字段的线将具有@F10。因此,如果脚本恰好具有11个字段,则它将打印该行。


您也可以print if @F==11在标量上下文中作为数组返回元素数。
Sobrique '16

1

如果字段可以包含逗号或换行符,则您的代码需要了解 csv。示例(三列):

$ cat filter.csv
a,b,c
d,"e,f",g
1,2,3,4
one,two,"three
...continued"

$ cat filter.csv | python3 -c 'import sys, csv
> csv.writer(sys.stdout).writerows(
> row for row in csv.reader(sys.stdin) if len(row) == 3)
> '
a,b,c
d,"e,f",g
one,two,"three
...continued"

我想到目前为止,大多数解决方案都将丢弃第二行和第四行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.