2008-11-06 1 views
122

Думая о my other problem, я решил, что я не могу даже создать регулярное выражение, которое будет соответствовать римским цифрам (не говоря уже о контекстно-свободной грамматике, которая будет генерировать их)Как вы сопоставляете только действительные римские цифры с регулярным выражением?

Проблемы соответствия только действительные римские цифр. Например, 990 НЕ «XM», это «CMXC»

Моя проблема с созданием регулярного выражения заключается в том, что для того, чтобы разрешить или не разрешить определенные символы, мне нужно оглянуться назад. Давайте возьмем тысячи и сотни, например.

Я могу разрешить M {0,2} C? M (для разрешения 900, 1000, 1900, 2000, 2900 и 3000). Однако, если совпадение находится на CM, я не могу допускать, чтобы следующие символы были C или D (потому что я уже достиг 900).

Как выразить это в регулярном выражении?
Если это просто не выражается в регулярном выражении, то это выражается в контекстно-свободной грамматике?

+0

Это действительно интересная проблема, и может быть использовано для обучения регулярных выражений ... если это выполнимо. Я не знаю их достаточно хорошо, чтобы рискнуть. – helloandre 2008-11-06 01:33:12

ответ

269

Try:

^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$ 

разбив его:


M{0,4}

Это определяет раздел тысяч и в основном ограничивает его между 0 и 4000. Это относительно просто:

0: <empty> matched by M{0} 
1000: M  matched by M{1} 
2000: MM  matched by M{2} 
3000: MMM  matched by M{3} 
4000: MMMM  matched by M{4} 

(CM|CD|D?C{0,3})

Немного более сложным, это для секции сотни и охватывает все возможности:

0: <empty> matched by D?C{0} (with D not there) 
100: C  matched by D?C{1} (with D not there) 
200: CC  matched by D?C{2} (with D not there) 
300: CCC  matched by D?C{3} (with D not there) 
400: CD  matched by CD 
500: D  matched by D?C{0} (with D there) 
600: DC  matched by D?C{1} (with D there) 
700: DCC  matched by D?C{2} (with D there) 
800: DCCC  matched by D?C{3} (with D there) 
900: CM  matched by CM 

(XC|XL|L?X{0,3})

Те же правила, что и предыдущий раздел, но для десятков мест:

0: <empty> matched by L?X{0} (with L not there) 
10: X  matched by L?X{1} (with L not there) 
20: XX  matched by L?X{2} (with L not there) 
30: XXX  matched by L?X{3} (with L not there) 
40: XL  matched by XL 
50: L  matched by L?X{0} (with L there) 
60: LX  matched by L?X{1} (with L there) 
70: LXX  matched by L?X{2} (with L there) 
80: LXXX  matched by L?X{3} (with L there) 
90: XC  matched by XC 

(IX|IV|V?I{0,3})

Это раздел единиц, обработка 0 через 9 и также simil соток в двух предыдущих разделах (римские цифры, несмотря на их кажущуюся странность, следуют некоторые логические правила, как только вы выяснить, что они есть):

0: <empty> matched by V?I{0} (with V not there) 
1: I  matched by V?I{1} (with V not there) 
2: II  matched by V?I{2} (with V not there) 
3: III  matched by V?I{3} (with V not there) 
4: IV  matched by IV 
5: V  matched by V?I{0} (with V there) 
6: VI  matched by V?I{1} (with V there) 
7: VII  matched by V?I{2} (with V there) 
8: VIII  matched by V?I{3} (with V there) 
9: IX  matched by IX 
+4

Разве это не M {0,3}? – lemon 2010-03-25 02:18:58

+0

Возможно. Я могу видеть только выводы в вопросе о том, что он должен подняться до 3999 (не конкретное определенное требование), но я дозволяю ему до 4999 в любом случае. Если вы действительно хотите ограничить его до 3999, то непременно удалите одну из миссий – paxdiablo 2010-03-25 02:41:26

+1

любого решения, чтобы избежать соответствия пустой строки? – 2011-11-01 22:33:42

7

К счастью, диапазон номеров ограничено 1..3999 или около , Таким образом, вы можете создать кусок еды регулярного выражения.

<opt-thousands-part><opt-hundreds-part><opt-tens-part><opt-units-part> 

