考虑到我的另一个问题,我决定我什至不能创建一个匹配罗马数字的正则表达式(更不用说会生成它们的无上下文语法了)

问题是仅匹配有效罗马数字。
例如990不是“ XM”,而是“ CMXC”。

为此制作正则表达式的问题是,为了允许或不允许某些字符,我需要回顾一下。例如,以成千上万为例。

我可以允许M {0,2} C?M(允许900、1000、1900、2000、2900和3000) 。但是,如果匹配在CM上,我不允许以下字符为C或D(因为我已经是900)。 />如果它在正则表达式中根本无法表达,那么它是否可以在上下文无关的语法中表达?

#1 楼

您可以为此使用以下正则表达式:

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


分解下来,M{0,4}指定千位部分,基本上将其限制在04000之间。这是相对简单的:

   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}


当然,如果要允许更大的数字,则可以使用M*之类的值来允许任意数量(包括零)的数千个。 。

下一个是(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})是单位部分,处理(IX|IV|V?I{0,3})0,并且与前两个类似部分(罗马数字尽管看起来很怪异,但是一旦弄清它们是什么,就遵循一些逻辑规则):

正则表达式还将匹配一个空字符串。如果您不希望这样做(并且您的正则表达式引擎足够现代),则可以使用正向后看和前瞻:
只是要事先检查长度不为零)。

评论


它不是M {0,3}吗?

–柠檬
10 Mar 25 '10在2:18

任何解决方案,以避免匹配空字符串?

–法肯多·卡斯科(Facundo Casco)
2011年11月1日下午22:33

@Aashish:当罗马人是一支不可忽视的力量时,MMMM是正确的方法。在核心帝国瓦解之后很久就出现了这种夸张的表象。

– paxdiablo
13年7月15日在2:18

@ paxdiablo这就是我发现mmmcm失败的方式。字符串regx =“ ^ M {0,3}(CM | CD | D?C {0,3})(XC | XL | L?X {0,3})(IX | IV | V?I {0, 3})$“; if(input.matches(regx))->对于Java中的MMMCM / MMMM,此值评估为false。

– amIT
2014年8月10日14:07



/ ^ M {0,3}(?: C [MD] | D?C {0,3})(?: X [CL] | L?X {0,3})(?: I [XV] | V?I {0,3})$ / i

–克里索夫
19年3月29日在13:11

#2 楼

实际上,您的前提是有缺陷的。 990 IS是“ XM”,还有“ CMXC”。只要加起来就可以了。因此,“ IIII”和“ IV”一样好于4。“ IIM”对于998来说完全很酷。直到1700年代。在那之前,只要读者能弄清楚,就足够了。

评论


当然可以,很酷。但是我认为我的“严格的三年级老师”语法需求使正则表达式问题更加有趣。

–丹尼尔·马格里奥拉(Daniel Magliola)
08年11月6日在3:03

好的观点,詹姆斯应该是一位严格的作者,但要宽容的读者。

–科林
2012年5月4日,0:19

@Corin:又名Postel的稳健性原则

– jfs
17年5月9日在21:27

#3 楼

只需在此处保存即可:

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


匹配所有罗马数字。不在乎空字符串(至少需要一个罗马数字字母)。应该可以在PCRE,Perl,Python和Ruby中使用。

在线Ruby演示:http://rubular.com/r/KLPR1zq3Hj

在线转换:http:// www .onlineconversion.com / roman_numerals_advanced.htm

评论


我不知道为什么,但是在MemoQ中自动翻译列表时,主要答案对我不起作用。但是,此解决方案可以-尽管排除字符串开始/结束符号。

–orlando2bjr
17年4月6日在12:31

@ orlando2bjr很高兴为您提供帮助。是的,在这种情况下,我是自己匹配数字的,没有环境。如果要在文本中查找,请确保您需要删除^ $。干杯!

–smileart
17年5月6日在16:37

我如何在一块文本中的任何地方进行匹配。仅当该行仅包含数字的字符时,这才匹配

– Verty00
10月29日13:44

@ Verty00查看以前的评论

–smileart
11月10日,0:53



#4 楼

为避免匹配空字符串,您需要重复四次模式,并依次用0替换每个1,并分别说明VLD: />在这种情况下(因为此模式使用^$),最好先检查空行,而不必打扰它们。如果您使用单词边界,那么就不会有问题,因为没有空单词之类的东西。 (至少正则表达式没有定义一个;不要开始哲学化,我在这里很务实!)


