"re" --- Regular expression operations
**************************************

**Source code:** Lib/re/

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

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

Both patterns and strings to be searched can be Unicode strings
("str") as well as 8-bit strings ("bytes"). However, Unicode strings
and 8-bit strings cannot be mixed: that is, you cannot match a Unicode
string with a bytes pattern or vice-versa; similarly, when asking for
a substitution, the replacement string must be of the same type as
both the pattern and the search string.

Regular expressions use the backslash character ("'\'") to indicate
special forms or to allow special characters to be used without
invoking their special meaning.  This collides with Python's usage of
the same character for the same purpose in string literals; for
example, to match a literal backslash, one might have to write
"'\\\\'" as the pattern string, because the regular expression must be
"\\", and each backslash must be expressed as "\\" inside a regular
Python string literal. Also, please note that any invalid escape
sequences in Python's usage of the backslash in string literals now
generate a "SyntaxWarning" and in the future this will become a
"SyntaxError". This behaviour will happen even if it is a valid escape
sequence for a regular expression.

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

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

더 보기:

  The third-party regex module, which has an API compatible with the
  standard library "re" module, but offers additional functionality
  and a more thorough Unicode support.


Regular expression syntax
=========================

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

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

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

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

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

Repetition operators or quantifiers ("*", "+", "?", "{m,n}", etc)
cannot be directly nested. This avoids ambiguity with the non-greedy
modifier suffix "?", and with other modifiers in other
implementations. To apply a second repetition to an inner repetition,
parentheses may be used. For example, the expression "(?:a{6})*"
matches any multiple of six "'a'" characters.

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

"."
   (Dot.)  In the default mode, this matches any character except a
   newline.  If the "DOTALL" flag has been specified, this matches any
   character including a newline.  "(?s:.)" matches any character
   regardless of flags.

"^"
   (캐럿.) 문자열의 시작과 일치하고, "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'와 일치합니다.

"*?", "+?", "??"
   The "'*'", "'+'", and "'?'" quantifiers are all *greedy*; they
   match as much text as possible.  Sometimes this behaviour isn't
   desired; if the RE "<.*>" is matched against "'<a> b <c>'", it will
   match the entire string, and not just "'<a>'".  Adding "?" after
   the quantifier makes it perform the match in *non-greedy* or
   *minimal* fashion; as *few* characters as possible will be matched.
   Using the RE "<.*?>" will match only "'<a>'".

"*+", "++", "?+"
   Like the "'*'", "'+'", and "'?'" quantifiers, those where "'+'" is
   appended also match as many times as possible. However, unlike the
   true greedy quantifiers, these do not allow back-tracking when the
   expression following it fails to match. These are known as
   *possessive* quantifiers. For example, "a*a" will match "'aaaa'"
   because the "a*" will match all 4 "'a'"s, but, when the final "'a'"
   is encountered, the expression is backtracked so that in the end
   the "a*" ends up matching 3 "'a'"s total, and the fourth "'a'" is
   matched by the final "'a'". However, when "a*+a" is used to match
   "'aaaa'", the "a*+" will match all 4 "'a'", but when the final
   "'a'" fails to find any more characters to match, the expression
   cannot be backtracked and will thus fail to match. "x*+", "x++" and
   "x?+" are equivalent to "(?>x*)", "(?>x+)" and "(?>x?)"
   correspondingly.

   Added in version 3.11.

"{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}?"
   Causes the resulting RE to match from *m* to *n* repetitions of the
   preceding RE, attempting to match as *few* repetitions as possible.
   This is the non-greedy version of the previous quantifier.  For
   example, on the 6-character string "'aaaaaa'", "a{3,5}" will match
   5 "'a'" characters, while "a{3,5}?" will only match 3 characters.

