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的初学者。任何帮助表示赞赏。谢谢!
答案 0 :(得分:5)
split在这里可以很好地运作。 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);
}
输出:
Date=> 09/27/2009 Time=> 19:48:00 Text=> Departure Location
答案 1 :(得分: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;
答案 2 :(得分:2)
你在解析器中做了太多工作。
my ($date, $time, $text) = split(' ', $_, 3);
say "Date=> $date";
say "Time=> $time";
say "Text=> $text";
答案 3 :(得分: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