从制表符分隔的文件中删除缺少值的行

从制表符分隔的文件中删除缺少值的行

我有一个很大的(~900MB)制表符分隔的文本文件,我将在下游程序中处理该文件。我需要删除任何缺少值的行。每行都有正确的列数(因此缺失值将对应于 2 个选项卡)。

注意:我的实际数据有大约 200 万行和 80-300 列。可能的字符包括 az AZ 0-9 -(连字符)_(下划线)和制表符(分隔)。文件中没有空格或特殊字符。

我对这种脚本编写很陌生,因此对所提供的任何代码的解释将不胜感激。我通常使用 R,但我的文件大小已经超出了 R 的数据操作功能。

如何在终端(或在 shell 脚本中)删除文件中缺少值的行(例如使用sed)?

输入文件示例:

Col1    Col2    Col3
A        B        C
D                 F
G        H        I
J        K        

输出文件示例:

Col1    Col2    Col3
A        B        C
G        H        I 

答案1

awk

awk -F"\t" '$1!=""&&$2!=""&&$3!=""' file

其实它就这么简单。

  • awk\t在用标志指定的字段分隔符选项卡处分割输入-F。当您的内容字段中没有空格时,也可以省略此选项。
  • $1!=""&&...是一个条件。当此条件成立时,awk只需打印该行。您也可以写'$1!=""&&$2!=""&&$3!=""{print}',但这不是必需的。当未给出任何操作时,awks 默认行为是打印该行。在这里,当 fields $1$2$3all 不为空时,即前 3 个字段具有值时,该条件为真。

要写入另一个文件,请使用以下命令:

awk -F"\t" '$1!=""&&$2!=""&&$3!=""' input_file >output_file

编辑:对于未定义的列数,您可以使用此命令awk,它会检查行中的每个字段:

awk -F"\t" '{for(i=1;i<=NF;i++){if($i==""){next}}}1' file

答案2

...要使以下任何一项发挥作用,您必须首先执行...

t=$(printf \\t)          ### because it's hard to demo CTRL+V TAB 

...现在,使用 POSIX grep...

grep -Ev "^$t+|$t($t|$)"     <in >out

grep将选择与模式不匹配的行 - 它使用| 或者元字符来表示^行头选项卡,或两个连续的选项卡,或$行尾选项卡 - 据我所知,这是唯一可能的失败情况。

如果没有-v否定开关,它可能是:

grep -E "([^$t]+$t){2}[^$t]" <in >out

...指定字符类中一个或多个字符的模式组的{出现计数,这些字符不是制表符后跟一个制表符。}()+[]^


...或者使用 POSIX sed...

sed -ne"s/[^$t][^$t]*/&/3p"  <in >out

...或者...

sed -ne"s/[^$t]\{1,\}/&/3p"  <in >out

...或者使用 GNU 或 BSDsed系统...

sed -Ene"s/[^$t]+/&/3p"      <in >out

...where ot 默认情况下sed-n打印任何行,除非它可以s///代替&至少一个[^非制表]符的最长可能序列的行上的第三次出现。


(为了可移植性,应该首选使用文字制表符。这个答案的原始版本使用\反斜杠转义符,它是不是有帮助。在字符类中使用\反斜杠转义肯定会限制代码的适用性。)[]

答案3

如果您的字段不能包含空格,则空字段意味着制表符作为第一个字符 ( ^\t)、制表符作为最后一个字符 ( \t$) 或两个连续的制表符 ( \t\t)。因此,您可以过滤掉包含以下任何内容的行:

grep -Ev $'^\t|\t\t|\t$' file

如果可以有空格,事情就会变得更加复杂。如果您的字段可以以空格开头,请使用它(它认为只有空格的字段为空):

grep -Pv '\t\s*(\t|$)|\t$|^\t' file

该更改会过滤掉与制表符匹配的行,后跟 0 个或多个空格,然后是另一个制表符或行尾。

如果最后一个字段只包含空格,这也会失败。为了避免这种情况,请使用perl-F选项-a将输入拆分到@F数组中,告诉它打印,除非其中一个字段为空 ( /^$/):

perl -F'\t' -lane 'print unless grep{/^$/} @F' file

答案4

你可以尝试这样的事情:

grep "^[a-zA-Z0-9]\+[[:space:]][a-zA-Z0-9]\+[[:space:]][a-zA-Z0-9]\+$" input_file > output_file

的目的grep是(或不)在一个或多个文件中查找与给定模式匹配的字符串。此处,该模式[a-zA-Z0-9]\+匹配一​​个或多个字母数字字符,后跟空格或制表符。行的开头与 匹配^,而$表示行的结尾。如果列中使用了其他字符,则应将它们添加到上面的字符类中。最后,>将匹配的输出重定向到输出文件。

另请查看下面 @terdon 的评论,了解潜在的陷阱和替代解决方案。请注意,如果您在 Linux/Unix 环境中工作,那么它的用处grep远远超出了这个特定的解决方案。

相关内容