2012-10-18 51 views
6

我想抓住字符串內的羅馬數字(80以下的數字就夠了)。我在How do you match only valid roman numerals with a regular expression?找到了很好的基礎。問題是:它處理整個字符串。我還沒有找到一個解決方案,如何檢測字符串內的羅馬數字,因爲沒有強制性的,每個組都可以是可選的。到目前爲止,我想是這樣的:如何捕捉字符串內的羅馬數字?

my $x = ' some text I-LXIII iv more '; 

if ( $x =~ s/\b(
        (
         (XC|XL|L?X{0,3}) # first group 10-90 
        | 
         (IX|IV|V?I{0,3}) # second group 1-9 
        )+ 
      ) 
     \b/>$1</xgi) { # mark every occurrence 
    say $x; 
} 

__END__ 
><some>< ><text>< ><>I<><-><>LXIII<>< ><>iv<>< ><more>< 
desired output: 
    some text >I<->LXIII< >iv< more 

所以,這一個自理捕獲單詞邊界太大,因爲所有的組是可選的。如何完成它?如何使這兩組中的一組成爲強制性的,而無法確定哪一組是強制性的?其他迎接羅馬人的方法也受到歡迎。

+0

一般來說,說了''或'B'或'ab',但不是沒有,你可以做'(A | B | AB)'或' (ab?| b)',但是你不會避免重複。 –

+0

問題:'a'或'b'本身由4個可選塊組成。涵蓋所有這些組合看起來非常瘋狂。 –

+0

啊對了,我明白你的意思了。 Perl支持向前看嗎?你可以在比賽開始時(邊界之後)添加前瞻:'(?= [IVXLDCM])' –

回答

2

這是Perl的讓我們失望,其失蹤\<\>(起止字邊界)構造在其他地方可用。像\b...\b的模式將匹配即使...消耗任何目標字符串,因爲第二\b開始字匹配愉快邊界第二次。

但是最終字邊界就是(?<=\w)(?!\w),所以我們可以使用它代替。

這個程序會做你想做的。它做了前瞻用於封閉在字邊界潛力羅馬字符的字符串(所以我們必須在開始字邊界),然後檢查是否是後面沒有字字符法律羅馬數字(所以現在我們處於結尾字邊界)。

請注意,我轉變了>...<馬克因爲他們混淆了我。

use strict; 
use warnings; 

use feature 'say'; 

my $x = ' some text I-LXIII iv more '; 

if ($x =~ s{ 
    (?= \b [CLXVI]+ \b) 
    (
     (?:XC|XL|L?X{0,3})? 
     (?:IX|IV|V?I{0,3})? 
    ) 
    (?!\w) 
    } 
    {<$1>}xgi) { 

    say $x; 
} 

輸出

some text <I>-<LXIII> <iv> more 
+0

您在代碼'(?!\ w)'中用作末端邊界,但是您之前將其定義爲'(?<= \ w)(?!\ w)'。這只是一個錯字或者我錯過了什麼嗎? –

+0

@ w.k:我們正在做的是找到一串完全由羅馬字母組成的*字*字符串,然後確保它是一個有效的羅馬數字。 '(?!\ w)'是爲了確保這個字符串的* all *是一個有效的羅馬數字,而不僅僅是前幾個字符。例如,如果我們有'LXIC',那麼只有'LXI'是有效的,'(?!\ w)'不匹配,因爲'C'是一個單詞字符。添加'(?<= \ w)'僅用於防止兩個非單詞字符之間的邊界也匹配,並且在這裏永遠不會發生 – Borodin

4

您可以使用Roman CPAN模塊

use Roman; 

my $x = ' some text I-LXIII VII XCVI IIIXII iv more '; 
if ( $x =~ 
    s/\b 
    (
     [IVXLC]+ 
    ) 
    \b 
    /isroman($1) ? ">$1<" : $1/exgi) { 
    say $x; 
} 

輸出:

some text >I<->LXIII< >VII< >XCVI<IIIXII>iv< more 
+0

謝謝,但它也捕獲了無效的序列。 –

+0

@ w.k:是的,你說得對。我沒有注意到。 – Toto

+0

@ w.k:查看我的更新。 – Toto