"re" --- 정규식 연산
********************

**소스 코드:** Lib/re.py

======================================================================

이 모듈은 Perl에 있는 것과 유사한 정규식 일치 연산을 제공합니다.

패턴과 검색 할 문자열은 모두 유니코드 문자열("str")과 8비트 문자열
("bytes")이 될 수 있습니다. 그러나, 유니코드 문자열과 8비트 문자열은
혼합될 수 없습니다: 즉, 유니코드 문자열을 바이트열 패턴과 일치시킬 수
없으며 그 반대도 마찬가지입니다; 마찬가지로, 치환을 요청할 때, 치환 문
자열은 패턴과 검색 문자열과 같은 형이어야 합니다.

정규식은 역 슬래시 문자("'\'")를 사용하여 특수 형식을 나타내거나 특별
한 의미를 갖지 않고 특수 문자를 사용할 수 있게 합니다. 이것은 문자열
리터럴에서 같은 목적을 위해 같은 문자를 사용하는 파이썬과 충돌합니다;
예를 들어, 리터럴 역 슬래시와 일치시키려면 "'\\\\'"를 패턴 문자열로 작
성해야 하는데, 정규식은 "\\" 여야하고, 각 역 슬래시는 일반 파이썬 문자
열 리터럴 내에서 "\\"로 표현되어야 하기 때문입니다. 또한, 파이썬의 문
자열 리터럴에서 역 슬래시가 사용될 때 유효하지 않은 이스케이프 시퀀스
는 이제 "DeprecationWarning"을 생성하고 앞으로는 "SyntaxError"가 될 것
이라는 점에 유의하십시오. 이 동작은 정규식에서 유효한 이스케이프 시퀀
스인 경우에도 발생합니다.

해결책은 정규식 패턴에 파이썬의 날 문자열(raw string) 표기법을 사용하
는 것입니다; 역 슬래시는 "'r'" 접두어가 붙은 문자열 리터럴에서 특별한
방법으로 처리되지 않습니다. 따라서 "r"\n""은 "'\'"와 "'n'"을 포함하는
두 글자 문자열이고, ""\n""은 개행을 포함하는 한 글자 문자열입니다. 일
반적으로 패턴은, 이 날 문자열 표기법을 사용하여 파이썬 코드로 표현됩니
다.

대부분 정규식 연산은 모듈 수준 함수와 컴파일된 정규식의 메서드로 사용
할 수 있다는 점에 유의해야 합니다. 함수는 정규식 객체를 먼저 컴파일할
필요가 없도록 하는 바로 가기이지만, 일부 미세 조정 매개 변수가 빠져있
습니다.

더 보기:

  제삼자 regex 모듈은 표준 라이브러리 "re" 모듈과 호환되는 API를 가지
  고 있지만, 추가 기능과 더 철저한 유니코드 지원을 제공합니다.


정규식 문법
===========

정규식(또는 RE)은 일치하는 문자열 집합을 지정합니다; 이 모듈의 함수는
특정 문자열이 주어진 정규식과 일치하는지 확인할 수 있도록 합니다 (또는
주어진 정규식이 특정 문자열과 일치하는지, 결국 같은 결과를 줍니다).

정규식을 이어붙여서 새로운 정규식을 만들 수 있습니다; *A*와 *B*가 모두
정규식이면 *AB*도 정규식입니다. 일반적으로 문자열 *p*가 *A*와 일치하고
다른 문자열 *q*가 *B*와 일치하면 문자열 *pq*가 AB와 일치합니다. 이것은
*A*나 *B*가 우선순위가 낮은 연산, *A*와 *B* 사이의 경계 조건 또는 숫자
그룹 참조를 포함하지 않는 한 성립합니다. 따라서, 복잡한 정규식은 여기
에 설명된 것과 같은 더 단순한 기본 정규식으로 쉽게 구성할 수 있습니다.
정규식의 이론과 구현에 관한 자세한 내용은 Friedl 책 [Frie09], 또는 컴
파일러 작성에 관한 거의 모든 교과서를 참조하십시오.

정규식의 형식에 대한 간단한 설명이 이어집니다. 더 자세한 정보와 더 친
절한 소개는 정규식 HOWTO를 참조하십시오.

