Programing

유효한 로마 숫자 만 정규식과 어떻게 일치합니까?

lottogame 2020. 6. 10. 23:16
반응형

유효한 로마 숫자 만 정규식과 어떻게 일치합니까?


내 다른 문제 에 대해 생각하면서 로마 숫자와 일치하는 정규 표현식을 만들 수도 없다고 결정했습니다 (문맥이없는 문법은 물론 문법을 생성합니다)

문제는 유효한 로마 숫자 만 일치합니다. 예를 들어, 990은 "XM"이 아니라 "CMXC"입니다.

정규 표현식을 만들 때 내 문제는 특정 문자를 허용하거나 허용하지 않기 위해 되돌아 봐야한다는 것입니다. 예를 들어 수천과 수백을 봅시다.

M {0,2} C? M을 허용 할 수 있습니다 (900, 1000, 1900, 2000, 2900 및 3000 허용). 그러나 경기가 CM에 있으면 다음 문자를 C 또는 D로 허용 할 수 없습니다 (이미 900에 있기 때문에).

정규식으로 어떻게 표현할 수 있습니까?
정규식으로 표현할 수없는 경우 문맥없는 문법으로 표현할 수 있습니까?


시험:

^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이전 두 섹션 (당신은 그들이 무엇인지 파악하면 로마 숫자는, 자신의 보이는 불확실성에도 불구하고, 몇 가지 논리적 규칙을 따라야)에 유사한 :

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

실제로, 전제에 결함이 있습니다. 990 IS "XM"뿐만 아니라 "CMXC".

로마인들은 3 학년 교사보다 "규칙"에 대해 훨씬 덜 걱정했습니다. 합산하면 괜찮습니다. 따라서 "IIII"은 4의 "IV"와 같았으며 "IIM"은 998의 온도로 완전히 시원했습니다.

(만약 당신이 그걸 다루는데 어려움이 있다면 ... 1700 년대까지 영어 철자가 공식화되지 않았다는 것을 기억하십시오. 그때까지 독자가 알아낼 수있는 한 충분했습니다.)


빈 문자열을 일치 방지하기 위해 당신은 패턴 네 번 반복하고 각 교체해야합니다 0A를을 1위해 차례로 및 계정 V, LD:

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

이 경우 (이 패턴을 사용하기 때문에 ^그리고 $당신이 첫 번째 빈 행을 검사 나을 것하고 일치 귀찮게하지 않습니다). 단어 경계사용 하는 경우 빈 단어와 같은 것이 없기 때문에 문제가 없습니다. (적어도 정규 표현식은 정의하지 않으며 철학을 시작하지 마십시오. 실용적입니다!)


내 자신의 특정 (실제) 경우에는 단어 끝에서 일치하는 숫자가 필요했고 그 주위에 다른 방법을 찾지 못했습니다. "홍해 cl 및 그레이트 베리어 리프 cli " 같은 텍스트 가로 변환 된 일반 텍스트 문서에서 각주 번호를 제거 해야했습니다 the Red Seacl and the Great Barrier Reefcli. 하지만 난 여전히 같은 유효한 단어에 대한 문제가 있었다 Tahitifantastic로 세정하는 Tahitfantasti.


여기에 저장하십시오.

(^(?=[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


다행히 숫자의 범위는 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})/;

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'

논리를 정말로 이해하고 싶은 사람들을 위해 diveintopython의 3 페이지에 대한 단계별 설명을 살펴보십시오 .

원래 솔루션과의 유일한 차이점은 M{0,4}'MMMM'이 유효한 로마 숫자가 아님을 발견했기 때문입니다 (오래된 로마인은 아마도 그 큰 수에 대해 생각하지 않았고 나와 동의하지 않을 것입니다). 당신이 오래된 로마인들에 동의하지 않는다면, 나를 용서하고 {0,4} 버전을 사용하십시오.


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입니다.

Example 7.8은 {n, m}을 사용한 짧은 형식입니다.


Steven Levithan은 자신의 게시물 에서이 정규 표현식을 사용 하여 값을 "로만 표기"하기 전에 로마 숫자를 확인합니다.

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

필자의 경우 로마 숫자의 모든 항목을 찾아서 텍스트 내에서 한 단어로 바꾸려고했기 때문에 줄의 시작과 끝을 사용할 수 없었습니다. 따라서 @paxdiablo 솔루션은 많은 길이가 0 인 일치 항목을 찾았습니다. 나는 다음과 같은 표현으로 끝났다.

(?=\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)

산출:

RULES OF LIFE: ROMAN. STAY CURIOUS; ROMAN. NEVER STOP LEARNING

Jeremy와 Pax의 솔루션 문제는 "아무것도"일치하지 않는다는 것입니다.

다음 정규식은 하나 이상의 로마 숫자를 예상합니다.

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

나는 나를 위해 내 일에 기능을 쓸 것이다. 다음은 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
    {
    }
}

참고 URL : https://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression

반응형