只保留包含确切数量的分隔符的行

Mir*_*abo 10 filter csv

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

cuo*_*glm 23

另一个POSIX:

awk -F , 'NF == 11' <file
Run Code Online (Sandbox Code Playgroud)

如果该行有 10 个逗号,则该行将有 11 个字段。所以我们简单地awk使用,作为字段分隔符。如果字段数为 11,则条件NF == 11为真,awk则执行默认操作print $0

  • 这实际上是我在这个问题上想到的第一件事。我认为这是矫枉过正,但查看代码......它确实更清晰。为了他人的利益:`-F` 设置字段分隔符,`NF` 是指给定行中的字段数。由于没有代码块“{statement}”附加到条件“NF == 11”,因此默认操作是打印该行。(@cuonglm,如果你愿意,可以随意加入这个解释。) (5认同)
  • +1:非常优雅和可读的解决方案,也非常通用。例如,我可以用 `awk -F , 'NF != 11' &lt;file` 找到所有格式错误的行 (5认同)

Ste*_*itt 8

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

egrep "^([^,]*,){10}[^,]*$" file.csv
Run Code Online (Sandbox Code Playgroud)

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

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

grep -xE "([^,]*,){10}[^,]*" file.csv
Run Code Online (Sandbox Code Playgroud)

不过,这比cuonglmawk解决方案效率低;对于包含大约 10 个逗号的行,后者在我的系统上通常快 6 倍。更长的线路将导致巨大的减速。


Wil*_*ard 5

最简单的grep代码可以工作:

grep -xE '([^,]*,){10}[^,]*'
Run Code Online (Sandbox Code Playgroud)

解释:

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

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

括号用于分组,{10}after 表示括号内的模式行中必须正好有十个匹配项。

[^,]是一个字符类——例如,[c-f]将匹配 a c、a d、ane或 an 的任何单个字符f,并[^A-Z]匹配任何不是大写字母的单个字符。所以[^,]匹配除逗号之外的任何单个字符。

*之后的字符类的意思是“零个或更多的这些。”

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


mik*_*erv 5

sed -ne's/,//11;t' -e's/,/&/10p' <in >out
Run Code Online (Sandbox Code Playgroud)

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

显然我之前回答过这个问题......这是一个我抄袭的问题,正在寻找恰好 4 次出现的某种模式:

您可以[num]通过将 seds///替换命令添加[num]到命令中来定位模式的第 th 次出现。当您t对成功替换进行 est 并且未指定目标:标签时,test 会从脚本中分支出来。这意味着您所要做的就是测试一个s///5或多个逗号,然后打印剩下的。

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

sed -ne 's|,||5;t' -e 's||,|4p'
Run Code Online (Sandbox Code Playgroud)

...只需将,一行中出现的第 4 次替换为它自己,并将您的prints///附加到替换标志上。因为任何匹配,5 次或更多次的行都已经被修剪,所以包含 4 个,匹配项的行包含4 个。