정규식은 특수 문자와 일반 문자를 모두 포함 할 수 있습니다. "'A'",
"'a'" 또는 "'0'"과 같은 대부분의 일반 문자는 가장 단순한 정규식입니다;
그들은 단순히 자신과 일치합니다. 일반 문자를 이어 붙일 수 있어서,
"last"는 "'last'" 문자열과 일치합니다. (이 절의 나머지 부분에서는, RE
를 (보통 따옴표 없이) "이런 특별한 스타일"로, 일치할 문자열은 "'작은따
옴표 안에'" 씁니다.)

"'|'"나 "'('"와 같은 일부 문자는 특수합니다. 특수 문자는 일반 문자의
클래스를 나타내거나, 그 주변의 정규식이 해석되는 방식에 영향을 줍니다.

반복 한정자("*", "+", "?", "{m,n}" 등)는 직접 중첩될 수 없습니다. 이렇
게 하면 비 탐욕적인 수정자 접미사인 "?"와 다른 구현의 다른 수정자와의
모호함을 피할 수 있습니다. 내부 반복에 두 번째 반복을 적용하려면 괄호
를 사용할 수 있습니다. 예를 들어, 정규식 "(?:a{6})*"는 여섯 개의 "'a'"
문자가 임의로 반복되는 것과 일치합니다.

특수 문자는 다음과 같습니다:

"."
   (점.) 기본 모드에서, 이것은 개행 문자를 제외한 모든 문자와 일치합니
   다. "DOTALL" 플래그가 지정되면, 개행 문자를 포함한 모든 문자와 일치
   합니다.

"^"
   (캐럿.) 문자열의 시작과 일치하고, "MULTILINE" 모드에서는 각 개행 직
   후에도 일치합니다.

"$"
   문자열의 끝이나 문자열 끝의 개행 문자 바로 직전과 일치하고,
   "MULTILINE" 모드에서는 개행 문자 앞에서도 일치합니다. "foo"는 'foo'
   와 'foobar'를 모두 일치시키는 반면, 정규식 "foo$"는 'foo' 만 일치합
   니다. 흥미롭게도, "'foo1\nfoo2\n'"에서 "foo.$"를 검색하면 'foo2' 는
   정상적으로 일치되지만, 'foo1'은 "MULTILINE" 모드에서 검색됩니다;
   "'foo\n'"에서 단일 "$"를 검색하면 두 개의 (빈) 일치가 발견됩니다:
   하나는 개행 직전에, 다른 하나는 문자열 끝에.

"*"
   결과 RE가 선행 RE의 가능한 한 많은 0회 이상의 반복과 일치하도록 합
   니다. "ab*"는 'a', 'ab' 또는 'a' 다음에 임의의 수의 'b'가 오는 것과
   일치합니다.

"+"
   결과 RE가 선행 RE의 1회 이상의 반복과 일치하도록 합니다. "ab+"는
   'a' 다음에 하나 이상의 'b'가 오는 것과 일치합니다; 단지 'a'와는 일
   치하지 않습니다.

"?"
   결과 RE가 선행 RE의 0 또는 1 반복과 일치하도록 합니다. "ab?"는 'a'
   나 'ab'와 일치합니다.

"*?", "+?", "??"
   "'*'", "'+'" 및 "'?'" 한정자는 모두 *탐욕적 (greedy)*입니다; 가능한
   한 많은 텍스트와 일치합니다. 때로는 이 동작이 바람직하지 않습니다;
   RE "<.*>"를 "'<a> b <c>'"와 일치시키면, "'<a>'"가 아닌 전체 문자열
   과 일치합니다. 한정자 뒤에 "?"를 추가하면 *비 탐욕적 (non-greedy)*
   또는 *최소 (minimal)* 방식으로 일치를 수행합니다; 가능하면 *적은*
   문자가 일치합니다. RE "<.*?>"를 사용하면 "'<a>'" 만 일치합니다.

"{m}"
   선행 RE의 정확히 *m* 복사가 일치하도록 지정합니다; 적은 횟수의 일치
   는 전체 RE가 일치하지 않게 됩니다. 예를 들어, "a{6}"는 정확히 6개의
   "'a'" 문자와 일치하지만, 5개의 문자와는 일치하지 않습니다.

"{m,n}"
   결과 RE를 선행 RE의 *m*에서 *n* 사이의 최대한 많은 반복과 일치하도
   록 합니다. 예를 들어, "a{3,5}"는 3에서 5개의 "'a'" 문자와 일치합니
   다. *m*을 생략하면 하한값 0이 지정되고, *n*을 생략하면 무한한 상한
   이 지정됩니다. 예를 들어, "a{4,}b"는 "'aaaab'" 나 1000개의 "'a'" 문
   자와 "'b'"가 일치하지만, "'aaab'"는 일치하지 않습니다. 콤마는 생략
   할 수 없습니다, 그렇지 않으면 한정자가 앞에서 설명한 형식과 혼동될
   수 있습니다.

"{m,n}?"
   결과 RE를 선행 RE의 *m*에서 *n* 사이의 가능한 한 *적은* 반복과 일치
   하도록 합니다. 이것은 이전 한정자의 비 탐욕적 버전입니다. 예를 들어
   , 6문자 문자열 "'aaaaaa'"에서, "a{3,5}"는 5개의 "'a'" 문자와 일치하
   고, "a{3,5}?"는 3개의 문자만 일치합니다.

"\"
   특수 문자를 이스케이프 하거나 ("'*'", "'?'" 등의 문자를 일치시킬 수
   있도록 합니다), 특수 시퀀스를 알립니다; 특수 시퀀스는 아래에서 설명
   합니다.

   날 문자열을 사용하여 패턴을 표현하지 않는다면, 파이썬이 문자열 리터
   럴에서 이스케이프 시퀀스로 역 슬래시를 사용한다는 것을 기억하십시오
   ; 이스케이프 시퀀스가 파이썬의 구문 분석기에 의해 인식되지 않으면,
   역 슬래시와 후속 문자가 결과 문자열에 포함됩니다. 그러나 파이썬이
   결과 시퀀스를 인식한다면 역 슬래시는 두 번 반복되어야 합니다. 이것
   은 복잡하고 이해하기 어렵기 때문에, 가장 단순한 표현 이외에는 날 문
   자열을 사용하는 것이 좋습니다.

"[]"
   문자 집합을 나타내는 데 사용됩니다. 집합 안에서:

   * 문자는 개별적으로 나열 할 수 있습니다, 예를 들어 "[amk]"는 "'a'",
     "'m'" 또는 "'k'"와 일치합니다.

   * 문자의 범위는 "'-'"로 구분된 두 문자를 주고는 것으로 나타낼 수 있
     습니다, 예를 들어 "[a-z]"는 모든 소문자 ASCII 문자와 일치하고,
     "[0-5][0-9]"는 "00"에서 "59"까지의 모든 두 자리 숫자와 일치하며,
     "[0-9A-Fa-f]"는 모든 16진수와 일치합니다. "-"가 이스케이프 처리되
     거나 (예를 들어 "[a\-z]") 첫 번째나 마지막 문자로 배치되면 (예를
     들어 "[-a]"나 "[a-]") 리터럴 "'-'"와 일치합니다.

   * 특수 문자는 집합 내에서 특별한 의미를 상실합니다. 예를 들어,
     "[(+*)]"는 리터럴 문자 "'('", "'+'", "'*'" 또는 "')'"와 일치합니
     다.

   * "\w" 나 "\S"(아래에서 정의됩니다)와 같은 문자 클래스도 집합 내에
     서 허용되지만, 일치하는 문자는 "ASCII"나 "LOCALE" 모드가 유효한지
     에 따라 다릅니다.

   * 범위 내에 있지 않은 문자는 *여집합(complementing)*을 만들어 일치
     할 수 있습니다. 집합의 첫 번째 문자가 "'^'"이면, 집합에 속하지 *
     않은* 모든 문자가 일치합니다. 예를 들어, "[^5]"는 "'5'"를 제외한
     모든 문자와 일치하며, "[^^]"는 "'^'"를 제외한 모든 문자와 일치합
     니다. "^"는 집합의 첫 번째 문자가 아닐 때 특별한 의미가 없습니다.

   * To match a literal "']'" inside a set, precede it with a
     backslash, or place it at the beginning of the set.  For example,
     both "[()[\]{}]" and "[]()[{}]" will match a right bracket, as
     well as left bracket, braces, and parentheses.

   * 유니코드 기술 표준 #18 에서 정의하는 중첩 집합과 집합 연산의 지원
     이 다음에 추가될 수 있습니다. 이것은 문법을 변경하므로, 이 변경을
     용이하게 하기 위해 당분간 "FutureWarning"이 모호한 경우에 발생합
     니다. 여기에는 리터럴 "'['"로 시작하거나 리터럴 문자 시퀀스
     "'--'", "'&&'", "'~~'" 및 "'||'"가 포함된 집합이 포함됩니다. 경고
     를 피하려면 역 슬래시로 이스케이프 처리하십시오.

   버전 3.7에서 변경: 문자 집합이 미래에 의미가 변할 구조를 포함하고
   있다면 "FutureWarning"이 발생합니다.

"|"
   "A|B"(여기서 *A*와 *B*는 임의의 RE일 수 있습니다)는 *A*나 *B*와 일
   치하는 정규식을 만듭니다. 이러한 방식으로 임의의 수의 RE를 "'|'"로
   분리 할 수 있습니다. 이것은 그룹(아래를 참조하세요)에서도 사용할 수
   있습니다. 대상 문자열이 스캔 될 때 "'|'"로 구분된 RE는 왼쪽에서 오
   른쪽으로 시도됩니다. 한 패턴이 완전히 일치하면, 해당 분기가 받아들
   여집니다. 이는 일단 *A*가 일치하면, *B*는 전체적으로 더 긴 일치를
   생성하더라도 더 검사되지 않는다는 것을 뜻합니다. 즉, "'|'" 연산자는
   절대로 탐욕적이지 않습니다. 리터럴 "'|'"와 일치시키려면, "\|"를 사
   용하거나, "[|]"처럼 문자 클래스 안에 넣으십시오.

"(...)"
   괄호 안에 있는 정규식과 일치하며, 그룹의 시작과 끝을 나타냅니다; 그
   룹의 내용은 일치가 수행된 후 조회할 수 있으며, 나중에 문자열에서
   "\number" 특수 시퀀스로 일치시킬 수 있습니다 (아래에서 설명됩니다).
   리터럴 "'('"나 "')'"를 일치시키려면, "\("나 "\)"를 사용하거나, 문자
   클래스 안에 넣으십시오: "[(]", "[)]".

"(?...)"
   이것은 확장 표기법입니다 (그렇지 않으면 "'('" 다음에 오는 "'?'``는
   의미가 없습니다). ``'?'" 다음의 첫 번째 문자는 확장의 의미와 이후의
   문법을 결정합니다. 확장은 대개 새 그룹을 만들지 않습니다; 이 규칙에
   대한 유일한 예외는 "(?P<name>...)"입니다. 다음은 현재 지원되는 확장
   입니다.

"(?aiLmsux)"
   (집합 "'a'", "'i'", "'L'", "'m'", "'s'", "'u'", "'x'"의 문자 중 하
   나 이상.) 그룹은 빈 문자열과 일치합니다; 문자는 해당 플래그를 전체
   정규식에 대해 설정합니다: "re.A" (ASCII만 일치), "re.I" (케이스 무
   시), "re.L" (로케일 종속), "re.M" (여러 줄), "re.S" (점이 모든 문자
   와 일치), "re.U" (유니코드 일치) 및 "re.X" (상세 모드). (플래그는
   모듈 내용에 설명되어 있습니다.) "re.compile()" 함수에 *flag* 인자를
   전달하는 대신, 정규식의 일부로 플래그를 포함하려는 경우에 유용합니
   다. 플래그는 정규식 문자열에서 처음에 사용해야 합니다.

"(?:...)"
   일반 괄호의 비 포착 버전. 괄호 안의 정규식과 일치하지만, 그룹과 일
   치하는 부분 문자열은 일치를 수행한 후 조회하거나 나중에 패턴에서 참
   조할 수 *없습니다*.

"(?aiLmsux-imsx:...)"
   (집합 "'a'", "'i'", "'L'", "'m'", "'s'", "'u'", "'x'"의 문자 중 0개
   이상, 선택적으로 "'-'"와 그 뒤에 "'i'", "'m'", "'s'", "'x'" 중 하나
   이상의 문자가 따라옵니다.) 문자는 해당 플래그를 정규식의 일부에 대
   해 설정하거나 제거합니다: "re.A" (ASCII만 일치), "re.I" (케이스 무
   시), "re.L" (로케일 종속), "re.M" (여러 줄), "re.S" (점이 모든 문자
   와 일치), "re.U" (유니코드 일치) 및 "re.X" (상세 모드). (플래그는
   모듈 내용에 설명되어 있습니다.)

   문자 "'a'", "'L'" 및 "'u'"는 인라인 플래그로 사용될 때 상호 배타적
   이므로, "'-'"와 결합하거나 그 뒤에 올 수 없습니다. 대신, 이 중 하나
   가 인라인 그룹에 나타나면, 그것은 둘러싸는 그룹의 일치 모드를 재정
   의합니다. 유니코드 패턴에서 "(?a:...)"는 ASCII 전용 일치로 전환하고
   , "(?u:...)"는 유니코드 일치(기본값)로 전환합니다. 바이트열 패턴에
   서 "(?L:...)"는 로케일 종속 일치로 전환하고, "(?a:...)"는 ASCII 전
   용 일치(기본값)로 전환합니다. 이 재정의는 좁은 인라인 그룹에 대해서
   만 적용되며, 원래의 일치 모드는 그룹 밖에서 복원됩니다.

   버전 3.6에 추가.

   버전 3.7에서 변경: 문자 "'a'", "'L'" 및 "'u'"도 그룹에서 사용할 수
   있습니다.

"(?P<name>...)"
   일반 괄호와 유사하지만, 그룹과 일치하는 부분 문자열은 기호 그룹 이
   름 *name*을 통해 액세스 할 수 있습니다. 그룹 이름은 유효한 파이썬
   식별자여야 하며, 각 그룹 이름은 정규식 내에서 한 번만 정의해야 합니
   다. 기호 그룹은 번호 그룹이기도 합니다, 마치 그룹이 이름 붙지 않은
   것처럼.

   이름 있는 그룹은 세 가지 문맥에서 참조될 수 있습니다. 패턴이
   "(?P<quote>['"]).*?(?P=quote)"면 (즉, 작은따옴표나 큰따옴표로 인용
   된 문자열과 일치):

   +-----------------------------------------+------------------------------------+
   | 그룹 "quote"에 대한 참조 문맥           | 참조하는 방법                      |
   |=========================================|====================================|
   | 같은 패턴 자체에서                      | * "(?P=quote)" (보이는 대로)  *    |
   |                                         | "\1"                               |
   +-----------------------------------------+------------------------------------+
   | 일치 객체 *m*을 처리할 때               | * "m.group('quote')"  *            |
   |                                         | "m.end('quote')" (등)              |
   +-----------------------------------------+------------------------------------+
   | "re.sub()"의 *repl* 인자로 전달되는 문  | * "\g<quote>"  * "\g<1>"  * "\1"   |
   | 자열에서                                |                                    |
   +-----------------------------------------+------------------------------------+