"{m,n}+"
   Causes the resulting RE to match from *m* to *n* repetitions of the
   preceding RE, attempting to match as many repetitions as possible
   *without* establishing any backtracking points. This is the
   possessive version of the quantifier above. For example, on the
   6-character string "'aaaaaa'", "a{3,5}+aa" attempts to match 5
   "'a'" characters, then, requiring 2 more "'a'"s, will need more
   characters than available and thus fail, while "a{3,5}aa" will
   match with "a{3,5}" capturing 5, then 4 "'a'"s by backtracking and
   then the final 2 "'a'"s are matched by the final "aa" in the
   pattern. "x{m,n}+" is equivalent to "(?>x{m,n})".

   Added in version 3.11.

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

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

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

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

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

   * Special characters except backslash lose their special meaning
     inside sets. For example, "[(+*)]" will match any of the literal
     characters "'('", "'+'", "'*'", or "')'".

   * Backslash either escapes characters which have special meaning in
     a set such as "'-'", "']'", "'^'" and "'\\'" itself or signals a
     special sequence which represents a single character such as
     "\xa0" or "\n" or a character class such as "\w" or "\S" (defined
     below). Note that "\b" represents a single "backspace" character,
     not a word boundary as outside a set, and numeric escapes such as
     "\1" are always octal escapes, not group references. Special
     sequences which do not match a single character such as "\A" and
     "\z" are not allowed.

   * 범위 내에 있지 않은 문자는 *여집합(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)"
   (One or more letters from the set "'a'", "'i'", "'L'", "'m'",
   "'s'", "'u'", "'x'".) The group matches the empty string; the
   letters set the corresponding flags for the entire regular
   expression:

   * "re.A" (ASCII-only matching)

   * "re.I" (ignore case)

   * "re.L" (locale dependent)

   * "re.M" (multi-line)

   * "re.S" (dot matches all)

   * "re.U" (Unicode matching)

   * "re.X" (verbose)

   (The flags are described in Module contents.) This is useful if you
   wish to include the flags as part of the regular expression,
   instead of passing a *flag* argument to the "re.compile()"
   function. Flags should be used first in the expression string.

   버전 3.11에서 변경: This construction can only be used at the start
   of the expression.

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

"(?aiLmsux-imsx:...)"
   (Zero or more letters from the set "'a'", "'i'", "'L'", "'m'",
   "'s'", "'u'", "'x'", optionally followed by "'-'" followed by one
   or more letters from the "'i'", "'m'", "'s'", "'x'".) The letters
   set or remove the corresponding flags for the part of the
   expression:

   * "re.A" (ASCII-only matching)

   * "re.I" (ignore case)

   * "re.L" (locale dependent)

   * "re.M" (multi-line)

   * "re.S" (dot matches all)

   * "re.U" (Unicode matching)

   * "re.X" (verbose)

   (The flags are described in Module contents.)

   The letters "'a'", "'L'" and "'u'" are mutually exclusive when used
   as inline flags, so they can't be combined or follow "'-'".
   Instead, when one of them appears in an inline group, it overrides
   the matching mode in the enclosing group.  In Unicode patterns
   "(?a:...)" switches to ASCII-only matching, and "(?u:...)" switches
   to Unicode matching (default).  In bytes patterns "(?L:...)"
   switches to locale dependent matching, and "(?a:...)" switches to
   ASCII-only matching (default). This override is only in effect for
   the narrow inline group, and the original matching mode is restored
   outside of the group.

   Added in version 3.6.

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

"(?>...)"
   Attempts to match "..." as if it was a separate regular expression,
   and if successful, continues to match the rest of the pattern
   following it. If the subsequent pattern fails to match, the stack
   can only be unwound to a point *before* the "(?>...)" because once
   exited, the expression, known as an *atomic group*, has thrown away
   all stack points within itself. Thus, "(?>.*)." would never match
   anything because first the ".*" would match all characters
   possible, then, having nothing left to match, the final "." would
   fail to match. Since there are no stack points saved in the Atomic
   Group, and there is no stack point before it, the entire expression
   would thus fail to match.

   Added in version 3.11.

"(?P<name>...)"
   Similar to regular parentheses, but the substring matched by the
   group is accessible via the symbolic group name *name*.  Group
   names must be valid Python identifiers, and in "bytes" patterns
   they can only contain bytes in the ASCII range.  Each group name
   must be defined only once within a regular expression.  A symbolic
   group is also a numbered group, just as if the group were not
   named.

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

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

   버전 3.12에서 변경: In "bytes" patterns, group *name* can only
   contain bytes in the ASCII range ("b'\x00'"-"b'\x7f'").

"(?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>'"과는 일치하지 않습니다.

   버전 3.12에서 변경: Group *id* can only contain ASCII digits. In
   "bytes" patterns, group *name* can only contain bytes in the ASCII
   range ("b'\x00'"-"b'\x7f'").

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

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

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

