在Perl中使用正则表达式分割字符串

3
我需要帮助将以下字符串拆分为(日期,ID,毫秒)。 May 26 09:33:33 localhost archiver: saving ID 0191070818_1462647213_489705 took 180 msec 我想要的仅是第一个下划线之前的ID部分。
因此,这就是我希望输出的样子: May 26 09:33:33, 0191070818, 180 我无法确定正则表达式中应该填写什么。
use strict;
use warnings;

my $data = 'May 26 09:33:33 localhost archiver: saving ID 0091070818_1432647213_489715 took 180 msec';

my @values = split('/[]/', $data);

foreach my $val (@values) {
  print "$val\n";
}

exit 0;
6个回答

4

好的。那个分隔符不起作用,因为你使用了单引号,所以该字符串会被直接使用。由于它在你的示例文本中不存在,所以它根本就没有任何作用。

Split函数是基于字段分隔符'cuts up'一个字符串,这可能不是你想要的。例如,

 split ( ' ', $data ); 

我可以为您提供:

$VAR1 = [
          'May',
          '26',
          '09:33:33',
          'localhost',
          'archiver:',
          'saving',
          'ID',
          '0091070818_1432647213_489715',
          'took',
          '180',
          'msec'
        ];

考虑到您的字符串并不适合“fieldify”处理,我建议采用不同的方法:

您需要选择想要从中获取的内容。假设您没有混杂一些奇怪的记录:

my $data = 'May 26 09:33:33 localhost archiver: saving ID 0091070818_1432647213_489715 took 180 msec';

my ($time_str) = ( $data =~ m/^(\w+ \d+ \d{2}:\d{2}:\d{2})/ );
my ($id)       = ( $data =~ m/(\d+)_/ );
my ($msec)     = ( $data =~ m/(\d+) msec/ );
print "$time_str, $id, $msec,\n";

注意 - 您可以组合您的正则表达式模式(正如一些示例所示)。我这样做是为了简化和澄清正在发生的事情。正则表达式匹配应用于 $data(因为有=~)。然后提取括号()中的“匹配”元素,并将其“返回”以插入到左侧的变量中。
(注意 - 您需要在括号中使用'my($msec)',因为这样使用值,而不是测试结果(true/false)的结果)

谢谢!现在,如果我从文本文件中读取多行类似于该行的内容,我应该这样做吗?while(<IN>){ if(/saving ID/){ my ($time_str) = ( m/^(\w+ \d+ \d{2}:\d{2}:\d{2})/ ); - user2007843
是的,差不多。虽然我建议使用open(my $input, "<", $filename) or die $!,然后使用while(<$input>){}。(它基本上是相同的,但风格更好) - Sobrique

4

甚至可以最简单地按空格将数据拆分(然后通过连接前三个字段重新构建日期)。 这并不是非常复杂,但它能完成工作。

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

my $data = 'May 26 09:33:33 localhost archiver: saving ID 0091070818_1432647213_489715 took 180 msec';

my @values = split(/\s+/, $data);

my $date = join ' ', @values[0,1,2];
my $id   = $values[7];
my $time = $values[9];

say "Date: $date";
say "ID:   $id";
say "Time: $time";

这将会得到:

Date: May 26 09:33:33
ID:   0091070818_1432647213_489715
Time: 180

3

split看起来不是这个任务的正确工具。我会使用正则表达式匹配:

my @values = $data =~ /^([[:alpha:]]{3}\s[0-9][0-9]\s[0-9][0-9]:[0-9][0-9]:[0-9][0-9]) # date & time
                       \s.*?\sID\s
                       ([0-9]+)            # ID
                       .*\stook\s
                       ([0-9]+)            # duration
                       \smsec/x;
print join(',', @values), "\n";

使用带有/a修饰符的\d是替代[0-9]的不错选择。 - Borodin
2
@Borodin:在5.14及以上版本中,是的。 - choroba

2

我不确定split()是最好的方法。这段代码匹配您的目标ID并提取它:

($id) = $data =~ m/(?<=ID )[^_]+/g;

正则表达式使用反向预查 (?<=ID ) 来锚定匹配的起始位置,紧接在 "ID " 的右侧,然后获取其后面的所有不是下划线的字符。
以下是一些测试代码:
my $data = 'May 26 09:33:33 localhost archiver: saving ID 0091070818_1432647213_489715 took 180 msec';
($id) = $data =~ m/(?<=ID )[^_]+/g;
print $id

输出:

0091070818

查看实时演示


2

最好使用三个单独的模式来完成这个任务。下面的代码演示了如何实现。

我使用了/x修饰符,以便可以在正则表达式模式中添加空格,提高可读性。

除非您确定数据格式良好(即它是程序的输出),否则应添加测试以确保在模式匹配后所有三个值都被定义。或者您可以直接测试模式匹配本身。

use strict;
use warnings;
use v5.10;

my $s = 'May 26 09:33:33 localhost archiver: saving ID 0191070818_1462647213_489705 took 180 msec';

for ( $s ) {

    my ($date)  = / ^ ( [a-z]+ \s+ \d+ \s+ [\d:]+ ) /ix;
    my ($id)    = / ID \s+ (\d+) _ /x;
    my ($msecs) = / (\d+) \s+ msec /x;

    say join ',', $date, $id, $msecs;
}

输出

May 26 09:33:33,0191070818,180

1

split 不是在这里使用的工具。下面是一个正则表达式,至少对于你列出的特定情况有效。

my $data = 'May 26 09:33:33 localhost archiver: saving ID 0091070818_1432647213_489715 took 180 msec';

$data =~ m/^(\w+ \d+ \d\d:\d\d:\d\d).+saving ID (\d+).+took (\d+) msec$/;

my ($date, $id, $msec) = ($1,$2,$3);

print "$date, $id, $msec\n";

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接