"(?P=name)"
   이름있는 그룹에 대한 역참조; *name*이라는 이름의 앞선 그룹과 일치하
   는 텍스트와 일치합니다.

"(?#...)"
   주석; 괄호의 내용은 단순히 무시됩니다.

"(?=...)"
   "..."가 다음과 일치하면 일치하지만, 문자열을 소비하지는 않습니다.
   이를 *미리 보기 어서션 (lookahead assertion)*이라고 합니다. 예를 들
   어, "Isaac (?=Asimov)"는 "'Asimov'"가 뒤따를 때만 "'Isaac '"과 일치
   합니다.

"(?!...)"
   "..."가 다음과 일치하지 않으면 일치합니다. 이것은 *부정적인 미리 보
   기 어서션 (negative lookahead assertion)*입니다. 예를 들어, "Isaac
   (?!Asimov)"는 "'Asimov'"가 뒤따르지 *않을* 때 만 "'Isaac '"과 일치
   합니다.

"(?<=...)"
   문자열의 현재 위치 앞에 현재 위치에서 끝나는 "..."와의 일치가 있으
   면 일치합니다. 이를 *긍정적인 되돌아보기 어서션 (positive
   lookbehind assertion)*이라고 합니다. 되돌아보기가 3문자를 백업하고
   포함된 패턴이 일치하는지 확인하기 때문에 "(?<=abc)def"는 "'abcdef'"
   에서 일치를 찾습니다. 포함된 패턴은 고정 길이의 문자열과 일치해야
   합니다, 즉, "abc"나 "a|b"는 허용되지만, "a*"와 "a{3,4}"는 허용되지
   않습니다. 긍정적인 되돌아보기 어서션으로 시작하는 패턴은 검색되는
   문자열의 시작 부분에서 일치하지 않음에 유의하십시오; "match()" 함수
   보다는 "search()" 함수를 사용하기를 원할 것입니다:

   >>> import re
   >>> m = re.search('(?<=abc)def', 'abcdef')
   >>> m.group(0)
   'def'

   이 예에서는 하이픈 다음의 단어를 찾습니다:

   >>> m = re.search(r'(?<=-)\w+', 'spam-egg')
   >>> m.group(0)
   'egg'

   버전 3.5에서 변경: 고정 길이의 그룹 참조에 대한 지원이 추가되었습니
   다.

"(?<!...)"
   문자열의 현재 위치 앞에 "..."와의 일치가 없으면 일치합니다. 이를 *
   부정적인 뒤돌아보기 어서션 (negative lookbehind assertion)*이라고
   합니다. 긍정적인 뒤돌아보기 어서션과 마찬가지로, 포함된 패턴은 고정
   길이의 문자열과 일치해야 합니다. 부정적인 뒤돌아보기 어서션으로 시
   작하는 패턴은 검색되는 문자열의 시작 부분에서 일치 할 수 있습니다.

"(?(id/name)yes-pattern|no-pattern)"
   주어진 *id*나 *name*의 그룹이 있으면 "yes-pattern"과, 그렇지 않으면
   "no-pattern"과 일치하려고 시도합니다. "no-pattern"은 선택적이며 생
   략될 수 있습니다. 예를 들어, "(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)"는
   정교하지 않은 전자 메일 일치 패턴인데, "'<user@host.com>'" 및
   "'user@host.com'"과 일치하지만, "'<user@host.com'"이나
   "'user@host.com>'"과는 일치하지 않습니다.

특수 시퀀스는 "'\'"와 아래 목록의 문자로 구성됩니다. 일반 문자가 ASCII
숫자나 ASCII 글자가 아니면, 결과 RE는 두 번째 문자와 일치합니다. 예를
들어, "\$"는 문자 "'$'"와 일치합니다.

"\number"
   같은 번호의 그룹 내용과 일치합니다. 그룹은 1부터 번호가 매겨집니다.
   예를 들어, "(.+) \1"은 "'the the'"나 "'55 55'"와 일치하지만,
   "'thethe'"와는 일치하지 않습니다 (그룹 뒤의 공백에 유의하십시오).
   이 특수 시퀀스는 첫 99개 그룹 중 하나와 일치하는 데에만 사용될 수
   있습니다. *number*의 첫 자릿수가 0이거나, *number*가 3 자릿수면, 그
   룹 일치로 해석되지 않고, 8진수 값 *number*를 갖는 문자로 해석됩니다
   . 문자 클래스의 "'['"와 "']'" 안에서는, 모든 숫자 이스케이프가 문자
   로 처리됩니다.

"\A"
   문자열의 시작 부분에서만 일치합니다.

"\b"
   빈 문자열과 일치하지만, 단어의 처음이나 끝에만 일치합니다. 단어는
   단어 문자의 시퀀스로 정의됩니다. 형식적으로, "\b"는 "\w"와 "\W" 문
   자 사이의 (또는 그 반대), 또는 "\w"와 문자열 시작/끝 사이의 경계로
   정의됩니다. 즉, "r'\bfoo\b'"는 "'foo'", "'foo.'", "'(foo)'", "'bar
   foo baz'"와는 일치하지만, "'foobar'"나 "'foo3'"와는 일치하지 않습니
   다.

   기본적으로 유니코드 영숫자가 유니코드 패턴에서 사용되는 것이지만,
   "ASCII" 플래그를 사용하여 변경할 수 있습니다. "LOCALE" 플래그가 사
   용되면, 단어 경계는 현재 로케일에 의해 결정됩니다. 문자 범위 내에서
   , 파이썬의 문자열 리터럴과의 호환성을 위해, "\b"는 백스페이스 문자
   를 나타냅니다.

"\B"
   단어의 시작이나 끝에 있지 *않을* 때만 빈 문자열과 일치합니다. 즉,
   "r'py\B'"는 "'python'", "'py3'", "'py2'"와 일치하지만, "'py'",
   "'py.'" 또는 "'py!'"와는 일치하지 않습니다. "\B"는 단지 "\b"의 반대
   이므로, 유니코드 패턴의 단어 문자는 유니코드 영숫자나 밑줄입니다.
   "ASCII" 플래그를 사용하여 변경할 수 있습니다. "LOCALE" 플래그가 사
   용되면 단어 경계는 현재 로케일에 의해 결정됩니다.

"\d"
   유니코드 (str) 패턴일 때:
      모든 유니코드 십진 숫자(즉, 유니코드 문자 범주 [Nd]의 모든 문자)
      와 일치합니다. 여기에는 "[0-9]" 및 다른 많은 숫자가 포함됩니다.
      "ASCII" 플래그가 사용되면 "[0-9]"만 일치합니다.

   8비트 (bytes) 패턴일 때:
      모든 십진 숫자와 일치합니다; 이것은 "[0-9]"와 동등합니다.

"\D"
   십진 숫자가 아닌 모든 문자와 일치합니다. 이것은 "\d"의 반대입니다.
   "ASCII" 플래그를 사용하면 "[^0-9]"와 동등합니다.

"\s"
   유니코드 (str) 패턴일 때:
      유니코드 공백 문자("[ \t\n\r\f\v]"와 많은 다른 문자들, 예를 들어
      많은 언어에서 타이포그래피 규칙에 의해 강제된 분리할 수 없는 스
      페이스(non-breaking spaces))와 일치합니다. "ASCII" 플래그가 사용
      되면, "[ \t\n\r\f\v]"만 일치합니다.

   8비트 (bytes) 패턴일 때:
      ASCII 문자 집합에서 공백으로 간주하는 문자와 일치합니다; 이것은
      "[ \t\n\r\f\v]"와 동등합니다.

"\S"
   공백 문자가 아닌 모든 문자와 일치합니다. 이것은 "\s"의 반대입니다.
   "ASCII" 플래그를 사용하면 "[^ \t\n\r\f\v]"와 동등합니다.

"\w"
   유니코드 (str) 패턴일 때:
      Matches Unicode word characters; this includes alphanumeric
      characters (as defined by "str.isalnum()") as well as the
      underscore ("_"). If the "ASCII" flag is used, only
      "[a-zA-Z0-9_]" is matched.

   8비트 (bytes) 패턴일 때:
      ASCII 문자 집합에서 영숫자로 간주하는 문자와 일치합니다; 이것은
      "[a-zA-Z0-9_]"와 동등합니다. "LOCALE" 플래그를 사용하면, 현재 로
      케일에서 영숫자로 간주하는 문자와 밑줄에 일치합니다.

"\W"
   단어 문자가 아닌 모든 문자와 일치합니다. 이것은 "\w"의 반대입니다.
   "ASCII" 플래그가 사용되면 "[^a-zA-Z0-9_]"와 동등하게 됩니다.
   "LOCALE" 플래그를 사용하면, 현재 로케일에서 영숫자로 간주하는 문자
   와 밑줄을 제외한 것과 일치합니다.