"\b"
   Matches the empty string, but only at the beginning or end of a
   word. A word is defined as a sequence of word characters. Note that
   formally, "\b" is defined as the boundary between a "\w" and a "\W"
   character (or vice versa), or between "\w" and the beginning or end
   of the string. This means that "r'\bat\b'" matches "'at'", "'at.'",
   "'(at)'", and "'as at ay'" but not "'attempt'" or "'atlas'".

   The default word characters in Unicode (str) patterns are Unicode
   alphanumerics and the underscore, but this can be changed by using
   the "ASCII" flag. Word boundaries are determined by the current
   locale if the "LOCALE" flag is used.

   참고:

     Inside a character range, "\b" represents the backspace
     character, for compatibility with Python's string literals.

"\B"
   Matches the empty string, but only when it is *not* at the
   beginning or end of a word. This means that "r'at\B'" matches
   "'athens'", "'atom'", "'attorney'", but not "'at'", "'at.'", or
   "'at!'". "\B" is the opposite of "\b", so word characters in
   Unicode (str) patterns are Unicode alphanumerics or the underscore,
   although this can be changed by using the "ASCII" flag. Word
   boundaries are determined by the current locale if the "LOCALE"
   flag is used.

   버전 3.14에서 변경: "\B" now matches empty input string.

"\d"
   유니코드 (str) 패턴일 때:
      Matches any Unicode decimal digit (that is, any character in
      Unicode character category [Nd]). This includes "[0-9]", and
      also many other digit characters.

      Matches "[0-9]" if the "ASCII" flag is used.

   8비트 (bytes) 패턴일 때:
      Matches any decimal digit in the ASCII character set; this is
      equivalent to "[0-9]".

"\D"
   Matches any character which is not a decimal digit. This is the
   opposite of "\d".

   Matches "[^0-9]" if the "ASCII" flag is used.

"\s"
   유니코드 (str) 패턴일 때:
      Matches Unicode whitespace characters (as defined by
      "str.isspace()"). This includes "[ \t\n\r\f\v]", and also many
      other characters, for example the non-breaking spaces mandated
      by typography rules in many languages.

      Matches "[ \t\n\r\f\v]" if the "ASCII" flag is used.

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

"\S"
   Matches any character which is not a whitespace character. This is
   the opposite of "\s".

   Matches "[^ \t\n\r\f\v]" if the "ASCII" flag is used.

"\w"
   유니코드 (str) 패턴일 때:
      Matches Unicode word characters; this includes all Unicode
      alphanumeric characters (as defined by "str.isalnum()"), as well
      as the underscore ("_").

      Matches "[a-zA-Z0-9_]" if the "ASCII" flag is used.

   8비트 (bytes) 패턴일 때:
      Matches characters considered alphanumeric in the ASCII
      character set; this is equivalent to "[a-zA-Z0-9_]". If the
      "LOCALE" flag is used, matches characters considered
      alphanumeric in the current locale and the underscore.

"\W"
   Matches any character which is not a word character. This is the
   opposite of "\w". By default, matches non-underscore ("_")
   characters for which "str.isalnum()" returns "False".

   Matches "[^a-zA-Z0-9_]" if the "ASCII" flag is used.

   If the "LOCALE" flag is used, matches characters which are neither
   alphanumeric in the current locale nor the underscore.

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

   Added in version 3.14.

"\Z"
   The same as "\z".  For compatibility with old Python versions.

Most of the escape sequences supported by Python string literals are
also accepted by the regular expression parser:

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

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

"'\u'", "'\U'", and "'\N'" escape sequences are only recognized in
Unicode (str) patterns. In bytes patterns they are errors. Unknown
escapes of ASCII letters are reserved for future use and treated as
errors.

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

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

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

버전 3.8에서 변경: The "'\N{*name*}'" escape sequence has been added.
As in string literals, it expands to the named Unicode character (e.g.
"'\N{EM DASH}'").


Module contents
===============

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


Flags
-----

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

class re.RegexFlag

   An "enum.IntFlag" class containing the regex options listed below.

   Added in version 3.11: - added to "__all__"

re.A
re.ASCII

   Make "\w", "\W", "\b", "\B", "\d", "\D", "\s" and "\S" perform
   ASCII-only matching instead of full Unicode matching.  This is only
   meaningful for Unicode (str) patterns, and is ignored for bytes
   patterns.

   Corresponds to the inline flag "(?a)".

   참고:

     The "U" flag still exists for backward compatibility, but is
     redundant in Python 3 since matches are Unicode by default for
     "str" patterns, and Unicode matching isn't allowed for bytes
     patterns. "UNICODE" and the inline flag "(?u)" are similarly
     redundant.

