184

私の他の問題について考えて、ローマ数字に一致する正規表現を作成することさえできないと判断しました(ローマ数字を生成する文脈自由文法は言うまでもありません)

問題は、有効なローマ数字のみを照合することです。たとえば、990 は「XM」ではなく、「CMXC」です。

これの正規表現を作成する際の問題は、特定の文字を許可または許可しないために、振り返る必要があることです。たとえば、数千と数百を考えてみましょう。

M{0,2}C?M を許可できます (900、1000、1900、2000、2900、および 3000 を許可するため)。ただ、試合がCMだと、次のキャラをCやDにすることはできません(すでに900なので)。

これを正規表現で表現するにはどうすればよいですか?
単に正規表現で表現できない場合、文脈自由文法で表現できますか?

4

17 に答える 17

360

これには、次の正規表現を使用できます。

^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}

もちろん、より大きな数を許可する場合は、数千の任意の数 (0 を含む) を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

3 番目に、(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})、単位のセクションで、前の 2 つのセクションと同様に処理09れます (ローマ数字は奇妙に見えますが、それが何であるかを理解すると、いくつかの論理規則に従います)。

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

その正規表現は空の文字列にも一致することに注意してください。これを望まない場合 (そして正規表現エンジンが最新の場合) は、肯定的な後読みと先読みを使用できます。

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

(もう 1 つの方法は、長さがゼロでないことを事前に確認することです)。

于 2008-11-06T01:35:41.510 に答える
27

実際、あなたの前提には欠陥があります。990 ISは「XM」、「CMXC」。

ローマ人は、あなたの 3 年生の教師よりも「規則」についてあまり気にしていませんでした。それが合算する限り、それはOKでした。したがって、"IIII" は 4 の "IV" と同じくらい優れていました。また、998 の "IIM" は完全にクールでした。

(これに対処するのが難しい場合は... 英語の綴りは 1700 年代まで形式化されていなかったことを思い出してください。それまでは、読者がそれを理解できる限り、それで十分でした)。

于 2008-11-06T01:51:36.237 に答える
13

空の文字列との一致を回避するには、パターンを 4 回繰り返し、それぞれを順番に0aに置き換え、 、 、および を考慮する必要があります。1VLD

(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}))

この場合 (このパターンでは^andが使用されているため$)、最初に空の行をチェックして、わざわざ一致させない方がよいでしょう。単語境界を使用している場合、空の単語などはないため、問題はありません。(少なくとも正規表現は定義していません。哲学を始めないでください。私はここで実用的です!)


私自身の特定の(現実の)ケースでは、単語の末尾に数字を一致させる必要がありましたが、それを回避する方法はありませんでした。「the Red Sea cl and the Great Barrier Reef cli」などのテキストがthe Red Seacl and the Great Barrier Reefcli. しかし、私はまだ and のような有効な単語に問題がTahitiあり、 andfantasticにスクラブされTahitていfantastiます。

于 2012-05-04T01:07:40.427 に答える
8

幸いなことに、数値の範囲は 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})/;

もちろん、すべて大文字と小文字を区別しない必要があります。

これを拡張して、James Curran が言及したオプションを処理することもできます (990 または 999 に対して XM または IM を許可し、400 に対して CCCC を許可するなど)。

<opt-hundreds-part> = m/(?:[IXC][MD]|D?C{0,4})/;
于 2008-11-06T01:36:18.950 に答える
1

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} を使用した短縮形です。

于 2008-11-06T03:03:07.433 に答える
1

この質問 の正確な複製としてマークされているため、 ここでローマ数字のPythonでの正規表現に答えています。

名前は似ているかもしれませんが、これは
その質問に対するこの回答からわかるように、特定の正規表現の質問/問題です。

検索対象の項目を 1 つの代替に結合してから
、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']

因数分解して数字だけをキャプチャするための正規表現の変更は次のとおりです。

 (?m)
 \s 
 (                     # (1 start)
      i{1,3} 
      v* 
   |  v
 )                     # (1 end)
 $
于 2019-12-12T18:50:05.040 に答える
0

Steven Levithan は、値を「非ローマ字化」する前にローマ数字を検証するこの正規表現を投稿で使用しています。

/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/
于 2014-06-22T19:26:33.570 に答える
-1

Jeremy と Pax の解決策の問題は、それが「nothing」にも一致することです。

次の正規表現では、少なくとも 1 つのローマ数字が必要です。

^(M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|[IDCXMLV])$
于 2011-03-16T14:12:41.907 に答える
-2

I would write functions to my work for me. Here are two roman numeral functions in 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
    {
    }
}
于 2015-01-01T11:59:14.767 に答える