"\Z"
   문자열 끝에만 일치합니다.

파이썬 문자열 리터럴이 지원하는 대부분의 표준 이스케이프는 정규식 구문
분석기도 받아들입니다:

   \a      \b      \f      \n
   \N      \r      \t      \u
   \U      \v      \x      \\

("\b"는 단어 경계를 나타내는 데 사용되며, 문자 클래스 내에서만 "백스페
이스"를 의미함에 유의하십시오.)

"'\u'", "'\U'" 및 "'\N'" 이스케이프 시퀀스는 유니코드 패턴에서만 인식
됩니다. 바이트열 패턴에서는 에러입니다. 알 수 없는 ASCII 문자 이스케이
프는 나중에 사용하기 위해 예약되어 있으며 에러로 처리됩니다.

8진수 이스케이프는 제한된 형식으로 포함됩니다. 첫 번째 숫자가 0이거나,
3개의 8진수가 있으면, 8진수 이스케이프로 간주합니다. 그렇지 않으면, 그
룹 참조입니다. 문자열 리터럴과 마찬가지로, 8진수 이스케이프 길이는 항
상 최대 3자리입니다.

버전 3.3에서 변경: "'\u'"와 "'\U'" 이스케이프 시퀀스가 추가되었습니다.

버전 3.6에서 변경: "'\'"와 ASCII 글자로 구성된 알 수 없는 이스케이프는
이제 에러입니다.

버전 3.8에서 변경: "'\N{name}'" 이스케이프 시퀀스가 추가되었습니다. 문
자열 리터럴과 마찬가지로, 이름 있는 유니코드 문자(예를 들어 "'\N{EM
DASH}'")로 확장됩니다.


모듈 내용
=========

모듈은 몇 가지 함수, 상수 및 예외를 정의합니다. 함수 중 일부는 컴파일
된 정규식의 모든 기능을 갖춘 메서드의 단순화된 버전입니다. 대부분의 사
소하지 않은 응용 프로그램은 항상 컴파일된 형식을 사용합니다.


Flags
-----

버전 3.6에서 변경: 플래그 상수는 이제 "enum.IntFlag"의 서브 클래스인
"RegexFlag"의 인스턴스입니다.

re.A
re.ASCII

   "\w", "\W", "\b", "\B", "\d", "\D", "\s" 및 "\S"가 전체 유니코드 일
   치 대신 ASCII 전용 일치를 수행하도록 합니다. 유니코드 패턴에만 의미
   가 있으며 바이트열 패턴에서는 무시됩니다. 인라인 플래그 "(?a)"에 해
   당합니다.

   이전 버전과의 호환성을 위해, "re.U" 플래그는 (동의어 "re.UNICODE"와
   내장 대응 버전 "(?u)"도) 여전히 존재하지만, 파이썬 3에서는 문자열에
   대한 일치가 기본적으로 유니코드이므로 (그리고 유니코드 일치는 바이
   트열에는 허용되지 않습니다) 필요 없습니다.

re.DEBUG

   컴파일된 정규식에 대한 디버그 정보를 표시합니다. 해당하는 인라인 플
   래그가 없습니다.

re.I
re.IGNORECASE

   대/소문자를 구분하지 않는 일치를 수행합니다; "[A-Z]"와 같은 정규식
   은 소문자와도 일치합니다. "re.ASCII" 플래그가 비 ASCII 일치를 비활
   성화하지 않는 한 전체 유니코드 일치(가령 "Ü"가 "ü"와 일치)가 작동합
   니다. "re.LOCALE" 플래그도 사용되지 않는 한, 현재 로케일은 이 플래
   그의 효과를 변경하지 않습니다. 인라인 플래그 "(?i)" 에 해당합니다.

   유니코드 패턴 "[a-z]"나 "[A-Z]"가 "IGNORECASE" 플래그와 함께 사용되
   면, 52개의 ASCII 글자와 4개의 추가 비 ASCII 문자와 일치합니다: 'İ'
   (U+0130, Latin capital letter I with dot above), 'ı' (U+0131, Latin
   small letter dotless i), 'ſ' (U+017F, Latin small letter long s) 및
   'K' (U+212A, Kelvin sign). "ASCII" 플래그가 사용되면, 문자 'a' 에서
   'z'와 'A' 에서 'Z' 만 일치합니다.

re.L
re.LOCALE

   "\w", "\W", "\b", "\B" 및 대소 문자를 구분하지 않는 일치를 현재 로
   케일에 의존하도록 만듭니다. 이 플래그는 바이트열 패턴에서만 사용할
   수 있습니다. 로케일 메커니즘은 신뢰성이 부족하고, 한 번에 하나의 "
   컬처(culture)" 만 처리하며, 8비트 로케일에서만 동작하므로, 이 플래
   그의 사용은 권장하지 않습니다. 파이썬 3에서, 유니코드 (str) 패턴에
   대해서 유니코드 일치가 기본적으로 이미 활성화되어 있으며 다른 로케
   일/언어를 처리할 수 있습니다. 인라인 플래그 "(?L)" 에 해당합니다.

   버전 3.6에서 변경: "re.LOCALE"은 바이트열 패턴에만 사용할 수 있으며
   "re.ASCII"와 호환되지 않습니다.

   버전 3.7에서 변경: "re.LOCALE" 플래그로 컴파일된 정규식 객체는 더는
   컴파일 타임의 로케일에 의존하지 않습니다. 일치하는 시점의 로케일 만
   일치 결과에 영향을 줍니다.

re.M
re.MULTILINE

   지정될 때, 패턴 문자 "'^'"는 문자열 시작과 각 줄의 시작(각 줄 바꿈
   바로 다음)에서 일치합니다; 패턴 문자 "'$'"는 문자열의 끝과 각 줄의
   끝(각 줄 바꿈 직전)에서 일치합니다. 기본적으로, "'^'"는 문자열의 시
   작 부분에서만 일치하고, "'$'"는 문자열 끝과 문자열 끝에 있는 (있다
   면) 줄 바꿈 바로 앞에서 일치합니다. 인라인 플래그 "(?m)" 에 해당합
   니다.

re.S
re.DOTALL

   "'.'" 특수 문자가 줄 넘김을 포함하여 모든 문자와 일치하도록 합니다;
   이 플래그가 없으면, "'.'"는 줄 넘김을 *제외한* 모든 문자와 일치합니
   다. 인라인 플래그 "(?s)"에 해당합니다.

re.X
re.VERBOSE

   This flag allows you to write regular expressions that look nicer
   and are more readable by allowing you to visually separate logical
   sections of the pattern and add comments. Whitespace within the
   pattern is ignored, except when in a character class, or when
   preceded by an unescaped backslash, or within tokens like "*?",
   "(?:" or "(?P<...>". For example, "(? :" and "* ?" are not allowed.
   When a line contains a "#" that is not in a character class and is
   not preceded by an unescaped backslash, all characters from the
   leftmost such "#" through the end of the line are ignored.

   이것은 십진수와 일치하는 다음 두 정규식 객체는 기능적으로 같음을 뜻
   합니다:

      a = re.compile(r"""\d +  # the integral part
                         \.    # the decimal point
                         \d *  # some fractional digits""", re.X)
      b = re.compile(r"\d+\.\d*")

   인라인 플래그 "(?x)" 에 해당합니다.


Functions
---------

re.compile(pattern, flags=0)

   정규식 패턴을 정규식 객체로 컴파일합니다. 정규식 객체는 아래에 설명
   되는 "match()", "search()" 및 기타 메서드를 일치시키는 데 사용할 수
   있습니다.

   정규식의 동작은 *flags* 값을 지정하여 수정할 수 있습니다. 값은 비트
   별 OR("|" 연산자)를 사용하여 다음 변수들을 결합할 수 있습니다.

   시퀀스

      prog = re.compile(pattern)
      result = prog.match(string)

   는 다음과 동등합니다

      result = re.match(pattern, string)

   하지만 정규식이 단일 프로그램에서 여러 번 사용될 때, "re.compile()"
   을 사용하고 결과 정규식 객체를 저장하여 재사용하는 것이 더 효율적입
   니다.

   참고:

     "re.compile()"과 모듈 수준 일치 함수에 전달된 가장 최근 패턴의 컴
     파일된 버전이 캐시 되므로, 한 번에 몇 가지 정규식만 사용하는 프로
     그램은 정규식 컴파일에 대해 신경 쓸 필요가 없습니다.

re.search(pattern, string, flags=0)

   *string*을 통해 스캔하여 정규식 *pattern*이 일치하는 첫 번째 위치를
   찾고, 대응하는 일치 객체를 반환합니다. 문자열의 어느 위치도 패턴과
   일치하지 않으면 "None"을 반환합니다; 이것은 문자열의 어떤 지점에서
   길이가 0인 일치를 찾는 것과는 다르다는 것에 유의하십시오.

re.match(pattern, string, flags=0)

   *string* 시작 부분에서 0개 이상의 문자가 정규식 *pattern*과 일치하
   면, 해당 일치 객체를 반환합니다. 문자열이 패턴과 일치하지 않으면
   "None"을 반환합니다; 이것은 길이가 0인 일치와는 다르다는 것에 유의
   하십시오.

   "MULTILINE" 모드에서도, "re.match()"는 각 줄의 시작 부분이 아니라
   문자열의 시작 부분에서만 일치함에 유의하십시오.

   *string*의 모든 위치에서 일치를 찾으려면, 대신 "search()"를 사용하
   십시오 (search() 대 match()도 참조하십시오).

re.fullmatch(pattern, string, flags=0)

   전체 *string*이 정규식 *pattern*과 일치하면, 해당하는 일치 객체를
   반환합니다. 문자열이 패턴과 일치하지 않으면 "None"을 반환합니다; 이
   것은 길이가 0인 일치와는 다르다는 것에 유의하십시오.

   버전 3.4에 추가.

re.split(pattern, string, maxsplit=0, flags=0)

   *string*을 *pattern*으로 나눕니다. *pattern*에서 포착하는 괄호가 사
   용되면 패턴의 모든 그룹 텍스트도 결과 리스트의 일부로 반환됩니다.
   *maxsplit*이 0이 아니면, 최대 *maxsplit* 분할이 발생하고, 나머지 문
   자열이 리스트의 마지막 요소로 반환됩니다.

      >>> re.split(r'\W+', 'Words, words, words.')
      ['Words', 'words', 'words', '']
      >>> re.split(r'(\W+)', 'Words, words, words.')
      ['Words', ', ', 'words', ', ', 'words', '.', '']
      >>> re.split(r'\W+', 'Words, words, words.', 1)
      ['Words', 'words, words.']
      >>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
      ['0', '3', '9']

   구분자에 포착하는 그룹이 있고 문자열 시작 부분에서 일치하면, 결과는
   빈 문자열로 시작됩니다. 문자열의 끝에 대해서도 마찬가지입니다:

      >>> re.split(r'(\W+)', '...words, words...')
      ['', '...', 'words', ', ', 'words', '...', '']

   그런 식으로, 구분자 구성 요소는 항상 결과 리스트 내의 같은 상대 인
   덱스에서 발견됩니다.

   패턴에 대한 빈(empty) 일치는 이전의 빈 일치와 인접하지 않을 때만 문
   자열을 분할합니다.

   >>> re.split(r'\b', 'Words, words, words.')
   ['', 'Words', ', ', 'words', ', ', 'words', '.']
   >>> re.split(r'\W*', '...words...')
   ['', '', 'w', 'o', 'r', 'd', 's', '', '']
   >>> re.split(r'(\W*)', '...words...')
   ['', '...', '', '', 'w', '', 'o', '', 'r', '', 'd', '', 's', '...', '', '', '']

   버전 3.1에서 변경: 선택적 flags 인자를 추가했습니다.

   버전 3.7에서 변경: 빈 문자열과 일치 할 수 있는 패턴으로 분할하는 지
   원을 추가했습니다.

re.findall(pattern, string, flags=0)

   Return all non-overlapping matches of *pattern* in *string*, as a
   list of strings or tuples.  The *string* is scanned left-to-right,
   and matches are returned in the order found.  Empty matches are
   included in the result.

   The result depends on the number of capturing groups in the
   pattern. If there are no groups, return a list of strings matching
   the whole pattern.  If there is exactly one group, return a list of
   strings matching that group.  If multiple groups are present,
   return a list of tuples of strings matching the groups.  Non-
   capturing groups do not affect the form of the result.

   >>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
   ['foot', 'fell', 'fastest']
   >>> re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
   [('width', '20'), ('height', '10')]

   버전 3.7에서 변경: 비어 있지 않은 일치는 이제 이전의 비어 있는 일치
   직후에 시작할 수 있습니다.

re.finditer(pattern, string, flags=0)

   *string*에서 겹치지 않는 RE *pattern*의 모든 일치를 일치 객체를 산
   출하는 *이터레이터*로 반환합니다. *string*은 왼쪽에서 오른쪽으로 스
   캔 되고, 일치는 찾은 순서대로 반환됩니다. 빈 일치가 결과에 포함됩니
   다.

   버전 3.7에서 변경: 비어 있지 않은 일치는 이제 이전의 비어 있는 일치
   직후에 시작할 수 있습니다.

re.sub(pattern, repl, string, count=0, flags=0)

   *string*에서 겹치지 않는 *pattern*의 가장 왼쪽 일치를 *repl*로 치환
   하여 얻은 문자열을 반환합니다. 패턴을 찾지 못하면, *string*이 변경
   되지 않고 반환됩니다. *repl*은 문자열이나 함수가 될 수 있습니다; 문
   자열이면 모든 역 슬래시 이스케이프가 처리됩니다. 즉, "\n"은 단일 개
   행 문자로 변환되고, "\r"는 캐리지 리턴으로 변환되고, 등등. 알 수 없
   는 ASCII 글자 이스케이프는 나중에 사용하기 위해 예약되어 있으며 에
   러로 처리됩니다. "\&"와 같은 다른 알려지지 않은 이스케이프는 그대로
   있습니다. "\6"과 같은 역참조는 패턴에서 그룹 6과 일치하는 부분 문자
   열로 치환됩니다. 예를 들면:

      >>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
      ...        r'static PyObject*\npy_\1(void)\n{',
      ...        'def myfunc():')
      'static PyObject*\npy_myfunc(void)\n{'

   *repl*이 함수면, *pattern*의 겹치지 않는 모든 일치마다 호출됩니다.
   이 함수는 단일 일치 객체 인자를 취하고, 치환 문자열을 반환합니다.
   예를 들면:

      >>> def dashrepl(matchobj):
      ...     if matchobj.group(0) == '-': return ' '
      ...     else: return '-'
      >>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
      'pro--gram files'
      >>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
      'Baked Beans & Spam'

   패턴은 문자열 또는 패턴 객체일 수 있습니다.

   선택적 인자 *count*는 치환될 패턴 발생의 최대 수입니다; *count*는
   음수가 아닌 정수여야 합니다. 생략되거나 0이면, 모든 발생이 치환됩니
   다. 패턴에 대한 빈 일치는 이전의 빈 일치와 인접하지 않을 때만 치환
   되므로, "sub('x*', '-', 'abxd')"는 "'-a-b--d-'"를 반환합니다.

   문자열형 *repl* 인자에서, 위에 설명된 문자 이스케이프와 역참조 외에
   , "\g<name>"는 "(?P<name>...)" 문법으로 정의한 "name"이라는 그룹에
   일치하는 부분 문자열을 사용합니다. "\g<number>"는 해당 그룹 번호를
   사용합니다; 따라서 "\g<2>"는 "\2"와 동등하지만, "\g<2>0"와 같은 치
   환에서 모호하지 않습니다. "\20"은 그룹 2에 대한 참조에 리터럴 문자
   "'0'"이 뒤에 오는 것이 아니라, 그룹 20에 대한 참조로 해석됩니다. 역
   참조 "\g<0>"은 RE와 일치하는 전체 부분 문자열을 치환합니다.

   버전 3.1에서 변경: 선택적 flags 인자를 추가했습니다.

   버전 3.5에서 변경: 일치하지 않는 그룹은 빈 문자열로 치환됩니다.

   버전 3.6에서 변경: *pattern*의 "'\'"와 ASCII 글자(letter)로 구성된
   알 수 없는 이스케이프는 이제 에러입니다.

   버전 3.7에서 변경: *repl*의 "'\'"와 ASCII 글자(letter)로 구성된 알
   수 없는 이스케이프는 이제 에러입니다.

   버전 3.7에서 변경: 패턴에 대한 빈 일치는 이전의 비어 있지 않은 일치
   와 인접 할 때 치환됩니다.

