https://docs.python.org/ko/3/howto/regex.html
Regular Expression HOWTO
Author, A.M. Kuchling < amk@amk.ca>,. Abstract: This document is an introductory tutorial to using regular expressions in Python with the re module. It provides a gentler introduction than the corr...
docs.python.org
위의 글을 공부하고 중요하다고 생각되는 내용 위주로 가져왔습니다.
소개
정규식(RE, regexes, regex패턴이라고 불립니다.)은 본질적으로 파이썬에 내장된 작고 고도로 특수화된 프로그래밍 언어 입니다. 이는 re모듀를 import하여 사용할 수 있습니다.
정규식 패턴은 일련의 바이트 코드로 컴파일된 다음 C로 작성된 일치 엔진에 의해 실행 됩니다. 고급사용을 위해서는, 엔진이 지정된 RE를 실행하는 방법에 주의를 기울이고 더 빠르게 실행되는 바이트 코드를 생성하기 위해 특정한 방법으로 RE를 작성하는 것이 필요할 수 있습니다.(특수항 상황에서는 최적화가 필요하다는 소리 입니다.)
정규식으로 모든 물자열 처리작업을 수행할 수 있는 것은 아닙니다. 정규식으로 수행할 수는 있지만, 표현이 아주 복잡해지는 작업도 있습니다. 이럴 때 처리하기 위해서는 파이썬 코드로 처리하는 것이 나을 수 있습니다.(파이썬 코드가 정규식 코드보다 이해하기 편할 수 있습니다.)
단순한 패턴
우리는 가능한 가장 단순한 정규식에 대해 배우는 것으로 시작합니다.
문자일치
대부분의 글자와 문자는 단순히 자신과 일치합니다. 예를 들어 정규식 `test`는 문자열 `test`와 정확히 일치합니다.
처음으로 살펴볼 메타문자는 "[", "]" 입니다. 일치시키려는 문자 집합인 문자 클래스를 지정하는데 사용됩니다. 두 문자를 주고 "-"로 구분하여 문자의 범위를 타나낼 수 있습니다. ([a-c] -> [abc])
여집합을 사용해서 클래스에 나열되지 않은 문자들을 일치시킬 수 있습니다. "^"를 포함하는 것으로 나타낼 수 있습니다. 예를 들어 "[^5]"의 경우는 "5"를 제외한 모든 문자와 일치합니다.
아마도 가장 중요한 메타 문자는 "\" 백 슬래시 입니다. 파이썬 문자열 리터럴에서와 같이 백슬래시 다음에 특수한 시퀀스를 알리는 문자가 따라올 수 있습니다. 예를 들어 "[", "\"를 일치시켜야 할 때 앞에 백 슬레시를 붙여 처리할 수 있습니다. "\[", "\\"
"\"로 시작하는 특수 시퀀스중 일부는 숫자 집합, 글자 집합 또는 공백이 아닌 모든 것의 집합과 같이 종종 유용한 미리 정의된 문자 집합을 나타냅니다. 예를 들어 보면, "\w"는 모든 알파벳 캐릭터와 일치합니다. 정규식 패턴을 바이트로 표현하면 "[a-zA-Z0-9_]"클래스와 동등합니다. 아래는 python에서 활용되는 백슬래시와 관련된 문자들의 모음입니다.
with 백슬래쉬 | 표현 |
\d | [0-9] |
\D | [^0-9] |
\s | [ \t\n\r\f\v] |
\S | [^ \t\n\r\f\v] |
\w | [a-zA-Z0-9_] |
\W | [^a-zA-Z0-9_] |
반복하기
반복을 대표적인 메타문자에는 *가 있습니다. 이전문자를 정확ㅎ히 한 번이 아닌 0번 이상 일치시킬 수도 있도록 지정합니다.
"ca*t" 는 'ct', 'cat', 'caat'등과 일치 합니다.
"*"과 같은 반복은 Greedy합니다. RE를 반복할 때 일치 엔진은 가능한 여러번 바놉ㄱ하려고 시도합니다. 패턴의 뒷부분이 일치하지 않으면 일치 엔진은 되돌아가서 반복적으로 다시 시도합니다.
단계별 예제를 통해서 알아볼 수 있습니다. "a[bcd]*b]"를 생각해 봅시다. 이 문자는 'a'문자와 일치하고 0개 이상의 [bcd]클래스 문자가 뒤따르고 마지막에 'b'로 끝납니다. RE를 문자열 'abcbd'와 일치시킨다고 상상해 보면 좋습니다.
단계 | 일치된 것 | 설명 |
1 | a | RE의 a가 일치 합니다. |
2 | abcbd | 엔진은 가능한 길게 [bcd]*와 일치시켤고 문자열의 끝까지 갑니다. |
3 | 실패 | 엔진은 b를 일치하려고 시도하지만, 현재 위치가 문자열의 끝이므로 실패합니다. |
4 | abcb | 물서서서 [bcd]*가 하나 적은 문자와 일치합니다. |
5 | 실패 | b를 다시 시도하지만, 현재 위치는 d 인 마지막 문자에 있습니다. |
6 | abc | 다시 물러서서 [bcd]*가 bc 하고 만 일치 합니다. |
6 | abcb | b를 다시 시도합니다. 이번에는 현재 위치의 문자가 'b'이므로 성공 합니다. |
RE의 끝에 도달했으며, 'abcb'와 일치했습니다. 이것은 일치 엔진이 처음에는 갈 수 있는 데까지 가본 다음, 일치하는 것이 발견되지 않으면 점진적으로 물러서고, 나머지 RE의 나머지 부분을 반복해서 다시 시도하는 것을 보여줍니다. [bcd]*에 대한 일치 항목의 길이가 0이 될 때까지 물러서고, 그것마저도 실패하면, 엔진은 문자열이 RE와 전혀 일치하지 않는다고 결론을 내립니다.
또 다른 반복 메타 문자는 +인 데, 하나 이상과 일치합니다. *와 +의 차이점에 주의하십시오; *는 0 이상과 일치하므로, 반복되는 내용이 전혀 표시되지 않을 수 있습니다. 반면 +는 적어도 1번 이상 나타날 것을 요구합니다. 비슷한 예제를 사용하면, ca+t는 'cat' (1 'a'), 'caaat' (3 'a')와 일치하지만 'ct'와 일치하지는 않습니다.
물음표인 "?"는 optional적인 역할을 한다고 이해하면 됩니다. regex가 "home-?brew" 일 때 "homebrew" 혹은 "home-brew"가 매치됩니다.
대괄호로 활용되는 {m,n} 도 존재합니다. m, n은 정수형태 이며 최소 몇번 최대 몇번 반복되는지 알기 위해 활용됩니다. 예를 들어 "a/{1, 3}b는 "a/b", "a//b", "a///b"와 매치될 것입니다. 하지만, "ab"혹은 "a////b"와는 매치가 되지 않을 수 있습니다. 또한 m혹은 n을 생략할 수 있습니다. 이때 빠진 값에 대해 합리적인 값이 가정됩니다. m을 생략하면 0 n을 생략하면 무한대가 됩니다.
더 많은 패턴 기능
| | or 연산자 A|B 는 A나 B와 일치하는 문자열과 일치합니다. |
^ | 줄의 시 부분에 일치합니다. |
$ | 줄의 끝 부분과 일치합니다. |
\A | 문자열의 시작 부분에서만 일치합니다. |
\Z | 문자열의 끝 부분에서만 일치 합니다. |
\b | 단어의 경계. 이것은 단어의 시작이니 끝 부분에서만 일치하는 폭이 없는 어서션입니다. |
\b예제
p = re.compile(r'\bclass\b')
print(p.search('no class at all')) ## <re.Match object; span=(3, 8), match='class'>
print(p.search('the declassified algorithm')) ## None
print(p.search('one subclass is')) ## None
t = r'\bthe\b'
curStr = "the first class. the first movement"
res = re.finditer(t, curStr)
for i in res:
print(i)
## <re.Match object; span=(0, 3), match='the'>
## <re.Match object; span=(17, 20), match='the'>
그룹
( 과 ) 를 활용하여 특정 문자열을 캡쳐할 수 있습니다.
p = re.compile('(a)b')
m = p.match('ab')
m.group() ##'ab'
m.group(0) ##'ab'
p = re.compile('(a(b)c)d')
m = p.match('abcd')
m.group(0) ##'abcd'
m.group(1) ##'abc'
m.group(2) ## 'b'
비 포착 그룹과 이름 있는 그룹
(?: ...) 을 활용해서 반복되는 문자열을 확인하고 그룹을 캡쳐하지 않게 만들 수 있습니다.
m = re.match("([abc])+", "abc")
m.groups() ## ('c',),
m = re.match("(?:[abc])+", "abc")
m.groups() ## () 비포착 하는 방법을 통해 바뀐 점을 쉬이 확인 할 수 있습니다.
이름이 있는 그룹을 만들기 위해 (?P<name> ...) 을 활용할 수 있습니다.
p = re.compile(r'(?P<word>\b\w+\b)')
m = p.search( '(((( Lots of punctuation )))' )
m.group('word') ## 'Lots'
m.group(1) ## 'Lots'
## use GroupDict()
m = re.match(r'(?P<first>\w+) (?P<last>\w+)', 'Jane Doe')
m.groupdict() ## {'first': 'Jane', 'last': 'Doe'}
(...)\1과 같은 정규식에서 역참조 문법은 그룹 번호를 나타냅니다. 자연스럽게 번호 대신 그룹 이름을 사용하는 변형이 있습니다. 이것은 다른 파이썬 확장입니다: (?P=name)은 name이라는 그룹의 내용이 현재 위치에서 다시 일치해야 함을 나타냅니다. 중복된 단어를 찾는 정규식인 \b(\w+)\s+\1\b는 \b(?P<word>\w+)\s+(?P=word)\b로 표현할 수도 있습니다:
p = re.compile(r'\b(?P<word>\w+)\s+(?P=word)\b')
p.search('Paris in the the spring').group() ## 'the the'
미리 보기 어서션
긍정의 어서션 (?=...) 은 현재 위치에서 성공적으로 일치하면 성공하고 그렇지 않으면 실패합니다. 부정의 어서션인 (?!...)은 긍정의 어서션의 반대입니다.
이를 구체적인 예를 든다면, bat와 exe를 제거하는 확장자를 만들기 위한 정규식은 다음과 같습니다.
.*[.](?!bat$|exe$)[^.]*$
정규식은 부정적 어서션을 통해 bat와 exe확장자를 제거 합니다.
검색과 치환
.sub(replacement, string[, count = 0] ) 을 통해 문자를 치환할 수 있습니다.
p = re.compile('(blue|white|red)')
p.sub('colour', 'blue socks and red shoes') ##'colour socks and colour shoes'
p.sub('colour', 'blue socks and red shoes', count=1) ##'colour socks and red shoes'
re.VERBOSE 사용하기
지금까지 정규식이 매우 콤팩트한 표기법이라는 사실을 눈치챘을 것입니다만, 극단적으로 읽기 어렵지는 않았습니다. 중간 정도의 복잡성을 가진 RE는 역 슬래시, 괄호 및 메타 문자의 긴 모음이 되어 읽고 이해하기 어려울 수 있습니다.
이러한 RE의 경우, 정규식을 컴파일할 때 re.VERBOSE 플래그를 지정하면 정규식을 보다 명확하게 포맷할 수 있어서 도움이 됩니다.
re.VERBOSE 플래그는 여러 가지 효과가 있습니다. 문자 클래스 안에 있지 않은 공백이 무시됩니다. 이것은, dog | cat과 같은 정규식은 가독성이 떨어지는 dog|cat과 동등하지만, [a b]는 여전히 'a', 'b' 또는 스페이스 문자와 일치함을 뜻합니다. 또한, 주석을 RE에 넣을 수도 있습니다; 주석은 # 문자에서 다음 줄 바꿈까지 확장됩니다. 삼중 따옴표로 묶은 문자열과 함께 사용하면, RE를 더 깔끔하게 포맷할 수 있습니다:
pat = re.compile(r"""
\s* # Skip leading whitespace
(?P<header>[^:]+) # Header name
\s* : # Whitespace, and a colon
(?P<value>.*?) # The header's value -- *? used to
# lose the following trailing whitespace
\s*$ # Trailing whitespace to end-of-line
""", re.VERBOSE)
'Python' 카테고리의 다른 글
[Python] Pyhton Sort에 관하여 (cmp_to_key 활용하기) (0) | 2024.10.10 |
---|