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

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

문제는 유효한 로마 숫자 만 일치합니다. 예를 들어, 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}

물론 더 큰 숫자를 허용하려면 수천의 숫자 (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

셋째, (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

정규식도 빈 문자열과 일치합니다. 이것을 원하지 않는다면 (그리고 정규식 엔진이 충분히 현대적이라면) 긍정적 인 룩 앤비와 룩어 헤드를 사용할 수 있습니다.

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

(다른 대안은 길이가 미리 0이 아닌지 확인하는 것입니다).


답변

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

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

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


답변

여기에 저장하십시오.

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


답변

빈 문자열을 일치 방지하기 위해 당신은 패턴 네 번 반복하고 각 교체해야합니다 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.


답변

다행히 숫자의 범위는 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} 버전을 사용하십시오.


답변

이 질문에 대답 하고 있습니다. 로마 숫자에 대한 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']

숫자를 고려하여 캡처하는 정규식 수정 사항은 다음과 같습니다.

 (?m)
 \s
 (                     # (1 start)
      i{1,3}
      v*
   |  v
 )                     # (1 end)
 $