re.subn(pattern, repl, string, count=0, flags=0)

   "sub()"와 같은 연산을 수행하지만, 튜플 "(new_string,
   number_of_subs_made)"를 반환합니다.

   버전 3.1에서 변경: 선택적 flags 인자를 추가했습니다.

   버전 3.5에서 변경: 일치하지 않는 그룹은 빈 문자열로 치환됩니다.

re.escape(pattern)

   *pattern*에서 특수 문자를 이스케이프 처리합니다. 이것은 정규식 메타
   문자가 포함되어있을 수 있는 임의의 리터럴 문자열을 일치시키려는 경
   우에 유용합니다. 예를 들면:

      >>> print(re.escape('https://www.python.org'))
      https://www\.python\.org

      >>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:"
      >>> print('[%s]+' % re.escape(legal_chars))
      [abcdefghijklmnopqrstuvwxyz0123456789!\#\$%\&'\*\+\-\.\^_`\|\~:]+

      >>> operators = ['+', '-', '*', '/', '**']
      >>> print('|'.join(map(re.escape, sorted(operators, reverse=True))))
      /|\-|\+|\*\*|\*

   이 함수는 "sub()"와 "subn()"의 치환 문자열에 사용하면 안 되며, 역
   슬래시만 이스케이프 해야 합니다. 예를 들면:

      >>> digits_re = r'\d+'
      >>> sample = '/usr/sbin/sendmail - 0 errors, 12 warnings'
      >>> print(re.sub(digits_re, digits_re.replace('\\', r'\\'), sample))
      /usr/sbin/sendmail - \d+ errors, \d+ warnings

   버전 3.3에서 변경: "'_'" 문자는 더는 이스케이프 되지 않습니다.

   버전 3.7에서 변경: 정규식에서 특별한 의미를 가질 수 있는 문자만 이
   스케이프 됩니다. 결과적으로, "'!'", "'"'", "'%'", ""'"", "','",
   "'/'", "':'", "';'", "'<'", "'='", "'>'", "'@'" 및 '*"*'는 더는 이
   스케이프 되지 않습니다.

re.purge()

   정규식 캐시를 지웁니다.


Exceptions
----------

exception re.error(msg, pattern=None, pos=None)

   여기에 있는 함수 중 하나에 전달된 문자열이 유효한 정규식이 아니거나
   (예를 들어, 쌍을 이루지 않는 괄호가 들어있을 수 있습니다) 컴파일이
   나 일치 중에 다른 에러가 발생할 때 발생하는 예외. 문자열에 패턴과의
   일치가 포함되지 않을 때 에러가 발생하지는 않습니다. 에러 인스턴스에
   는 다음과 같은 추가 어트리뷰트가 있습니다:

   msg

      포맷되지 않은 에러 메시지.

   pattern

      정규식 패턴.

   pos

      컴파일이 실패한 위치를 가리키는 *pattern*의 인덱스 ("None"일 수
      있습니다).

   lineno

      *pos*에 해당하는 줄 ("None"일 수 있습니다).

   colno

      *pos*에 해당하는 열 ("None"일 수 있습니다).

   버전 3.5에서 변경: 추가 어트리뷰트가 추가되었습니다.


정규식 객체
===========

컴파일된 정규식 객체는 다음 메서드와 어트리뷰트를 지원합니다:

Pattern.search(string[, pos[, endpos]])

   *string*을 통해 스캔하여 이 정규식이 일치하는 첫 번째 위치를 찾고,
   대응하는 일치 객체를 반환합니다. 문자열의 어느 위치도 패턴과 일치하
   지 않으면 "None"을 반환합니다; 이것은 문자열의 어떤 지점에서 길이가
   0인 일치를 찾는 것과는 다르다는 것에 유의하십시오.

   선택적 두 번째 매개 변수 *pos*는 검색을 시작할 문자열의 인덱스를 제
   공합니다; 기본값은 "0"입니다. 이것은 문자열을 슬라이싱하는 것과 완
   전히 동등하지는 않습니다; "'^'" 패턴 문자는 문자열의 실제 시작 부분
   과 개행 직후의 위치에서 일치하지만, 검색을 시작할 색인에서 반드시
   일치하지는 않습니다.

   선택적 매개 변수 *endpos*는 문자열을 어디까지 검색할지를 제한합니다
   ; 문자열이 *endpos* 문자 길이인 것처럼 취급되어, 일치를 찾기 위해
   *pos*에서 "endpos - 1"까지의 문자만 검색됩니다. *endpos*가 *pos*보
   다 작으면 일치는 없습니다; 그렇지 않으면, *rx*가 컴파일된 정규식 객
   체일 때, "rx.search(string, 0, 50)"는 "rx.search(string[:50], 0)"와
   동등합니다.

      >>> pattern = re.compile("d")
      >>> pattern.search("dog")     # Match at index 0
      <re.Match object; span=(0, 1), match='d'>
      >>> pattern.search("dog", 1)  # No match; search doesn't include the "d"

Pattern.match(string[, pos[, endpos]])

   *string*의 *처음*에서 0개 이상의 문자가 이 정규식과 일치하면, 해당
   하는 일치 객체를 반환합니다. 문자열이 패턴과 일치하지 않으면 "None"
   을 반환합니다; 이것은 길이가 0인 일치와는 다릅니다.

   선택적 *pos*와 *endpos* 매개 변수는 "search()" 메서드에서와 같은 의
   미입니다.

      >>> pattern = re.compile("o")
      >>> pattern.match("dog")      # No match as "o" is not at the start of "dog".
      >>> pattern.match("dog", 1)   # Match as "o" is the 2nd character of "dog".
      <re.Match object; span=(1, 2), match='o'>

   *string*의 임의 위치에서 일치를 찾으려면, 대신 "search()"를 사용하
   십시오 (search() 대 match()도 참조하십시오).

Pattern.fullmatch(string[, pos[, endpos]])

   전체 *string*이 이 정규식과 일치하면, 해당하는 일치 객체를 반환합니
   다. 문자열이 패턴과 일치하지 않으면 "None"을 반환합니다; 이것은 길
   이가 0인 일치와는 다릅니다.

   선택적 *pos*와 *endpos* 매개 변수는 "search()" 메서드에서와 같은 의
   미입니다.

      >>> pattern = re.compile("o[gh]")
      >>> pattern.fullmatch("dog")      # No match as "o" is not at the start of "dog".
      >>> pattern.fullmatch("ogre")     # No match as not the full string matches.
      >>> pattern.fullmatch("doggie", 1, 3)   # Matches within given limits.
      <re.Match object; span=(1, 3), match='og'>

   버전 3.4에 추가.

Pattern.split(string, maxsplit=0)

   "split()" 함수와 같은데, 컴파일된 패턴을 사용합니다.

Pattern.findall(string[, pos[, endpos]])

   "findall()" 함수와 유사한데, 컴파일된 패턴을 사용합니다. 하지만,
   "search()"처럼 검색 영역을 제한하는 선택적 *pos*와 *endpos* 매개 변
   수도 받아들입니다.

Pattern.finditer(string[, pos[, endpos]])

   "finditer()" 함수와 유사한데, 컴파일된 패턴을 사용합니다. 하지만,
   "search()"처럼 검색 영역을 제한하는 선택적 *pos*와 *endpos* 매개 변
   수도 받아들입니다.

Pattern.sub(repl, string, count=0)

   "sub()" 함수와 같은데, 컴파일된 패턴을 사용합니다.

Pattern.subn(repl, string, count=0)

   "subn()" 함수와 같은데, 컴파일된 패턴을 사용합니다.

Pattern.flags

   정규식 일치 플래그. 이것은 "compile()"에 주어진 플래그, 패턴의 모든
   "(?...)" 인라인 플래그 및 패턴이 유니코드 문자열일 때 "UNICODE"와
   같은 묵시적 플래그의 조합입니다.

Pattern.groups

   패턴에 있는 포착 그룹 수.

Pattern.groupindex

   "(?P<id>)"로 정의된 기호 그룹 이름을 그룹 번호에 매핑하는 딕셔너리.
   패턴에 기호 그룹이 사용되지 않으면 딕셔너리는 비어 있습니다.

Pattern.pattern

   패턴 객체가 컴파일된 패턴 문자열.

버전 3.7에서 변경: "copy.copy()"와 "copy.deepcopy()" 지원이 추가되었습
니다. 컴파일된 정규식 객체는 원자적이라고 간주합니다.


일치 객체
=========

일치 객체는 항상 불리언 값 "True"를 가집니다. "match()"와 "search()"는
일치가 없을 때 "None"을 반환하기 때문에, 간단한 "if" 문으로 일치가 있
는지 검사할 수 있습니다:

   match = re.search(pattern, string)
   if match:
       process(match)

일치 객체는 다음 메서드와 어트리뷰트를 지원합니다:

Match.expand(template)

   "sub()" 메서드에서 수행되는 것처럼, 템플릿 문자열 *template*에 역
   슬래시 치환을 수행하여 얻은 문자열을 반환합니다. "\n"과 같은 이스케
   이프는 적절한 문자로 변환되고, 숫자 역참조("\1", "\2")와 이름 있는
   역참조("\g<1>", "\g<name>")는 해당 그룹의 내용으로 치환됩니다.

   버전 3.5에서 변경: 일치하지 않는 그룹은 빈 문자열로 치환됩니다.

Match.group([group1, ...])

   일치의 하나 이상의 서브 그룹을 반환합니다. 단일 인자가 있으면, 결과
   는 단일 문자열입니다; 인자가 여러 개면, 결과는 인자당 하나의 항목이
   있는 튜플입니다. 인자가 없으면, *group1*의 기본값은 0입니다 (전체
   일치가 반환됩니다). *groupN* 인자가 0이면, 해당 반환 값은 전체 일치
   문자열입니다; 경계를 포함하는 범위 [1..99]에 있으면, 해당 괄호로 묶
   은 그룹과 일치하는 문자열입니다. 그룹 번호가 음수이거나 패턴에 정의
   된 그룹 수보다 크면, "IndexError" 예외가 발생합니다. 패턴이 일치하
   지 않는 부분에 그룹이 포함되어 있으면, 해당 결과는 "None"입니다. 그
   룹이 여러 번 일치하는 패턴의 일부에 포함되어 있으면, 마지막 일치가
   반환됩니다.

      >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
      >>> m.group(0)       # The entire match
      'Isaac Newton'
      >>> m.group(1)       # The first parenthesized subgroup.
      'Isaac'
      >>> m.group(2)       # The second parenthesized subgroup.
      'Newton'
      >>> m.group(1, 2)    # Multiple arguments give us a tuple.
      ('Isaac', 'Newton')

   정규식이 "(?P<name>...)" 문법을 사용하면, *groupN* 인자는 그룹 이름
   으로 그룹을 식별하는 문자열일 수도 있습니다. 문자열 인자가 패턴의
   그룹 이름으로 사용되지 않으면, "IndexError" 예외가 발생합니다.

   적당히 복잡한 예:

      >>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
      >>> m.group('first_name')
      'Malcolm'
      >>> m.group('last_name')
      'Reynolds'

   이름있는 그룹은 인덱스로 참조할 수도 있습니다:

      >>> m.group(1)
      'Malcolm'
      >>> m.group(2)
      'Reynolds'

   그룹이 여러 번 일치하면, 마지막 일치만 액세스 할 수 있습니다:

      >>> m = re.match(r"(..)+", "a1b2c3")  # Matches 3 times.
      >>> m.group(1)                        # Returns only the last match.
      'c3'

Match.__getitem__(g)

   이것은 "m.group(g)"와 같습니다. 일치에서 개별 그룹에 더 쉽게 액세스
   할 수 있게 합니다:

      >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
      >>> m[0]       # The entire match
      'Isaac Newton'
      >>> m[1]       # The first parenthesized subgroup.
      'Isaac'
      >>> m[2]       # The second parenthesized subgroup.
      'Newton'

   버전 3.6에 추가.

Match.groups(default=None)

   1에서 패턴에 있는 그룹의 수까지, 일치의 모든 서브 그룹을 포함하는
   튜플을 반환합니다. *default* 인자는 일치에 참여하지 않은 그룹에 사
   용됩니다; 기본값은 "None"입니다.

   예를 들면:

      >>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
      >>> m.groups()
      ('24', '1632')

   우리가 소수점과 그 이후의 모든 것을 선택적으로 만들면, 모든 그룹이
   일치에 참여하지 않을 수 있습니다. 이 그룹은 *default* 인자가 주어지
   지 않는 한 기본값 "None"이 됩니다:

      >>> m = re.match(r"(\d+)\.?(\d+)?", "24")
      >>> m.groups()      # Second group defaults to None.
      ('24', None)
      >>> m.groups('0')   # Now, the second group defaults to '0'.
      ('24', '0')

Match.groupdict(default=None)

   일치의 모든 *이름 있는* 서브 그룹을 포함하고, 서브 그룹의 이름을 키
   로 사용하는 딕셔너리를 반환합니다. *default* 인자는 일치에 참여하지
   않은 그룹에 사용됩니다; 기본값은 "None"입니다. 예를 들면:

      >>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
      >>> m.groupdict()
      {'first_name': 'Malcolm', 'last_name': 'Reynolds'}

Match.start([group])
Match.end([group])

   *group*과 일치하는 부분 문자열의 시작과 끝 인덱스를 반환합니다;
   *group*의 기본값은 0입니다 (전체 일치 문자열을 뜻합니다). *group*이
   있지만, 일치에 기여하지 않으면, "-1"을 반환합니다. 일치 객체 *m*과
   일치에 기여한 그룹 *g*에서, 그룹 *g*와 일치하는 부분 문자열
   ("m.group(g)"와 동등합니다)은 다음과 같습니다

      m.string[m.start(g):m.end(g)]

   *group*이 널 문자열과 일치하면 "m.start(group)"은 "m.end(group)"와
   같음에 유의하십시오. 예를 들어, "m = re.search('b(c?)', 'cba')" 이
   후에, "m.start(0)"은 1이고, "m.end(0)"은 2이며, "m.start(1)"과
   "m.end(1)"은 모두 2이고, "m.start(2)"는 "IndexError" 예외를 발생시
   킵니다.

   전자 메일 주소에서 *remove_this*를 제거하는 예:

      >>> email = "tony@tiremove_thisger.net"
      >>> m = re.search("remove_this", email)
      >>> email[:m.start()] + email[m.end():]
      'tony@tiger.net'

Match.span([group])

   일치가 *m*일 때, 2-튜플 "(m.start(group), m.end(group))"를 반환합니
   다. *group*이 일치에 기여하지 않으면, 이것은 "(-1, -1)"임에 유의하
   십시오. *group*의 기본값은 0으로, 전체 일치입니다.

Match.pos

   정규식 객체의 "search()"나 "match()" 메서드에 전달된 *pos* 값. 이것
   은 RE 엔진이 일치를 찾기 시작한 string에 대한 인덱스입니다.

Match.endpos

   정규식 객체의 "search()"나 "match()" 메서드에 전달된 *endpos* 값.
   이것은 RE 엔진이 넘어가지 않을 string에 대한 인덱스입니다.

Match.lastindex

   마지막으로 일치하는 포착 그룹의 정수 인덱스, 또는 그룹이 전혀 일치
   하지 않으면 "None". 예를 들어, 정규식 "(a)b", "((a)(b))" 및
   "((ab))"는 문자열 "'ab'"에 적용될 경우 "lastindex == 1"이 되지만,
   "(a)(b)" 정규식은 같은 문자열에 적용될 때 "lastindex == 2"가 됩니다
   .

Match.lastgroup

   마지막으로 일치하는 포착 그룹의 이름, 또는 그룹에 이름이 없거나, 그
   룹이 전혀 일치하지 않으면 "None".

Match.re

   "match()"나 "search()" 메서드가 이 일치 인스턴스를 생성한 정규식 객
   체.

Match.string

   "match()"나 "search()"에 전달된 문자열.

버전 3.7에서 변경: "copy.copy()"와 "copy.deepcopy()" 지원이 추가되었습
니다. 일치 객체는 원자적이라고 간주합니다.


정규식 예제
===========


쌍 검사하기
-----------

이 예제에서는, 다음과 같은 도우미 함수를 사용하여 좀 더 세련되게 일치
객체를 표시합니다:

   def displaymatch(match):
       if match is None:
           return None
       return '<Match: %r, groups=%r>' % (match.group(), match.groups())

플레이어의 패를 5문자 문자열로 나타내는 포커 프로그램을 작성하고 있다
고 가정해봅시다. "a"는 에이스, "k"는 킹, "q"는 퀸, "j"는 잭, "t"는 10,
"2"에서 "9"는 그 값의 카드를 나타냅니다.

주어진 문자열이 유효한 패인지 보려면, 다음과 같이 할 수 있습니다:

   >>> valid = re.compile(r"^[a2-9tjqk]{5}$")
   >>> displaymatch(valid.match("akt5q"))  # Valid.
   "<Match: 'akt5q', groups=()>"
   >>> displaymatch(valid.match("akt5e"))  # Invalid.
   >>> displaymatch(valid.match("akt"))    # Invalid.
   >>> displaymatch(valid.match("727ak"))  # Valid.
   "<Match: '727ak', groups=()>"

마지막 패 ""727ak""는 페어, 즉 같은 값의 카드 두 장을 포함합니다. 이것
을 정규식과 일치시키려면, 역참조를 다음과 같이 사용할 수 있습니다:

   >>> pair = re.compile(r".*(.).*\1")
   >>> displaymatch(pair.match("717ak"))     # Pair of 7s.
   "<Match: '717', groups=('7',)>"
   >>> displaymatch(pair.match("718ak"))     # No pairs.
   >>> displaymatch(pair.match("354aa"))     # Pair of aces.
   "<Match: '354aa', groups=('a',)>"

페어가 어떤 카드로 구성되어 있는지 알아내려면, 다음과 같이 일치 객체의
"group()" 메서드를 사용할 수 있습니다:

   >>> pair = re.compile(r".*(.).*\1")
   >>> pair.match("717ak").group(1)
   '7'

   # Error because re.match() returns None, which doesn't have a group() method:
   >>> pair.match("718ak").group(1)
   Traceback (most recent call last):
     File "<pyshell#23>", line 1, in <module>
       re.match(r".*(.).*\1", "718ak").group(1)
   AttributeError: 'NoneType' object has no attribute 'group'

   >>> pair.match("354aa").group(1)
   'a'


scanf() 시뮬레이션
------------------

파이썬에는 현재 "scanf()"에 해당하는 것이 없습니다. 정규식은 일반적으
로 "scanf()" 포맷 문자열보다 강력하지만, 더 장황하기도 합니다. 아래 표
는 "scanf()" 포맷 토큰과 정규식 간의 다소 비슷한 매핑을 제공합니다.

+----------------------------------+-----------------------------------------------+
| "scanf()" 토큰                   | 정규식                                        |
|==================================|===============================================|
| "%c"                             | "."                                           |
+----------------------------------+-----------------------------------------------+
| "%5c"                            | ".{5}"                                        |
+----------------------------------+-----------------------------------------------+
| "%d"                             | "[-+]?\d+"                                    |
+----------------------------------+-----------------------------------------------+
| "%e", "%E", "%f", "%g"           | "[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?"     |
+----------------------------------+-----------------------------------------------+
| "%i"                             | "[-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+)"         |
+----------------------------------+-----------------------------------------------+
| "%o"                             | "[-+]?[0-7]+"                                 |
+----------------------------------+-----------------------------------------------+
| "%s"                             | "\S+"                                         |
+----------------------------------+-----------------------------------------------+
| "%u"                             | "\d+"                                         |
+----------------------------------+-----------------------------------------------+
| "%x", "%X"                       | "[-+]?(0[xX])?[\dA-Fa-f]+"                    |
+----------------------------------+-----------------------------------------------+

다음과 같은 문자열에서 파일명과 숫자를 추출하려면

   /usr/sbin/sendmail - 0 errors, 4 warnings

여러분은 다음과 같은 "scanf()" 포맷을 사용할 것입니다

   %s - %d errors, %d warnings

동등한 정규식은 다음과 같습니다

   (\S+) - (\d+) errors, (\d+) warnings


search() 대 match()
-------------------

Python offers different primitive operations based on regular
expressions:

* "re.match()" checks for a match only at the beginning of the string

* "re.search()" checks for a match anywhere in the string (this is
  what Perl does by default)

* "re.fullmatch()" checks for entire string to be a match

예를 들면:

   >>> re.match("c", "abcdef")    # No match
   >>> re.search("c", "abcdef")   # Match
   <re.Match object; span=(2, 3), match='c'>
   >>> re.fullmatch("p.*n", "python") # Match
   <re.Match object; span=(0, 6), match='python'>
   >>> re.fullmatch("r.*n", "python") # No match

"'^'"로 시작하는 정규식은 "search()"와 함께 사용하여 문자열 시작 부분
의 일치로 제한 할 수 있습니다:

   >>> re.match("c", "abcdef")    # No match
   >>> re.search("^c", "abcdef")  # No match
   >>> re.search("^a", "abcdef")  # Match
   <re.Match object; span=(0, 1), match='a'>

그러나 "MULTILINE" 모드에서 "match()"는 문자열 시작 부분에서만 일치하
지만, "'^'"로 시작하는 정규식을 "search()"에 사용하면 각 줄의 시작 부
분에서 일치합니다.

   >>> re.match("X", "A\nB\nX", re.MULTILINE)  # No match
   >>> re.search("^X", "A\nB\nX", re.MULTILINE)  # Match
   <re.Match object; span=(4, 5), match='X'>


전화번호부 만들기
-----------------

"split()"는 문자열을, 전달된 패턴으로 구분된 리스트로 분할합니다. 이
메서드는 전화번호부를 만드는 다음 예제에서 보이듯이 텍스트 데이터를 파
이썬에서 쉽게 읽고 수정할 수 있는 데이터 구조로 변환하는 데 매우 중요
합니다.

먼저, 여기 입력이 있습니다. 보통 파일에서 올 수 있습니다만, 여기서는
삼중 따옴표로 묶인 문자열 문법을 사용합니다.

   >>> text = """Ross McFluff: 834.345.1254 155 Elm Street
   ...
   ... Ronald Heathmore: 892.345.3428 436 Finley Avenue
   ... Frank Burger: 925.541.7625 662 South Dogwood Way
   ...
   ...
   ... Heather Albrecht: 548.326.4584 919 Park Place"""

항목은 하나 이상의 개행으로 구분됩니다. 이제 비어있지 않은 각 줄이 항
목이 되도록 문자열을 리스트로 변환합니다:

   >>> entries = re.split("\n+", text)
   >>> entries
   ['Ross McFluff: 834.345.1254 155 Elm Street',
   'Ronald Heathmore: 892.345.3428 436 Finley Avenue',
   'Frank Burger: 925.541.7625 662 South Dogwood Way',
   'Heather Albrecht: 548.326.4584 919 Park Place']

마지막으로, 각 항목을 이름, 성, 전화번호 및 주소로 구성된 리스트로 분
할합니다. 주소에 우리의 분할 패턴인 스페이스가 들어있기 때문에,
"split()"의 "maxsplit" 매개 변수를 사용합니다:

   >>> [re.split(":? ", entry, 3) for entry in entries]
   [['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],
   ['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],
   ['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],
   ['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]

":?" 패턴은 결과 리스트에 나타나지 않도록, 성 뒤의 콜론과 일치합니다.
"maxsplit"로 "4"를 사용하면, 번지수를 거리 이름과 분리 할 수 있습니다:

   >>> [re.split(":? ", entry, 4) for entry in entries]
   [['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],
   ['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],
   ['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],
   ['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]


텍스트 뒤섞기
-------------

"sub()"는 패턴의 모든 일치를 문자열이나 함수의 결과로 치환합니다. 이
예제는 "sub()"에 텍스트를 "뒤섞는", 즉 문장의 각 단어에서 첫 번째 문자
와 마지막 문자를 제외한 모든 문자의 순서를 무작위로 바꾸는 함수를 사용
하는 방법을 보여줍니다:

   >>> def repl(m):
   ...     inner_word = list(m.group(2))
   ...     random.shuffle(inner_word)
   ...     return m.group(1) + "".join(inner_word) + m.group(3)
   >>> text = "Professor Abdolmalek, please report your absences promptly."
   >>> re.sub(r"(\w)(\w+)(\w)", repl, text)
   'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
   >>> re.sub(r"(\w)(\w+)(\w)", repl, text)
   'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'


모든 부사 찾기
--------------

"findall()"은 "search()"처럼 첫 번째 등장뿐만 아니라, 패턴의 *모든* 등
장과 일치합니다. 예를 들어, 작가가 어떤 텍스트에서 부사를 모두 찾고 싶
으면, 다음과 같은 방식으로 "findall()"을 사용할 수 있습니다:

   >>> text = "He was carefully disguised but captured quickly by police."
   >>> re.findall(r"\w+ly\b", text)
   ['carefully', 'quickly']


모든 부사와 그 위치 찾기
------------------------

일치하는 텍스트보다 패턴의 모든 일치에 대한 자세한 정보가 필요하면,
"finditer()"는 문자열 대신 일치 객체를 제공하므로 유용합니다. 이전 예
에서 계속해서, 작가가 어떤 텍스트에서 부사*와 그 위치*를 모두 찾고 싶
으면, 다음과 같은 방식으로 "finditer()"를 사용합니다:

   >>> text = "He was carefully disguised but captured quickly by police."
   >>> for m in re.finditer(r"\w+ly\b", text):
   ...     print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))
   07-16: carefully
   40-47: quickly


날 문자열 표기법
----------------

날 문자열 표기법("r"text"")은 정규식을 합리적인 상태로 유지합니다. 이
것 없이는, 정규식의 모든 역 슬래시("'\'")를 이스케이프 하기 위해 그 앞
에 또 하나의 역 슬래시를 붙여야 합니다. 예를 들어, 다음 두 코드 줄은
기능상으로 같습니다:

   >>> re.match(r"\W(.)\1\W", " ff ")
   <re.Match object; span=(0, 4), match=' ff '>
   >>> re.match("\\W(.)\\1\\W", " ff ")
   <re.Match object; span=(0, 4), match=' ff '>

리터럴 역 슬래시와 일치시키려면, 정규식에서 이스케이프 되어야 합니다.
날 문자열 표기법을 사용하면, "r"\\""이 됩니다. 날 문자열 표기법을 사용
하지 않으면, ""\\\\""를 사용해야 하는데, 다음 코드 줄들은 기능적으로
같습니다:

   >>> re.match(r"\\", r"\\")
   <re.Match object; span=(0, 1), match='\\'>
   >>> re.match("\\\\", r"\\")
   <re.Match object; span=(0, 1), match='\\'>


토크나이저 작성하기
-------------------

토크나이저나 스캐너는 문자열을 분석하여 문자 그룹을 분류합니다. 이것은
컴파일러나 인터프리터를 작성하는 데 유용한 첫 번째 단계입니다.

텍스트 범주는 정규식으로 지정됩니다. 이 기법은 이들을 하나의 마스터 정
규식으로 결합하고 연속적인 일치를 반복하는 것입니다:

   from typing import NamedTuple
   import re

   class Token(NamedTuple):
       type: str
       value: str
       line: int
       column: int

   def tokenize(code):
       keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
       token_specification = [
           ('NUMBER',   r'\d+(\.\d*)?'),  # Integer or decimal number
           ('ASSIGN',   r':='),           # Assignment operator
           ('END',      r';'),            # Statement terminator
           ('ID',       r'[A-Za-z]+'),    # Identifiers
           ('OP',       r'[+\-*/]'),      # Arithmetic operators
           ('NEWLINE',  r'\n'),           # Line endings
           ('SKIP',     r'[ \t]+'),       # Skip over spaces and tabs
           ('MISMATCH', r'.'),            # Any other character
       ]
       tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
       line_num = 1
       line_start = 0
       for mo in re.finditer(tok_regex, code):
           kind = mo.lastgroup
           value = mo.group()
           column = mo.start() - line_start
           if kind == 'NUMBER':
               value = float(value) if '.' in value else int(value)
           elif kind == 'ID' and value in keywords:
               kind = value
           elif kind == 'NEWLINE':
               line_start = mo.end()
               line_num += 1
               continue
           elif kind == 'SKIP':
               continue
           elif kind == 'MISMATCH':
               raise RuntimeError(f'{value!r} unexpected on line {line_num}')
           yield Token(kind, value, line_num, column)

   statements = '''
       IF quantity THEN
           total := total + price * quantity;
           tax := price * 0.05;
       ENDIF;
   '''

   for token in tokenize(statements):
       print(token)

토크나이저는 다음과 같은 출력을 생성합니다:

   Token(type='IF', value='IF', line=2, column=4)
   Token(type='ID', value='quantity', line=2, column=7)
   Token(type='THEN', value='THEN', line=2, column=16)
   Token(type='ID', value='total', line=3, column=8)
   Token(type='ASSIGN', value=':=', line=3, column=14)
   Token(type='ID', value='total', line=3, column=17)
   Token(type='OP', value='+', line=3, column=23)
   Token(type='ID', value='price', line=3, column=25)
   Token(type='OP', value='*', line=3, column=31)
   Token(type='ID', value='quantity', line=3, column=33)
   Token(type='END', value=';', line=3, column=41)
   Token(type='ID', value='tax', line=4, column=8)
   Token(type='ASSIGN', value=':=', line=4, column=12)
   Token(type='ID', value='price', line=4, column=15)
   Token(type='OP', value='*', line=4, column=21)
   Token(type='NUMBER', value=0.05, line=4, column=23)
   Token(type='END', value=';', line=4, column=27)
   Token(type='ENDIF', value='ENDIF', line=5, column=4)
   Token(type='END', value=';', line=5, column=9)

[Frie09] Friedl, Jeffrey. Mastering Regular Expressions. 3rd ed.,
         O'Reilly Media, 2009. 이 책의 세 번째 판은 더는 파이썬을 다루
         지 않지만, 초판은 훌륭한 정규식 패턴 작성을 아주 자세하게 다
         루었습니다.