Каждая из этих частей будет иметь дело с капризами римской нотации. Например, с использованием обозначений Perl:

<opt-hundreds-part> = m/(CM|DC{0,3}|CD|C{1,3})?/; 

Повторите и смонтируйте.

Добавлено: <opt-hundreds-part> может быть сжат дальше:

<opt-hundreds-part> = m/(C[MD]|D?C{0,3})/; 

Поскольку положение не может сравниться ничего, нет никакой необходимости в знак вопроса 'D C {0,3}?'. И, скорее всего, круглые скобки должны быть типа без захвата - в Perl:

<opt-hundreds-part> = m/(?:C[MD]|D?C{0,3})/; 

Конечно, все это должно быть нечувствительны к регистру, тоже.

Вы также можете расширить это, чтобы обсудить варианты, упомянутые Джеймсом Карраном (разрешить XM или IM для 990 или 999 и CCCC для 400 и т. Д.).

<opt-hundreds-part> = m/(?:[IXC][MD]|D?C{0,4})/; 
13

На самом деле, ваше помещение испорчено. 990 IS «XM», а также «CMXC».

Римляне были гораздо менее обеспокоены «правилами», чем ваш учитель третьего класса. Пока он складывался, все было в порядке. Следовательно, «IIII» был так же хорош, как «IV» для 4. И «IIM» был полностью прохладным для 998.

(Если у вас есть проблемы с этим ... Помните, что английское правописание не было формализовано до 1700-х годов. До тех пор, пока читатель мог это понять, это было достаточно хорошо).

1

Как указал Джереми и Пакс выше ... '^ M {0,4} (CM | CD | D? C {0,3}) (XC | XL | L? X {0,3}) (IX | IV |? V I {0,3}) $»должно быть решение вы после ...

конкретный URL, который должен был быть прикреплен (ИМХО) это http://thehazeltree.org/diveintopython/7.html

Пример 7.8 - это короткая форма с использованием {n, m}

0

Проблема решения от Джереми и Пакса состоит в том, что он также соответствует «ничего».

следующее регулярное выражение ожидает, по меньшей мере, один римская цифра:

^(M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|[IDCXMLV])$ 
11

Чтобы избежать сопоставлений пустой строки, которую вы должны будете повторять рисунок на четыре раза, и заменить каждую 0 с 1 в своей очереди, и учет V, L и D:

