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

Gai*_*tus 5 sed shell-script text-processing csv

我有一个很大的(~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        
Run Code Online (Sandbox Code Playgroud)

示例输出文件:

Col1    Col2    Col3
A        B        C
G        H        I 
Run Code Online (Sandbox Code Playgroud)

cha*_*aos 9

与awk:

awk -F"\t" '$1!=""&&$2!=""&&$3!=""' file
Run Code Online (Sandbox Code Playgroud)

其实就是这么简单。

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

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

awk -F"\t" '$1!=""&&$2!=""&&$3!=""' input_file >output_file
Run Code Online (Sandbox Code Playgroud)

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

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


mik*_*erv 5

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

t=$(printf \\t)          ### because it's hard to demo CTRL+V TAB 
Run Code Online (Sandbox Code Playgroud)

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

grep -Ev "^$t+|$t($t|$)"     <in >out
Run Code Online (Sandbox Code Playgroud)

grep将选择与模式不匹配的行 - 使用| 或元字符来表示^行首制表符,或两个连续制表符,或$行尾制表符 - 这是唯一可能的失败情况与 I可以告诉。

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

grep -E "([^$t]+$t){2}[^$t]" <in >out
Run Code Online (Sandbox Code Playgroud)

...指定的{出现计数}的(图案组)的+在所述一个或多个字符[类]的字符的其是^未接片,随后一个选项卡。


...或使用 POSIX sed...

sed -ne"s/[^$t][^$t]*/&/3p"  <in >out
Run Code Online (Sandbox Code Playgroud)

...或者...

sed -ne"s/[^$t]\{1,\}/&/3p"  <in >out
Run Code Online (Sandbox Code Playgroud)

...或带有 GNU 或 BSD seds ...

sed -Ene"s/[^$t]+/&/3p"      <in >out
Run Code Online (Sandbox Code Playgroud)

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


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


ter*_*don 4

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

grep -Ev $'^\t|\t\t|\t$' file
Run Code Online (Sandbox Code Playgroud)

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

grep -Pv '\t\s*(\t|$)|\t$|^\t' file
Run Code Online (Sandbox Code Playgroud)

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

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

perl -F'\t' -lane 'print unless grep{/^$/} @F' file
Run Code Online (Sandbox Code Playgroud)

  • @GaiusAugustus perl 不会更改原始文件,您需要将其输出重定向到新文件或使用“-i”。但是,现在您已经指定数据不能包含空格,您可以简单地使用 `time grep -Ev $'^\t|\t\t|\t$' file1&gt; out4` ,它的速度快得惊人并且有效在任意数量的字段上。 (2认同)