2012-07-09 36 views
2
09/27/2009 19:48:00 Departure Location 

我想匹配並替換文本文件中給定的行。日期和時間之後的文本長度可能會有所不同。我讀一行文件中的行,我需要最終輸出到打印as--Perl多重模式匹配和替換一次傳遞

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

這種模式尤其是給你的麻煩:

$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; 
+0

尼斯,戴維。鑑於這一行 - 以及您的數據警告 - 這也適用:'s /(\ S +)(\ S +)(。*)/ Date => $ 1 \ nTime => $ 2 \ nText => $ 3 \ n /' – Kenosis 2012-07-09 04:52:22

+1

肯尼迪是的,那也可以。我傾向於贊成更明確的匹配,以便儘可能少地誤報誤差,以便我可以更好地控制匹配發生的字符串中的哪個位置,但是您的方法也可以工作。 – DavidO 2012-07-09 04:55:11

+1

您的正則表達式理由很有意義,您添加的命名捕獲有助於提高可讀性。 – Kenosis 2012-07-09 05:27:02

2

你做你的解析器的工作太多了。

my ($date, $time, $text) = split(' ', $_, 3); 
say "Date=> $date"; 
say "Time=> $time"; 
say "Text=> $text"; 
+0

我想你誤解了OP的目標字符串。 (更新評論) – DavidO 2012-07-09 05:48:35

+1

哦!我得到了輸入和輸出反向。固定。 – ikegami 2012-07-09 14:46:19

+0

(如果您想驗證日期,時間和文本,請在分割後進行) – ikegami 2012-07-09 18:35:41

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
+0

我試圖將它拆分得更早,但由於我的整型變量(數字)不能包含時間值,所以在從@array中提取它時遇到了困難。那麼,現在我知道正確的方法。謝謝。 +1,因爲它整潔美觀! :) – Prince 2012-07-09 07:02:14

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 
+0

我沒有得到第一行,'<<'關鍵字做了什麼? – Prince 2012-07-09 18:31:54

+1

這是一個「HERE」文檔。 「<< END」後面的行通過(但不包括)「END」引用「雙引號」語義。請參閱「報價和引用類運營商」部分中的perlop。 – DavidO 2012-07-09 18:33:39

+0

這裏最棘手的是通常我們習慣於看到'<< END;'。如果您不熟悉本文的工作方式以及實際引用文本的開頭位置,那麼包含「if」修飾符可能會有點混淆。 – DavidO 2012-07-09 18:34:33