re.DEBUG

   Display debug information about compiled expression.

   No corresponding inline flag.

re.I
re.IGNORECASE

   Perform case-insensitive matching; expressions like "[A-Z]" will
   also  match lowercase letters. Full Unicode matching (such as "Ü"
   matching "ü") also works unless the "ASCII" flag is used to disable
   non-ASCII matches. The current locale does not change the effect of
   this flag unless the "LOCALE" flag is also used.

   Corresponds to the inline flag "(?i)".

   Note that when the Unicode patterns "[a-z]" or "[A-Z]" are used in
   combination with the "IGNORECASE" flag, they will match the 52
   ASCII letters and 4 additional non-ASCII letters: 'İ' (U+0130,
   Latin capital letter I with dot above), 'ı' (U+0131, Latin small
   letter dotless i), 'ſ' (U+017F, Latin small letter long s) and 'K'
   (U+212A, Kelvin sign). If the "ASCII" flag is used, only letters
   'a' to 'z' and 'A' to 'Z' are matched.

re.L
re.LOCALE

   Make "\w", "\W", "\b", "\B" and case-insensitive matching dependent
   on the current locale. This flag can be used only with bytes
   patterns.

   Corresponds to the inline flag "(?L)".

   경고:

     This flag is discouraged; consider Unicode matching instead. The
     locale mechanism is very unreliable as it only handles one
     "culture" at a time and only works with 8-bit locales. Unicode
     matching is enabled by default for Unicode (str) patterns and it
     is able to handle different locales and languages.

   버전 3.6에서 변경: "LOCALE" can be used only with bytes patterns
   and is not compatible with "ASCII".

   버전 3.7에서 변경: Compiled regular expression objects with the
   "LOCALE" flag no longer depend on the locale at compile time. Only
   the locale at matching time affects the result of matching.

re.M
re.MULTILINE

   When specified, the pattern character "'^'" matches at the
   beginning of the string and at the beginning of each line
   (immediately following each newline); and the pattern character
   "'$'" matches at the end of the string and at the end of each line
   (immediately preceding each newline).  By default, "'^'" matches
   only at the beginning of the string, and "'$'" only at the end of
   the string and immediately before the newline (if any) at the end
   of the string.

   Corresponds to the inline flag "(?m)".

re.NOFLAG

   Indicates no flag being applied, the value is "0".  This flag may
   be used as a default value for a function keyword argument or as a
   base value that will be conditionally ORed with other flags.
   Example of use as a default value:

      def myfunc(pattern, text, flag=re.NOFLAG):
          return re.search(pattern, text, flag)

   Added in version 3.11.

re.S
re.DOTALL

   Make the "'.'" special character match any character at all,
   including a newline; without this flag, "'.'" will match anything
   *except* a newline.

   Corresponds to the inline flag "(?s)".

