我有一个巨大的csv文件,其中10个字段用逗号分隔。不幸的是,某些行格式错误,并且不完全包含10个逗号(当我想将文件读入R时,这会引起一些问题)。如何仅过滤出恰好包含10个逗号的行?
grep
答案没有一个可以接受的答案……
我有一个巨大的csv文件,其中10个字段用逗号分隔。不幸的是,某些行格式错误,并且不完全包含10个逗号(当我想将文件读入R时,这会引起一些问题)。如何仅过滤出恰好包含10个逗号的行?
grep
答案没有一个可以接受的答案……
Answers:
另一个POSIX:
awk -F , 'NF == 11' <file
如果该行有10个逗号,则该行中将有11个字段。因此,我们只需让awk
使用,
作为字段分隔符。如果字段数为11,则条件NF == 11
为true,awk
然后执行默认操作print $0
。
-F
设置字段分隔符并NF
引用给定行中的字段数。由于没有代码块{statement}
附加到该条件NF == 11
,因此默认操作是打印该行。(@cuonglm,如果愿意,可以随时添加此说明。)
awk -F , 'NF != 11' <file
-
或开头的文件-
。
使用egrep
(或grep -E
在POSIX中):
egrep "^([^,]*,){10}[^,]*$" file.csv
这会过滤掉不包含10个逗号的所有内容:它匹配整行(^
在开头和$
结尾),完全包含{10}
序列“除','以外的任何数量的字符,后跟单个','”的十个重复()。 (([^,]*,)
),然后再加上除“,”([^,]*
)以外的任意数量的字符。
您还可以使用-x
参数来删除锚点:
grep -xE "([^,]*,){10}[^,]*" file.csv
但是,这比cuonglm的awk
解决方案效率低。对于大约10个逗号的行,后者在我的系统上通常快六倍。更长的线会导致巨大的减速。
最有效的grep
代码:
grep -xE '([^,]*,){10}[^,]*'
说明:
-x
确保模式必须与整行匹配,而不仅仅是部分匹配。这很重要,因此您不要匹配超过10个逗号的行。
-E
表示“扩展的正则表达式”,这可以减少正则表达式中的反斜杠转义。
括号用于分组,{10}
其后意味着括号中的模式行中必须恰好有十个匹配项。
[^,]
是一个字符类,例如,[c-f]
将与c
,a d
,an e
或an的任何单个字符匹配f
,并且[^A-Z]
将与非大写字母的任何单个字符匹配。因此[^,]
匹配除逗号以外的任何单个字符。
*
字符类的after表示“其中零个或多个”。
因此,正则表达式部分的([^,]*,)
意思是“除逗号以外的任何字符任何次数(包括零次),后跟一个逗号”,并{10}
指定其中的10个。然后[^,]*
将其余的非逗号字符匹配到该行的末尾。
sed -ne's/,//11;t' -e's/,/&/10p' <in >out
首先,用11个或更多逗号分隔出任何行,然后打印仅匹配10个逗号的行。
显然,我之前已经回答了这个问题……这是我的一个抄袭问题,该问题恰好出现了某种模式的4次出现:
您可以
[num]
通过将seds///
ubstitution命令添加[num]
到命令中来确定模式的出现。当您t
估算成功替换并且不指定目标:
标签时,t
est分支出脚本。这意味着您需要做的就是测试一个s///5
或多个逗号,然后打印剩余的内容。或者,至少,它可以处理超过您的最大值4的行。显然,您也有最低要求。幸运的是,这很简单:
sed -ne 's|,||5;t' -e 's||,|4p'
...只需
,
用自身替换一行中的第4次出现,然后p
将s///
lint标记在ubstitution标志上。因为匹配,
5次或更多次的任何行都已被修剪,所以包含4个,
匹配项的行仅包含4个。
s/hello/world/2
为s//world/2
,那么GNU sed可以正常工作。sed
从祖传/usr/5bin/posix/sed
遗物中拿出两个,加高segfault,/usr/5bin/sed
进入不定式循环。
sed
和的讨论awk
(在注释中)—我喜欢并赞成此答案,但请注意,接受的awk
答案的翻译为:“打印11行的行”,而该sed
答案的翻译为:“尝试删除第11个逗号;如果失败,请跳至下一行。尝试用其自身替换第10个逗号;如果成功,请打印行。” 的awk
回答给人以计算机指令只是你会表达他们在英语的方式。(awk
适用于基于字段的数据。)
这是一种Perl方式:
perl -F, -ane 'print if $#F==10'
将-n
导致perl
按行读入其输入文件中的行并执行给定的脚本-e
,每行。在-a
自动分割轮流:每个输入线将在由给定的值进行分割-F
(在此为逗号)并保存为阵列@F
。
的$#F
(或者更一般地$#array
),是阵列的最高索引@F
。因为数组在开始0
,与11个字段的线将具有@F
的10
。因此,如果脚本恰好具有11个字段,则它将打印该行。
print if @F==11
在标量上下文中作为数组返回元素数。
如果字段可以包含逗号或换行符,则您的代码需要了解 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"
我想到目前为止,大多数解决方案都将丢弃第二行和第四行。
sed
这里所做的一样)扫描比寻找的匹配项还要远的匹配项,尽管此问题可以。您不应该关闭此窗口。