로마 숫자의 유효성을 검사하는 파이썬 정규식

참고: 이것은 나의 첫 번째 게시물입니다, 당신이 그것을 좋아하기를 바랍니다 :)

나는 당신에게 거짓말을하지 않을거야 ... REGEX를 사랑 해요!



어렸을 때 저는 퍼즐과 수수께끼로 가득한 어드벤처 게임을 하며 자랐습니다. 해결책을 찾는 것은 개인 탐구, 보물 찾기였습니다. 그것을 찾는 것은 매우 흥미로웠지만, 다른 수수께끼에 뛰어드는 것만큼은 아니었습니다!

(훌륭한) 파이썬 프로그래머가 되기 위한 여정에서 정규 표현식을 발견했을 때도 같은 흥분을 느꼈습니다. 나는 그들이 제공하는 수많은 가능성에 놀랐습니다. 하나를 해독하는 것은 갑자기 상형문자를 읽을 수 있는 것과 같았고 하나를 쓰는 것은 내가 외국어를 말할 수 있다는 것을 발견하는 것과 같았습니다. 주의해서 사용해야 하고 특별한 경우에만 사용해야 한다는 것을 알고 있지만 가능한 모든 곳에서 사용하려고 계속 노력하고 있습니다.

그렇기 때문에 Codewars에서 로마 숫자와 아라비아 숫자를 변환하는 함수를 작성하라는 요청을 받았을 때 그 문제를 해결하는 데 도움이 되는 정규식 작성을 거부할 수 없었습니다.



잡담은 그만하고 손을 더럽히자.



내 첫 번째 작업은 사용자 입력이 유효한 로마 숫자인지 확인하는 것이 었습니다. 요약하면 로마 숫자는 다음 기호로 구성됩니다. 출처: Wikipedia

천 단위 [M]이 [MMM]을 넘어서 확장되지 않는 것 같습니다. 즉, 가장 큰 로마 숫자는 [MMMCMXCIX] 또는 3999가 됩니다. 어쨌든 이 문제를 위해 나는 1에서 3999 사이의 숫자로 자신을 제한했습니다.

이제 트릭은 기호 배치가 매우 중요하다는 것입니다. 올바른 순서로 배치하지 않으면 결과 숫자가 유효하지 않고 읽을 수 없게 됩니다. 위의 표에 나와 있듯이 숫자는 천[M] 1000으로 시작하여 백[D/C] 500/100, 수십[L/X] 50/10, 마지막으로 단위[V/1] 5가 되어야 합니다./1.

하지만 그게 아닙니다! 숫자는 [CD] 400과 같은 두 숫자의 콤보로 전환하기 전에 [CCC] 300과 같이 3번만 반복할 수 있습니다. 따라서 예를 들어 [CM] 900에서와 같이 [M] 1000 앞에 [C] 100을 계속 사용할 수 있습니다. .

조금 혼란스럽죠?

자, 조건을 요약해 보겠습니다.


  • 로마 숫자의 범위는 [I] 1에서 [MMMCMXCIX] 3999
  • 까지입니다.
  • 숫자는 정확한 순서를 따라야 합니다: [M] 1000/[D] 500/[C] 100/[L] 50/[X] 10/[V] 5/[I] 1
  • 숫자는 3번 이상 반복할 수 없으며 쌍을 사용합니다
  • .
  • 다음 쌍이 허용됩니다. [CM] 900/[CD] 400/[XC] 90/[XL] 40/[IX] 9/[IV] 4

  • REGEX가 표시되기 시작합니까? :)

    이것을 코드로 번역해 봅시다.



    이를 위해 우리는 정규식을 작성할 때 정말 도움이 되는 태그를 사용할 것입니다. 이 태그는 장황한 것(re.VERBOSE 또는 re.X)입니다. 이를 통해 패턴을 여러 줄에 분산시키고 더 읽기 쉽습니다. 해 봅시다!

    import re
    
    def is_roman_number(num):
    
        pattern = re.compile(r"""   
                                    ^M{0,3}
                                    (CM|CD|D?C{0,3})?
                                    (XC|XL|L?X{0,3})?
                                    (IX|IV|V?I{0,3})?$
                """, re.VERBOSE)
    
        if re.match(pattern, num):
            return True
    
        return False
    


    와, 벌써 놀랍네요! 다음 4줄을 자세히 살펴보겠습니다.

  • ^M{0,3} = 문자열의 시작 부분 [^]에서 0과 3 사이의 [M]

  • (CM|CD|D?C{0,3})? = 1쌍[CM] 또는 1쌍[CD] 또는 [D], 최대 3개[C]. 각 요소는 선택적 [?]이며 전체 블록 [()?]

  • (XC|XL|L?X{0,3})? = 한 쌍 [XC] 또는 한 쌍 [XL] 또는 [L], 최대 3개 [X]. 각 요소는 선택적 [?]이며 전체 블록 [()?]

  • (IX|IV|V?I{0,3})?$ = 한 쌍 [IX] 또는 한 쌍 [IV] 또는 [V], 최대 3 [I]까지. 각 요소는 선택 사항 [?]이며 전체 블록 [()?]은 문자열 [$] 끝에 있어야 합니다
  • .

    코드를 테스트해 봅시다



    내 함수를 호출하는 간단한 fstring을 사용하고 문자열을 패턴과 비교하여 숫자의 유효성을 검사합니다.

    
    num_valid = 'MMDCCLXXIII'
    num_invalid = 'CCCMMVIIVV'
    
    print(f"{num_valid} is {'not' if not is_roman_number(num_valid) else ''}a roman number")
    print(f"{num_invalid} is {'not ' if not is_roman_number(num_invalid) else ''}a roman number")
    
    # Output:
    # MMDCCLXXIII is a roman number
    # CCCMMVIIVV is not a roman number
    


    결국 그렇게 나쁘지 않았습니다! 이제 이것을 보고 당신이 인생에서 본 것 중 가장 아름다운 것이 아니라고 말해주세요.

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




    그게 다야! 챌린지의 두 번째 부분인 로마 숫자를 아라비아 숫자로/로 변환하는 데 관심이 있으시면 알려주세요. 제 솔루션을 공유하겠습니다.

    밖에서 안전하게 지내고 곧 읽어보세요 :)

    좋은 웹페이지 즐겨찾기