re.U
re.UNICODE

   In Python 3, Unicode characters are matched by default for "str"
   patterns. This flag is therefore redundant with **no effect** and
   is only kept for backward compatibility.

   See "ASCII" to restrict matching to ASCII characters instead.

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)

   Compile a regular expression pattern into a regular expression
   object, which can be used for matching using its "prefixmatch()",
   "search()", and other methods, described below.

   The expression's behaviour can be modified by specifying a *flags*
   value. Values can be any of the flags variables, combined using
   bitwise OR (the "|" operator).

   시퀀스

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

   는 다음과 동등합니다

      result = re.search(pattern, string)

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

   참고:

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

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

   Scan through *string* looking for the first location where the
   regular expression *pattern* produces a match, and return a
   corresponding "Match". Return "None" if no position in the string
   matches the pattern; note that this is different from finding a
   zero-length match at some point in the string.

   The expression's behaviour can be modified by specifying a *flags*
   value. Values can be any of the flags variables, combined using
   bitwise OR (the "|" operator).

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

   If zero or more characters at the beginning of *string* match the
   regular expression *pattern*, return a corresponding "Match".
   Return "None" if the string does not match the pattern; note that
   this is different from a zero-length match.

   참고:

     Even in "MULTILINE" mode, this will only match at the beginning
     of the string and not at the beginning of each line.

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

   The expression's behaviour can be modified by specifying a *flags*
   value. Values can be any of the flags variables, combined using
   bitwise OR (the "|" operator).

   This function now has two names and has long been known as
   "match()".  Use that name when you need to retain compatibility
   with older Python versions.

   Added in version 3.15.

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

   Soft deprecated since version 3.15: "match()" has been *soft
   deprecated* in favor of the alternate "prefixmatch()" name of this
   API which is more explicitly descriptive. Use it to better express
   intent. The norm in other languages and regular expression
   implementations is to use the term *match* to refer to the behavior
   of what Python has always called "search()". See prefixmatch() vs.
   match().

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

   If the whole *string* matches the regular expression *pattern*,
   return a corresponding "Match".  Return "None" if the string does
   not match the pattern; note that this is different from a zero-
   length match.

   The expression's behaviour can be modified by specifying a *flags*
   value. Values can be any of the flags variables, combined using
   bitwise OR (the "|" operator).

   Added in version 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.', maxsplit=1)
      ['Words', 'words, words.']
      >>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
      ['0', '3', '9']

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

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

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

   Adjacent empty matches are not possible, but an empty match can
   occur immediately after a non-empty match.

      >>> 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', '...', '', '', '']

   The expression's behaviour can be modified by specifying a *flags*
   value. Values can be any of the flags variables, combined using
   bitwise OR (the "|" operator).

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

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

   버전 3.13부터 폐지됨: Passing *maxsplit* and *flags* as positional
   arguments is deprecated. In future Python versions they will be
   keyword-only parameters.

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')]

   The expression's behaviour can be modified by specifying a *flags*
   value. Values can be any of the flags variables, combined using
   bitwise OR (the "|" operator).

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

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

   Return an *iterator* yielding "Match" objects over all non-
   overlapping matches for the RE *pattern* in *string*.  The *string*
   is scanned left-to-right, and matches are returned in the order
   found.  Empty matches are included in the result.

   The expression's behaviour can be modified by specifying a *flags*
   value. Values can be any of the flags variables, combined using
   bitwise OR (the "|" operator).

   버전 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{'

   If *repl* is a function, it is called for every non-overlapping
   occurrence of *pattern*.  The function takes a single "Match"
   argument, and returns the replacement string.  For example:

      >>> 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'

   The pattern may be a string or a "Pattern".

   The optional argument *count* is the maximum number of pattern
   occurrences to be replaced; *count* must be a non-negative integer.
   If omitted or zero, all occurrences will be replaced.

   Adjacent empty matches are not possible, but an empty match can
   occur immediately after a non-empty match. As a result, "sub('x*',
   '-', 'abxd')" returns "'-a-b--d-'" instead of "'-a-b-d-'".

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

   The expression's behaviour can be modified by specifying a *flags*
   value. Values can be any of the flags variables, combined using
   bitwise OR (the "|" operator).

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

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

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

   버전 3.7에서 변경: Unknown escapes in *repl* consisting of "'\'"
   and an ASCII letter now are errors. An empty match can occur
   immediately after a non-empty match.

   버전 3.12에서 변경: Group *id* can only contain ASCII digits. In
   "bytes" replacement strings, group *name* can only contain bytes in
   the ASCII range ("b'\x00'"-"b'\x7f'").

   버전 3.13부터 폐지됨: Passing *count* and *flags* as positional
   arguments is deprecated. In future Python versions they will be
   keyword-only parameters.

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

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

   The expression's behaviour can be modified by specifying a *flags*
   value. Values can be any of the flags variables, combined using
   bitwise OR (the "|" operator).

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.PatternError(msg, pattern=None, pos=None)

   Exception raised when a string passed to one of the functions here
   is not a valid regular expression (for example, it might contain
   unmatched parentheses) or when some other error occurs during
   compilation or matching.  It is never an error if a string contains
   no match for a pattern.  The "PatternError" instance has the
   following additional attributes:

   msg

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

   pattern

      정규식 패턴.

   pos

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

   lineno

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

   colno

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

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

   버전 3.13에서 변경: "PatternError" was originally named "error";
   the latter is kept as an alias for backward compatibility.


Regular expression objects
==========================