在我自己的特殊(现实世界)情况下,我需要在字尾,我发现没有其他方法可以解决。我需要清除纯文本文档中的脚注编号,其中“ Red Seacl and the Great Barrier Reefcli”之类的文本已转换为the Red Seacl and the Great Barrier Reefcli。但是我仍然对Tahitifantastic等有效词被擦洗到Tahitfantasti遇到问题。

评论


我有类似的问题(!):对项目列表(类型I或i的HTML OL)的剩余/残余罗马数字进行“左修剪”。因此,当有剩余时,我需要在项目文本的开头(左侧)使用正则表达式进行清理(如修整功能)...但是更简单:项目从不使用M或C或L,所以,你有这种简化的正则表达式吗?

– Peter Krauss
2014年11月11日20:00

... ok,在这里看来还可以(!),(X {1,3}(IX | IV | V?I {0,3})| X {0,3}(IX | I?V | V? I {1,3}))

– Peter Krauss
2014年11月11日20:21

您无需重复模式即可拒绝空字符串。您可以使用先行断言

– jfs
17年5月10日在20:09

#5 楼

幸运的是,数字范围限制为1..3999左右。因此,您可以构建正则表达式小块。例如,使用Perl表示法:

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


添加:<opt-hundreds-part>可以进一步压缩:

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


由于'D?C {0,3}'子句无法匹配,因此不需要问号。而且,最有可能的是,括号应该是非捕获类型-在Perl中: />
还可以扩展它以处理James Curran提到的选项(允许XM或IM用于990或999,CCCC用于400等)。 />

评论


从成千上万个单位开始,很容易创建一个FSM来计算和验证给定的罗马数字

– jfs
17年5月10日在20:08

幸运的是,数字的范围限制为1..3999左右。谁限制了它?

–SexyBeast
17年9月29日在14:32

@SexyBeast:5,000并没有标准的罗马记数法,更不用说更大的数字了,因此可以正常工作的规则就会停止工作。

–乔纳森·莱弗勒(Jonathan Leffler)
17年9月29日在15:09

不确定为什么会相信,但是罗马数字可以表示成百万的数字。 zh.wikipedia.org/wiki/Roman_numerals#Large_numbers

–安布罗斯礼拜堂
18-12-29 at 2:47

@AmbroseChapel:正如我所说的,5,000没有任何(单一)标准符号,更不用说更大的数字了。您必须使用所链接到的Wikipedia文章中概述的多种系统中的一种,并且在正字法,负数法或反向C字法下,系统正字法会遇到问题。您将必须向所有人解释您正在使用的系统及其含义;人们通常不会认识到M以外的罗马数字。那是您的特权,正如我有权支持我以前的评论一样。

–乔纳森·莱弗勒(Jonathan Leffler)
18/12/29在4:11

#6 楼

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'


对于真正想了解逻辑的人,请看一下关于deepintopython的3页上的逐步说明。里面有M{0,4})是因为我发现'MMMM'不是有效的罗马数字(同样,老罗马人很可能没有考虑过这么大的数字,因此会与我不同意)。如果您是不同意的古罗马人之一,请原谅我并使用{0,4}版本。

评论


答案中的正则表达式允许使用空数字。如果您不想要它;您可以使用前瞻性断言来拒绝空字符串(它也忽略字母的大小写)。

– jfs
17年5月10日在20:03



#7 楼

就我而言,我试图用文本内的一个单词查找并替换所有出现的罗马数字,因此我无法使用行的开头和结尾。因此@paxdiablo解决方案发现了许多零长度匹配项。
我最终得到以下表达式: br />
(?=\b[MCDXLVI]{1,6}\b)M{0,4}(?:CM|CD|D?C{0,3})(?:XC|XL|L?X{0,3})(?:IX|IV|V?I{0,3})

输出:

import re
text = "RULES OF LIFE: I. STAY CURIOUS; II. NEVER STOP LEARNING"
text = re.sub(r'(?=\b[MCDXLVI]{1,6}\b)M{0,4}(?:CM|CD|D?C{0,3})(?:XC|XL|L?X{0,3})(?:IX|IV|V?I{0,3})', 'ROMAN', text)
print(text)


