罗马数字的正则表达式不起作用

时间:2014-07-20 15:38:00

标签: python regex

我正在尝试使用以下正则表达式从文本中识别罗马数字:

>>>Title="LXXXIV XC, XCII      XXX     LXII"
>>>RomanNum = re.findall(r'[\s,]+M{0,4}[CM|CD|D?C{0,3}]?[XC|XL|L?X{0,3}]?[IX|IV|V?I{0,3}]?[\s,]+', Title, re.M|re.I)`
>>>RomanNum
[' \t']

我想要类似的东西:

['LXXXIV', 'XC, 'XCII', 'XXX', 'LXII']

就我对正则表达的理解而言,我认为至少XC应该匹配。 XC应该与上面的空格前面的正则表达式的[XC|XL|L?X{0,3}]部分匹配,并且上面的正则表达式捕获后面的逗号。我错过了什么?

除此之外,我可以达到以下所需的结果(但我希望避免更大的复杂性):

>>>RomanNum = [re.search(r'^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$', TitleElem, re.M|re.I) for TitleElem in re.split(',| ', Title)]`

任何帮助表示感谢。

3 个答案:

答案 0 :(得分:1)

此时您的正则表达式语法已关闭:

  

XC应与[XC|XL|L?X{0,3}]

匹配

因为您使用 square 括号,您可以在其中描述 round 括号的行为。将方括号更改为圆形以进行更正。

在完整正则表达式的其他部分重复此错误。

答案 1 :(得分:1)

如果您想使用findallfinditer方法在字符串中查找多个罗马数字,可能的模式是:

(?=[MDCXLVI])(?<![MDCXLVI])M{0,4}(?:C[MD]|D?C{0,3})(?:X[CL]|L?X{0,3})(?:I[XV]|V?I{0,3})(?![MDCXLVI])

它有点长,我将解释为什么我认为它有效:

(?=[MDCXLVI])是一个前瞻,检查位置是否跟随其中一个字符。这个前瞻有两个功能:

  • 首先是模仿一种第一个字符的歧视,以便快速避免所有不包含其中一个字符的位置(这样,正则表达式引擎就是& #39;需要使用M{0,4}(?:C[MD]|D?C{0,3})(?:X[CL]|L?X{0,3})(?:I[XV]|V?I{0,3}))测试所有可能的开头。

  • 第二个检查是否至少有一个字符,因为M{0,4}(?:C[MD]|D?C{0,3})(?:X[CL]|L?X{0,3})(?:I[XV]|V?I{0,3})可以匹配一个空字符串。

(?<![MDCXVLI])(?![MDCXVLI])用作边界,以确保没有其他&#34;罗马字符&#34;周围(否则像ILVIII这样的子字符串将返回LVIII,而不是以错误的格式跳过整个字符组)。 请注意,其他类型的边界也是可能的,例如\b(?<![^\s,]) (?![^\s,]) ...取决于字符串格式。另请注意,左边界是仅在(?=[MDCXVLI])之后放置才能打破第一个字符歧视。

CM|CD等变体缩小为C[MD]

该模式仅使用非捕获组(?:...)来保留内存并避免不必要的存储任务。

答案 2 :(得分:0)

Dive Into Python为检测罗马数字提供了一个很好的正则表达式。它们还提供了sample script,您可以利用它来开始。此脚本来自我的第一个链接的7.5部分。

#Define pattern to detect valid Roman numerals
romanNumeralPattern = re.compile("""
    ^                   # beginning of string
    M{0,4}              # thousands - 0 to 4 M's
    (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
                        #            or 500-800 (D, followed by 0 to 3 C's)
    (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
                        #        or 50-80 (L, followed by 0 to 3 X's)
    (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
                        #        or 5-8 (V, followed by 0 to 3 I's)
    $                   # end of string
    """ ,re.VERBOSE)