class re.Pattern

   Compiled regular expression object returned by "re.compile()".

   버전 3.9에서 변경: "re.Pattern" supports "[]" to indicate a Unicode
   (str) or bytes pattern. See 제네릭 에일리어스 형.

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

   Scan through *string* looking for the first location where this
   regular expression produces a match, and return a corresponding
   "Match". Return "None" if no position in the string matches the
   pattern; note that this is different from finding a zero-length
   match at some point in the string.

   선택적 두 번째 매개 변수 *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.prefixmatch(string[, pos[, endpos]])

   If zero or more characters at the *beginning* of *string* match
   this regular expression, return a corresponding "Match". Return
   "None" if the string does not match the pattern; note that this is
   different from a zero-length match.

   Note that even in "MULTILINE" mode, this will only match at the
   beginning of the string and not at the beginning of each line.

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

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

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

   This method now has two names and has long been known as "match()".
   Use that name when you need to retain compatibility with older
   Python versions.

   Added in version 3.15.

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

   Soft deprecated since version 3.15: "match()" has been *soft
   deprecated* in favor of the alternate "prefixmatch()" name of this
   API which is more explicitly descriptive. Use it to better express
   intent. The norm in other languages and regular expression
   implementations is to use the term *match* to refer to the behavior
   of what Python has always called "search()". See prefixmatch() vs.
   match().

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

   If the whole *string* matches this regular expression, return a
   corresponding "Match".  Return "None" if the string does not match
   the pattern; note that this is different from a zero-length match.

   선택적 *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'>

   Added in version 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

   The regex matching flags.  This is a combination of the flags given
   to "compile()", any "(?...)" inline flags in the pattern, and
   implicit flags such as "UNICODE" if the pattern is a Unicode
   string.

Pattern.groups

   패턴에 있는 포착 그룹 수.

Pattern.groupindex

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

Pattern.pattern

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

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


Match objects
=============

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

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

class re.Match

   Match object returned by successful "match"es and "search"es.

   버전 3.9에서 변경: "re.Match" supports "[]" to indicate a Unicode
   (str) or bytes match. See 제네릭 에일리어스 형.

Match.expand(template)

   Return the string obtained by doing backslash substitution on the
   template string *template*, as done by the "sub()" method. Escapes
   such as "\n" are converted to the appropriate characters, and
   numeric backreferences ("\1", "\2") and named backreferences
   ("\g<1>", "\g<name>") are replaced by the contents of the
   corresponding group. The backreference "\g<0>" will be replaced by
   the entire match.

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

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

   Returns one or more subgroups of the match.  If there is a single
   argument, the result is a single string; if there are multiple
   arguments, the result is a tuple with one item per argument.
   Without arguments, *group1* defaults to zero (the whole match is
   returned). If a *groupN* argument is zero, the corresponding return
   value is the entire matching string; if it is a positive integer,
   it is the string matching the corresponding parenthesized group.
   If a group number is negative or larger than the number of groups
   defined in the pattern, an "IndexError" exception is raised. If a
   group is contained in a part of the pattern that did not match, the
   corresponding result is "None". If a group is contained in a part
   of the pattern that matched multiple times, the last match is
   returned.

      >>> m = re.search(r"\A(\w+) (\w+)", "Norwegian Blue, pining for the fjords")
      >>> m.group(0)       # The entire match
      'Norwegian Blue'
      >>> m.group(1)       # The first parenthesized subgroup.
      'Norwegian'
      >>> m.group(2)       # The second parenthesized subgroup.
      'Blue'
      >>> m.group(1, 2)    # Multiple arguments give us a tuple.
      ('Norwegian', 'Blue')

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

   적당히 복잡한 예:

      >>> m = re.search(r"(?P<adjective>\w+) (?P<animal>\w+)", "killer rabbit")
      >>> m.group('adjective')
      'killer'
      >>> m.group('animal')
      'rabbit'

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

      >>> m.group(1)
      'killer'
      >>> m.group(2)
      'rabbit'

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

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

Match.__getitem__(g)

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

      >>> m = re.search(r"(\w+) (\w+)", "Norwegian Blue, pining for the fjords")
      >>> m[0]       # The entire match
      'Norwegian Blue'
      >>> m[1]       # The first parenthesized subgroup.
      'Norwegian'
      >>> m[2]       # The second parenthesized subgroup.
      'Blue'

   Named groups are supported as well:

      >>> m = re.search(r"(?P<adjective>\w+) (?P<animal>\w+)", "killer rabbit")
      >>> m['adjective']
      'killer'
      >>> m['animal']
      'rabbit'

   Added in version 3.6.