评论


尝试输入“我是生活规则:I。保持虔诚; II。永不停止学习”,它会输出罗马生活规则:罗马。保持好奇罗马活到老学到老

– Ste
7月26日20:52

#8 楼

我在这里回答此问题的原因是Python中的罗马数字正则表达式
,因为它被标记为与该问题完全相同。

其名称可能相似,但这是一个特定的正则表达式问题/问题,从该问题的答案可以看出。

要查找的项可以组合成一个单独的替换项,然后
装入捕获组中,该捕获组将与findall()函数一起放入列表。 />是这样完成的:

>>> import re
>>> target = (
... r"this should pass v" + "\n"
... r"this is a test iii" + "\n"
... )
>>>
>>> re.findall( r"(?m)\s(i{1,3}v*|v)$", target )
['v', 'iii']


正则表达式对因数的修改和仅捕获数字是: >

#9 楼

正如Jeremy和Pax在上面指出的...
'^ M {0,4}(CM | CD | D?C {0,3})(XC | XL | L?X {0,3}) (IX | IV | V?I {0,3})$'应该是您追求的解决方案...

应附加的特定URL(IMHO)为
http://thehazeltree.org/diveintopython/7.html

示例7.8是使用{n,m}的缩写形式

#10 楼

我已经看到了多个答案,这些答案没有涵盖空字符串或使用先行方式解决了这个问题。我想添加一个新的答案,该答案涵盖空字符串并且不使用超前。正则表达式如下:

^(I[VX]|VI{0,3}|I{1,3})|((X[LC]|LX{0,3}|X{1,3})(I[VX]|V?I{0,3}))|((C[DM]|DC{0,3}|C{1,3})(X[LC]|L?X{0,3})(I[VX]|V?I{0,3}))|(M+(C[DM]|D?C{0,3})(X[LC]|L?X{0,3})(I[VX]|V?I{0,3}))$

我允许​​无限M,带有M+,但是如果有人愿意,当然可以更改为M{1,4}以只允许1或4 。

下面是一个可视化图表,可帮助您了解正在执行的操作,前面有两个在线演示: br />


#11 楼

下面的表达式对我有用,以验证罗马数字。数百个

M{0,4}将匹配十进制

C[MD]|D?C{0,3}将匹配单位演示:
Debuggex演示
Regex 101演示
Python代码:
^M{0,4}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$


#12 楼

史蒂文·莱维森(Steven Levithan)在其帖子中使用了此正则表达式,该正则表达式在对值进行“数字化”之前先验证罗马数字:

#13 楼

它可以在Java和PCRE正则表达式引擎中使用,现在应该可以在最新的JavaScript中使用,但可能无法在所有上下文中使用。但是,出于逻辑目的,这是最容易理解的。基本上,如果中间(?<![A-Z])(M*(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3}))(?![A-Z])前面有字母,则第一个(?<!)不匹配中间([MATCH]),如果后面有字母,则最后一个([MATCH])不匹配中间(?!)。 />中间的([MATCH])只是最常用于匹配罗马数字序列的正则表达式。但是现在,如果周围有任何字母,您就不想匹配它。

自己看看。
https://regexr.com/4vce5

#14 楼

@paxdiablo为避免匹配空字符串而建议的正向和反向查找对我来说似乎不起作用。
我通过使用负向反向查找来解决它:
(?!$)M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})

NB:如果您添加一些内容(例如,正则表达式末尾的“ foobar”,那么显然您必须用(?!$)替换(?!f)(其中f是“ foobar”的第一个字符)。

#15 楼

杰里米(Jeremy)和帕克斯(Pax)解决方案的问题是,它也确实匹配“ nothing”。 >

评论


那将不起作用(除非您使用的是非常怪异的regex实现)-|的左侧可以匹配一个空字符串和所有有效的罗马数字,因此右侧完全多余。是的,它仍然匹配一个空字符串。

–脏冰
2011年8月8日在23:40

“来自Jeremy和Pax的解决方案的问题是”……与该答案存在的问题完全相同。如果您打算为一个假定的问题提出解决方案,则可能应该进行测试。 :-)

– paxdiablo
15年7月12日在8:37



我有这个空字符串

– Aminah Nuraini
16年4月26日在10:51

#16 楼

我会为我的工作编写函数。
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
    {
    }
}