【问题标题】:find and delete lines matching a condition查找和删除符合条件的行
【发布时间】:2012-06-24 17:16:17
【问题描述】:

我有三个不同的文件,其中包含数字列。这些文件非常大(其中有 50,000,000+ 行)

例如,数据格式是这样的

1.2 22.333 10002.3432 223.2111
50.2166 2.873 15402.3432 322.1
.
.
.

对于每个文件(file1、file2 和 file3),我需要执行以下操作:

文件1 查找包含任意数字 x

文件2 找到包含任何数字 x >=1800 的行并从 file2 中删除这些行

文件3 找到包含任意数字 1000

我对 REGEX 的了解不足以弄清楚如何快速实现这一点。非常感谢任何帮助。

【问题讨论】:

  • 比较数字通常不是正则表达式的任务。
  • sed 和正则表达式可能无济于事,因为它们不懂数字。 awk 可能会这样做,但我会推荐 perl 来完成这项任务。
  • +1 感谢大家这么快就打败了我……伙计,在这里很难回答新问题。大声笑
  • 您真的是要从所有文件中删除 x == 1800 或 x == 1000 吗?或者你说的是<=,而你真正的意思是<?例如。如果 x = 1000,那么它是 = 1000。

标签: linux perl bash sed awk


【解决方案1】:

正如其他人在 cmets 中提到的那样,正则表达式在这种情况下并不理想。

这是使用awk 的一种方法:

awk '{for (i=1;i<=NF;i++) {if ($i<=1000) next}; if (NF) print}' file1 > new1

解析file 并禁止任何包含数字&lt;= 1000(和空行)的行。然后将输出通过管道传输到新文件。

对于file2file3,只需更改相关 if 语句中的条件以符合您的要求。


这里有一个简单的解释:

         This is repeated for each line in the input file
                                |
      -------------------------------------------------------
     /                                                       \
awk '{for (i=1;i<=NF;i++) {if ($i<=1000) next}; if (NF) print}'
      ------------------   ------------------   -------------
             |                     |                  |
     for each field/column         |                  |
                                   |                  |
                      If condition is met, skip       |
                             this line                |
                                                      |
                                          otherwise, if the line is
                                          not empty (number of fields != 0)
                                          print out the whole line.

【讨论】:

  • 哪一部分抑制了空行?这是第二个 if 语句吗?
  • @regex99 “if (NF)”位。
  • 很好的解释。非常感谢。
【解决方案2】:

输入文件“sample”在哪里:

500 500 500
1000 1000 1000
2000 2000 2000
3000 3000 3000

剥离x &lt;= 1000:

$ awk '{ for (i=1; i<=NF; i++) { if ($i <= 1000) next } print }' < sample
2000 2000 2000
3000 3000 3000

剥离x &gt;= 1800:

$ awk '{ for (i=1; i<=NF; i++) { if ($i >= 1800) next } print }' < sample
500 500 500
1000 1000 1000

剥离1000 &lt;= x &lt;= 1800:

$ awk '{ for (i=1; i<=NF; i++) { if (1000 <= $i && $i <= 1800) next } print }' < sample
500 500 500
2000 2000 2000
3000 3000 3000

【讨论】:

  • 啊哈……我们似乎想出了相同的解决方案。 +1 击败我 20 秒 ;)
【解决方案3】:

这是一个相当短的 Perl 脚本,用于输出您的 FILE3:

#!/usr/bin/perl

use warnings;
use strict;

our $lower = 1000.0;
our $upper = 1800.0;

OUTER: while (<>) {
    $_ >= $lower && $_ < $upper and next OUTER for /(\S+)/g;
    print;
}

您可以针对 FILE1 和 FILE2 进行调整。

(无论好坏,我的脚本都包含基本的 Perl 习惯用法,尽管脚本很简洁,但如果您不了解 Perl,它几乎无法阅读。不过,这就是在 Perl 中完成的,您会使用的脚本语言喜欢学习,一个嫌疑人。)

【讨论】:

  • 不错,是的。具有所有 perl 功能的良好而简单的脚本。喜欢你的风格。
【解决方案4】:

类似下面的脚本应该适合你。

#!/usr/bin/perl
while(<>) {
    my $line = $_;
    foreach my $col (split ' ', $line){     #for each column
        unless ($col <= 1000) {
            print $line;
        }
        #add other statements for other files
    }
}

编辑 - 让代码更高效 感谢 TLP

【讨论】:

  • 不像其他人那么短,但可读性强,可以完成工作。
  • 您应该使用for my $col (split ' ', $line) 而不是使用索引。您应该使用split ' ',否则双空格会在您的列表中插入空字符串。在数组中存储 50,000,000 多个元素并打印它们会严重影响您的性能——您应该立即打印这些行。
  • 好电话,我想我错过了几件事。
猜你喜欢
  • 1970-01-01
  • 2019-06-21
  • 1970-01-01
  • 1970-01-01
  • 2020-08-06
  • 2019-08-31
  • 1970-01-01
  • 2018-07-23
相关资源
最近更新 更多