(M{1,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|M{0,4}(CM|C?D|D?C{1,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|M{0,4}(CM|CD|D?C{0,3})(XC|X?L|L?X{1,3})(IX|IV|V?I{0,3})|M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|I?V|V?I{1,3})) 

В этом случае (поскольку эта модель использует ^ и $) лет u лучше было бы сначала проверить пустые строки и не мешать их сопоставлению. Если вы используете word boundaries, то у вас нет проблем, потому что нет такого понятия, как пустое слово. (По крайней мере, регулярное выражение не определяет одно, не начинайте философствовать, я прагматично здесь!)


В моем конкретном (реальном мире) случае мне нужна матч цифра в слове окончаний, и я не нашел никакого другого способа вокруг него. Мне нужно было вычистить номера сносок из моего обычного текстового документа, где текст, такой как «Красное море» cl и Большой барьерный риф cli был преобразован в the Red Seacl and the Great Barrier Reefcli. Но у меня все еще были проблемы с действительными словами, такими как Tahiti, и fantastic очищены от Tahit и fantasti.

0

Стивен Levithan использует это регулярное выражение в his post, который проверяет римские цифры до «deromanizing» значение:

/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/ 
3
import re 
pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$' 
if re.search(pattern, 'XCCMCI'): 
    print 'Valid Roman' 
else: 
    print 'Not valid Roman' 

Для людей, которые действительно хотят, чтобы понять логику, пожалуйста, взглянуть на шаг пошаговое объяснение на 3 страницах на diveintopython.

Единственное отличие от оригинального решения (которое имело M{0,4}) заключается в том, что я обнаружил, что «MMMM» не является действительной римской цифрой (также старые римляне, скорее всего, не подумали об этом огромном количестве и не согласятся со мной). Если вы один из несогласных старых римлян, пожалуйста, простите меня и используйте версию {0,4}.

0

Я хотел бы написать функции для своей работы для меня. Вот две романные цифровые функции в PowerShell.

function ConvertFrom-RomanNumeral 
{ 
    <# 
    .SYNOPSIS 
     Converts a Roman numeral to a number. 
    .DESCRIPTION 
     Converts a Roman numeral - in the range of I..MMMCMXCIX - to a number. 
    .EXAMPLE 
     ConvertFrom-RomanNumeral -Numeral MMXIV 
    .EXAMPLE 
     "MMXIV" | ConvertFrom-RomanNumeral 
    #> 
    [CmdletBinding()] 
    [OutputType([int])] 
    Param 
    (
     [Parameter(Mandatory=$true, 
        HelpMessage="Enter a roman numeral in the range I..MMMCMXCIX", 
        ValueFromPipeline=$true, 
        Position=0)] 
     [ValidatePattern("^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$")] 
     [string] 
     $Numeral 
    ) 

    Begin 
    { 
     $RomanToDecimal = [ordered]@{ 
      M = 1000 
      CM = 900 
      D = 500 
      CD = 400 
      C = 100 
      XC = 90 
      L = 50 
      X = 10 
      IX = 9 
      V = 5 
      IV = 4 
      I = 1 
     } 
    } 
    Process 
    { 
     $roman = $Numeral + " " 
     $value = 0 

     do 
     { 
      foreach ($key in $RomanToDecimal.Keys) 
      { 
       if ($key.Length -eq 1) 
       { 
        if ($key -match $roman.Substring(0,1)) 
        { 
         $value += $RomanToDecimal.$key 
         $roman = $roman.Substring(1) 
         break 
        } 
       } 
       else 
       { 
        if ($key -match $roman.Substring(0,2)) 
        { 
         $value += $RomanToDecimal.$key 
         $roman = $roman.Substring(2) 
         break 
        } 
       } 
      } 
     } 
     until ($roman -eq " ") 

     $value 
    } 
    End 
    { 
    } 
} 

function ConvertTo-RomanNumeral 
{ 
    <# 
    .SYNOPSIS 
     Converts a number to a Roman numeral. 
    .DESCRIPTION 
     Converts a number - in the range of 1 to 3,999 - to a Roman numeral. 
    .EXAMPLE 
     ConvertTo-RomanNumeral -Number (Get-Date).Year 
    .EXAMPLE 
     (Get-Date).Year | ConvertTo-RomanNumeral 
    #> 
    [CmdletBinding()] 
    [OutputType([string])] 
    Param 
    (
     [Parameter(Mandatory=$true, 
        HelpMessage="Enter an integer in the range 1 to 3,999", 
        ValueFromPipeline=$true, 
        Position=0)] 
     [ValidateRange(1,3999)] 
     [int] 
     $Number 
    ) 

    Begin 
    { 
     $DecimalToRoman = @{ 
      Ones  = "","I","II","III","IV","V","VI","VII","VIII","IX"; 
      Tens  = "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"; 
      Hundreds = "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"; 
      Thousands = "","M","MM","MMM" 
     } 

     $column = @{Thousands = 0; Hundreds = 1; Tens = 2; Ones = 3} 
    } 
    Process 
    { 
     [int[]]$digits = $Number.ToString().PadLeft(4,"0").ToCharArray() | 
          ForEach-Object { [Char]::GetNumericValue($_) } 

     $RomanNumeral = "" 
     $RomanNumeral += $DecimalToRoman.Thousands[$digits[$column.Thousands]] 
     $RomanNumeral += $DecimalToRoman.Hundreds[$digits[$column.Hundreds]] 
     $RomanNumeral += $DecimalToRoman.Tens[$digits[$column.Tens]] 
     $RomanNumeral += $DecimalToRoman.Ones[$digits[$column.Ones]] 

     $RomanNumeral 
    } 
    End 
    { 
    } 
} 
6

Просто, чтобы сохранить его здесь:

(^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$) 

Спички все римские цифры. Не заботится о пустых строках (требуется хотя бы одна римская цифра). Должен работать в PCRE, Perl, Python и Ruby.

Интернет Рубин демо: http://rubular.com/r/KLPR1zq3Hj

Интернет Конверсия: http://www.onlineconversion.com/roman_numerals_advanced.htm

Смежные вопросы