【问题标题】:Finding the biggest number in a file (containing both numeric and non numeric elements) with Perl使用 Perl 查找文件中的最大数字(包含数字和非数字元素)
【发布时间】:2013-05-29 05:26:23
【问题描述】:

我真的需要 Perl 黑客的帮助。这看起来很简单,但我已经考虑了一个小时并没有提出任何解决方案。

假设我们有一个平面文件或日志文件,如下所示:

    2013-05-27T19:01:23 [INFO] item_id:1, start at Reader.pm line 23
    2013-05-27T19:01:29 [INFO] item_id:2, pause at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:1, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:1, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:1, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:1, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:3, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:3, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:3, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:5, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:5, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:5, start at Reader.pm line 23
    2013-05-27T19:01:30 [INFO] item_id:5, start at Reader.pm line 23
    (...)

我必须创建一个方法来找到最大的 item_id 编号,在本例中为 5,并将其定位在变量 $found 中。请注意,我们不知道先验是最大的数字,所以我不能使用 grep,因为我需要将“最大的数字(在这种情况下为 5)”作为输入。我们唯一的输入是文件的位置。你有什么建议?

【问题讨论】:

  • 有关信息,perl one 衬里和循环最快,大约 600 毫秒。循环程序为 700-800 毫秒。 max @ids 大约是 900 毫秒。 cut | sed | sort 是 1200 毫秒。 sed | sort 是迄今为止最慢的 3800 毫秒。
  • @Schwern 感谢您提供信息。顺便问一下,你会如何测试这样的任务?
  • use File::Temp; use Test::More; my $tmp = File::Temp->new; print $tmp $test_log_data; is max_item_id_in_log($tmp->filename), $expected_max_item_id; done_testing;

标签: perl search


【解决方案1】:

这个解决方案非常简单。它需要输入文件的名称作为命令行上的参数。

use strict;
use warnings;

my $found = 0;

while (<>) {
  next unless /item_id:(\d+)/;
  $found = $1 if $found < $1;
}

print "Found: $found";

输出

Found: 5

更新

如果你想要的只是值,那么这里有这个命令行版本

perl -ne "/item_id:(\d+)/ && $f<$1 and $f=$1; END{print $f}" data.txt

【讨论】:

  • @squiguy:你有没有像我说的那样给它一个文件名参数?
  • 我没有投反对票,我知道命令行需要一个文件名。
  • 可能是因为您说当 OP 要求它提供 5 时,您得到了 0 用于输出?如果是这种情况,请修复该问题。
  • @squiguy,您也可以通过 STDIN 提供输入
  • 我认为解决方案看起来是正确的,所以我只想指出这一点,以便人们投票。 +1
【解决方案2】:

List::Util 有一个 max() 函数,它将选择最大的数字。

use List::Util qw(max);

my @ids;
while(my $line = <$fh>) {
    my($id) = $line =~ /item_id:(\d+)/;
    push @ids, $id;
}

print max(@ids);

为了启迪,max 是一个非常简单的函数。

sub max {
    my $max;
    for my $num (@_) {
        $max = $num if $num > $max;
    }

    return $max;
}

如果您有大量的行,您可以在循环中进行最大值计算以避免存储列表。

my $max;
while(my $line = <$fh>) {
    my($id) = $line =~ /item_id:(\d+)/;
    $max = $id if $id > $max;
}

【讨论】:

  • @Schwern 谢谢我喜欢这个。该文件有 50 万行,所以我正在寻找一些简洁的东西。
  • @squiguy 我自己也想知道。使用数组有点浪费,但即使有 50 万行,它也不会影响性能或内存。一百万个整数的数组只有 4 兆。 max 可以在几毫秒内撕裂它们。所有费用都将用于读取文件。
  • “大量行”的解决方案似乎比List::Util 解决方案更直接;即使是小的输入,我也更喜欢它。
  • @squidguy,问:如何将两个数字相乘?这个答案的类似物:这是一个支持乘法的 peano 数字模块。 Factorial 5 消耗 100MB 的 RAM,并且在我的系统上运行需要十分钟才能完成,所以如果你正在处理疯狂的大数字,你可能想要勉强做明显更可取的事情
  • @JulianFondren 让我们保持头脑清醒。 max 解决方案花费 900 毫秒,while 循环花费 700 毫秒。 I/O 和解析是大部分成本。我展示了两种解决方案。在循环中进行数学运算是最快且最节省内存的,如果要解析和存储日志数据以用于其他目的,则取列表的最大值是最自然的。
【解决方案3】:

只需读入日志文件的每一行并使用正则表达式获取游戏 id,将游戏 id 初始化为第一个,并在获得更大的 id 时替换它。

use strict;
use warnings;

my $location = "file.txt";
open LOGFILE, $location;

my $first_line = 1;
my $max_id;

while (<LOGFILE>) {
    if (/item_id:(\d)+/) {
        if ($first_line) {
            $first_line = 0;
            $max_id = $1;
        } else {
            $max_id = $1 if ($1 > $max_id);
        }
    }
}

my $found = $max_id;
print "$found\n";

close LOGFILE;

【讨论】:

  • 是的。作为一个单行,并假设 ID 永远不会都是负数:perl -lne '$id = $1 if /item_id:(\d+)/ and $1 &gt; $id; END { print $id }' thefile
  • 如果要检查输入的第一行,请使用$. == 1
【解决方案4】:

作为 Perl 单行:

perl -lne '{$s{$1}++ if /item_id:(\d+)/} END{print ((sort keys %s)[-1])}' input

或者,

perl -nle '{$m = $1 if /item_id:(\d+)/ and $1 > $m} END{print $m}' input

因为您提到了 grep,所以这里有一种使用命令行工具的方法:

cut -d' ' -f3 input | sed 's/[^:]*:\([0-9]*\),/\1/' | sort -nr | head -1

或者,

sed 's/.*item_id:\([0-9]*\),.*/\1/' input | sort -nr | head -1

【讨论】:

  • 这是一个关于Perl的问题
  • 为什么不切入 sed?或用 cut 提取数字(例如cut -d, -f1 input | cut -d: -f4 | sort ...
  • @Borodin: $found = qx'sed...' :)
  • @Borodin,由于某种原因,我认为 OP 在命令行工具中提到了grep
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-28
  • 2021-02-06
  • 1970-01-01
  • 2016-07-05
  • 2022-11-03
  • 1970-01-01
相关资源
最近更新 更多