在处理完Perl数组后如何从元数组中删除元素?

时间:2010-02-03 19:28:23

标签: perl arrays parsing

我正在将一个postfix邮件日志文件读入一个数组,然后循环遍历它以提取消息。在第一遍,我正在检查“to =”行上的匹配并获取消息ID。在构建一个MSGID数组之后,我将循环返回数组以提取有关to =,from =和client =行的信息。

我想要做的是在从数据中提取数据后立即从数组中删除一行,以便使处理速度更快(即要检查的行少一行)。

有什么建议吗?这是在Perl。


编辑:gbacon的回答足以让我推出一个可靠的解决方案。以下是它的内容:

my %msg;
while (<>) {
    my $line = $_;
    if (s!^.*postfix/\w+\[.+?\]: (\w+):\s*!!) {
            my $key = $1;
            push @{ $msg{$key}{$1} } => $2
                    while /\b(to|from|client|size|nrcpt)=<?(.+?)(?:>|,|\[|$)/g;
    }
    if ($line =~ s!^(\w+ \d+ \d+:\d+:\d+)\s(\w+.*)\s+postfix/\w+\[.+?\]: (\w+):\s*removed!!) {
            my $key = $3;
            push @{ $msg{$key}{date} } => $1;
            push @{ $msg{$key}{server} } => $2;
    }
}

use Data::Dumper;
$Data::Dumper::Indent = 1;
print Dumper \%msg;

我确信第二个正则表达式可以更令人印象深刻,但它可以完成我需要的工作。我现在可以获取所有消息的哈希值并提取出我感兴趣的消息。

感谢所有回答的人。

6 个答案:

答案 0 :(得分:5)

一次性完成:

#! /usr/bin/perl

use warnings;
use strict;

# for demo only
*ARGV = *DATA;

my %msg;
while (<>) {
  if (s!^.*postfix/\w+\[.+?\]: (\w+):\s*!!) {
    my $key = $1;
    push @{ $msg{$key}{$1} } => $2
      while /\b(to|from|client)=(.+?)(?:,|$)/g;
  }
}

use Data::Dumper;
$Data::Dumper::Indent = 1;
print Dumper \%msg;
__DATA__
Apr  8 14:22:02 MailSecure03 postfix/smtpd[32388]: BA1CE38965: client=mail.example.com[x.x.x.x]
Apr  8 14:22:03 MailSecure03 postfix/cleanup[32070]: BA1CE38965: message-id=<49dc4d9a.6020...@example.com>
Apr  8 14:22:03 MailSecure03 postfix/qmgr[19685]: BA1CE38965: from=<mailt...@example.com>, size=1087, nrcpt=2 (queue active)
Apr  8 14:22:04 MailSecure03 postfix/smtp[32608]: BA1CE38965: to=<us...@test.com>, relay=127.0.0.1[127.0.0.1]:10025, delay=1.7, delays=1/0/0/0.68, dsn=2.0.0, status=sent (250 OK, sent 49DC509B_360_15637_162D8438973)
Apr  8 14:22:04 MailSecure03 postfix/smtp[32608]: BA1CE38965: to=<us...@test.com>, relay=127.0.0.1[127.0.0.1]:10025, delay=1.7, delays=1/0/0/0.68, dsn=2.0.0, status=sent (250 OK, sent 49DC509B_360_15637_162D8438973)
Apr  8 14:22:04 MailSecure03 postfix/qmgr[19685]: BA1CE38965: removed
Apr  8 14:22:04 MailSecure03 postfix/smtpd[32589]: 62D8438973: client=localhost.localdomain[127.0.0.1]
Apr  8 14:22:04 MailSecure03 postfix/cleanup[32080]: 62D8438973: message-id=<49dc4d9a.6020...@example.com>
Apr  8 14:22:04 MailSecure03 postfix/qmgr[19685]: 62D8438973: from=<mailt...@example.com>, size=1636, nrcpt=2 (queue active)
Apr  8 14:22:04 MailSecure03 postfix/smtp[32417]: 62D8438973: to=<us...@test.com>, relay=y.y.y.y[y.y.y.y]:25, delay=0.19, delays=0.04/0/0.04/0.1, dsn=2.6.0, status=sent (250 2.6.0  <49dc4d9a.6020...@example.com> Queued mail for delivery)
Apr  8 14:22:04 MailSecure03 postfix/smtp[32417]: 62D8438973: to=<us...@test.com>, relay=y.y.y.y[y.y.y.y]:25, delay=0.19, delays=0.04/0/0.04/0.1, dsn=2.6.0, status=sent (250 2.6.0  <49dc4d9a.6020...@example.com> Queued mail for delivery)
Apr  8 14:22:04 MailSecure03 postfix/qmgr[19685]: 62D8438973: removed

代码的工作原理是首先查找我们存储在BA1CE38965中的队列ID(例如62D8438973$key)。

接下来,我们会在当前行上找到所有匹配项(感谢/g切换),看起来像to=<...>client=mail.example.com,依此类推,有没有分隔逗号。

模式中的注意事项是

  • \b - 仅匹配字边界(阻止匹配xxxto=<...>
  • (to|from|client) - 匹配tofromclient
  • (.+?) - 将字段的值与非贪心量词匹配
  • (?:,|$) - 匹配逗号或字符串结尾而不会捕获到$3

非贪婪的(.+?)强制匹配停在它遇到的第一个逗号而不是最后一个逗号。否则,在

的行上
to=<foo@example.com>, other=123

你得到<foo@example.com>, other=123作为收件人!

然后,对于匹配的每个字段,我们将push放在数组的末尾(例如,因为可能有多个收件人)连接到队列ID和字段名称。看看结果:

$VAR1 = {
  '62D8438973' => {
    'client' => [
      'localhost.localdomain[127.0.0.1]'
    ],
    'to' => [
      '<us...@test.com>',
      '<us...@test.com>'
    ],
    'from' => [
      '<mailt...@example.com>'
    ]
  },
  'BA1CE38965' => {
    'client' => [
      'mail.example.com[x.x.x.x]'
    ],
    'to' => [
      '<us...@test.com>',
      '<us...@test.com>'
    ],
    'from' => [
      '<mailt...@example.com>'
    ]
  }
};

现在说您要打印队列ID为BA1CE38965的邮件的所有收件人:

my $queueid = "BA1CE38965";
foreach my $recip (@{ $msg{$queueid}{to} }) {
  print $recip, "\n":
}

也许您只想知道有多少收件人:

print scalar @{ $msg{$queueid}{to} }, "\n";

如果您愿意假设每封邮件只有一个客户端,请使用

访问它
print $msg{$queueid}{client}[0], "\n";

答案 1 :(得分:4)

它实际上不会使处理更快,因为从阵列中间移除是一项昂贵的操作。

更好的选择:

  • 一次性完成任务
  • 构建ID数组时,在主数组中包含指针(索引),以便您可以快速访问其元素以获取给定ID

答案 2 :(得分:1)

为什么不这样做:

my @extracted = map  extract_data($_), 
                grep msg_rcpt_to( $rcpt, $_ ), @log_data;

完成后,您将获得一系列提取的数据,其顺序与日志中显示的顺序相同。

答案 3 :(得分:0)

在perl中,您可以使用splice()例程从数组中删除元素。

像往常一样,在循环数组时从数组中删除时要小心,因为数组索引会发生变化。

答案 4 :(得分:0)

假设您手头有索引,请使用splice:

splice(@array, $indextoremove, 1)

但要小心。删除元素后,您的索引将无效。

答案 5 :(得分:0)

操纵数组内容的常用方法:

# start over with this list for each example:
my @list = qw(a b c d);

<强>剪接

splice @list, 2, 1, qw(e);
# @list now contains: qw(a b e d)

pop unshift

pop @list;
# @list now contains: qw(a b c)

unshift @list;
# @list now contains: qw(b c d)

<强>地图:

@list = map { $_ eq 'b' ? () : $_ } @list;
# list now contains: qw(a c d);

数组切片

@list[3..4] = qw(e f);
# list now contais: qw(a b c e f);

foreach 循环:

foreach (@list)
{
    # $_ is aliased to each element of the list in turn;
    # assignments will be propogated back to the original structure
    $_ = uc if m/[a-c]/;
}
# list now contains: qw(A B C d);

perldoc perlfuncperldoc perldata中的切片以及perldoc perlsyn中的循环中了解所有这些功能。