2
09/27/2009 19:48:00 Departure Location

我正在尝试匹配和替换文本文件中的给定行。日期和时间之后的文本长度可能会有所不同。我正在逐行读取文件,我需要将最终输出打印为 -

Date=> 09/27/2009
Time=> 19:48:00 
Text=> Departure Location

我试图一次性完成替换,如下所示 -

if($line =~ m/(\d+)\/(\d+)\/(\d+)\h{1}(\d+):(\d+):(\d+)/){

    $line =~ s/(\[a-zA-Z])/\nText=> $1/;
    $line =~ s/(\d+)\/(\d+)\/(\d+)/\nDate=> $1\/$2\/$3/;
    $line =~ s/\h{1}(\d+):(\d+):(\d+)/\nTime=> $1\:$2\:$3/;

    print FH "$line\n";

}

但我得到的只是这个——

Date=> 09/27/2009
Time=> 19:48:10 Departure Location

我知道匹配有问题,Text但我无法修复它。我仍然是 Perl 初学者。任何帮助表示赞赏。谢谢!

4

4 回答 4

5

有限制的拆分在这里可以很好地工作。pairwise不是绝对必要的,但帮助我避免了循环:

#!/usr/bin/env perl

use strict; use warnings;
use feature 'say';
use List::MoreUtils qw( pairwise );

my $input = q{09/27/2009 19:48:00 Departure Location};
my @fields = qw(Date Time Text);
my @values = split ' ', $input, @fields;

{
    no warnings 'once';
    say join("\n", pairwise { "$a=> $b" } @fields, @values);
}

输出:

日期=> 09/27/2009
时间=> 19:48:00
文本=> 出发地点
于 2012-07-09T06:39:37.470 回答
4

这种模式尤其会给您带来麻烦:

$line =~ s/(\[a-zA-Z])/\nText=> $1/;

它有一些问题。首先,左括号前面的反斜杠: \[, 正在转义括号,因此您的字符类根本不是字符类,而是文字文本“ [a-zA-Z]”。其次,您的文本匹配中不允许出现“空格”,因此如果字符串的文本部分包含任何空格字符(或标点符号),它也将无法匹配。第三,没有量词,所以只会匹配单个字符。最后一点是它可能应该锚定到字符串的末尾。它可能像这样工作(但不要使用它,请继续阅读):

$line =~ s/([a-zA-Z\s]+)$/\nText=> $1/;

但可能有更好的解决方案。这一切都可以一次完成而不会失去清晰度。对我来说,如果您捕获更大的细分,它开始变得更有意义:

$string =~ s{^
    (\d\d/\d\d/\d{4})\s    # The date.
    (\d\d:\d\d:\d\d)\s     # The time.
    (.+)$                  # The rest (the text).
}{Date=> $1\nTime=> $2\nText=> $3}x;

通常情况下,/x 修饰符有助于更容易阅读代码。

有一些很好的资源可以用来处理 Perl 的正则表达式。我建议从perldoc perlretut开始,它是“关于在 Perl 中理解、创建和使用正则表达式的基本教程”。

使用命名捕获还可以增加一定程度的清晰度,尤其是当您的正则表达式变得更加复杂时:

$string =~ s{
    ^
    (?<date>\d\d/\d\d/\d{4})\s
    (?<time>\d\d:\d\d:\d\d)\s
    (?<text>.+)
    $
}
{Date=> $+{date}\nTime=> $+{time}\nText=> $+{text}}x;
于 2012-07-09T04:40:47.373 回答
2

您在解析器中做了太多工作。

my ($date, $time, $text) = split(' ', $_, 3);
say "Date=> $date";
say "Time=> $time";
say "Text=> $text";
于 2012-07-09T05:36:58.820 回答
2

将尽可能多的功能塞进一个小空间只会增加 Perl 难以理解的声誉。

这段代码对我来说似乎更清晰

$line = <<END if $line =~ m|^(\d\d/\d\d/\d{4}) \s+ (\d\d:\d\d:\d\d) \s+ (.*)|x;
Date=> $1
Time=> $2 
Text=> $3
END
于 2012-07-09T16:01:45.300 回答