Match.groups(default=None)

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

   예를 들면:

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

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

      >>> m = re.search(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.search(r"(?P<adjective>\w+) (?P<animal>\w+)", "killer rabbit")
      >>> m.groupdict()
      {'adjective': 'killer', 'animal': 'rabbit'}

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()" 지원이 추가되었습
니다. 일치 객체는 원자적이라고 간주합니다.


Regular expression examples
===========================


Checking for a pair
-------------------

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

   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_hand_re = re.compile(r"^[a2-9tjqk]{5}$")
   >>> displaymatch(valid_hand_re.search("akt5q"))  # Valid.
   "<Match: 'akt5q', groups=()>"
   >>> displaymatch(valid_hand_re.search("akt5e"))  # Invalid.
   >>> displaymatch(valid_hand_re.search("akt"))    # Invalid.
   >>> displaymatch(valid_hand_re.search("727ak"))  # Valid.
   "<Match: '727ak', groups=()>"

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

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

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

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

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

   >>> pair_re.prefixmatch("354aa").group(1)
   'a'


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

Python does not currently have an equivalent to "scanf()".  Regular
expressions are generally more powerful, though also more verbose,
than "scanf()" format strings.  The table below offers some more-or-
less equivalent mappings between "scanf()" format tokens and regular
expressions.

+----------------------------------+-----------------------------------------------+
| "scanf()" Token                  | 정규식                                        |
|==================================|===============================================|
| "%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]+"                    |
+----------------------------------+-----------------------------------------------+

To extract the filename and numbers from a string like:

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

you would use a "scanf()" format like:

   %s - %d errors, %d warnings

The equivalent regular expression would be:

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


search() vs. prefixmatch()
--------------------------

Python offers different primitive operations based on regular
expressions:

* "re.prefixmatch()" 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.prefixmatch("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.prefixmatch("c", "abcdef")  # No match
   >>> re.search("^c", "abcdef")      # No match
   >>> re.search("^a", "abcdef")      # Match
   <re.Match object; span=(0, 1), match='a'>

Note however that in "MULTILINE" mode "prefixmatch()" only matches at
the beginning of the string, whereas using "search()" with a regular
expression beginning with "'^'" will match at the beginning of each
line.

   >>> re.prefixmatch("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'>


prefixmatch() vs. match()
-------------------------

Why is the "match()" function and method discouraged in favor of the
longer "prefixmatch()" spelling?

Many other languages have gained regex support libraries since regular
expressions were added to Python. However in the most popular of
those, they use the term *match* in their APIs to mean the unanchored
behavior provided in Python by "search()". Thus use of the plain term
*match* can be unclear to those used to other languages when reading
or writing code and not familiar with the Python API's divergence from
what otherwise become the industry norm.

Quoting from the Zen Of Python ("python3 -m this"): *"Explicit is
better than implicit"*. Anyone reading the name "prefixmatch()" is
likely to understand the intended semantics. When reading "match()"
there remains a seed of doubt about the intended behavior to anyone
not already familiar with this old Python gotcha.

We **do not** plan to remove the older "match()" name, as it has been
used in code for over 30 years. It has been *soft deprecated*: code
supporting older versions of Python should continue to use "match()",
while new code should prefer "prefixmatch()".

Added in version 3.15: "prefixmatch()"

Soft deprecated since version 3.15: "match()"


Making a phonebook
------------------

"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, maxsplit=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, maxsplit=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']]


Text munging
------------

"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.'


Finding all adverbs
-------------------

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

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


Finding all adverbs and their positions
---------------------------------------

If one wants more information about all matches of a pattern than the
matched text, "finditer()" is useful as it provides "Match" objects
instead of strings.  Continuing with the previous example, if a writer
wanted to find all of the adverbs *and their positions* in some text,
they would use "finditer()" in the following manner:

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


Raw string notation
-------------------

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

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

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

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


Writing a tokenizer
-------------------

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

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

   from typing import NamedTuple
   import re

   class Token(NamedTuple):
       type: str
       value: int | float | 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. 이 책의 세 번째 판은 더는 파이썬을 다루
         지 않지만, 초판은 훌륭한 정규식 패턴 작성을 아주 자세하게 다
         루었습니다.
