메타 문자
|
' | ' 문자는 or 과 동일한 의미롤 사용되는 메타 문자로 A | B는 A 또는 B 라는 의미를 갖는다.
>>> p = re.compile('Crow|Servo') >>> m = p.match('CrowHello') >>> print(m) <re.Match object; span=(0,4), match='Crow'> |
^
' | ' 문자는 문자열의 맨 처음과 일치한다는 것을 의미한다. ^Life life로 시작하는 문자열의 의미를 갖는다.
>> print(re.search('^Life', 'Life is too short')) <re.Match object; span =(0, 4), match='Life'> >>> print(re.search('^Life', 'My Life')) None |
>> print(re.search('^Life', 'Life is too short')) <re.Match object; span=(0, 4), match='Life'> >>> print(re.search('^Life', 'My Life')) None
^Life 정규식은 Life 문자열이 처음에 온 경우에는 매치하지만 처음 위치가 아닌 경우에는 매치되지 않음을 알 수 있다.
$
$ 메타 문자는 ^ 메타 문자와 반대의 경우로. $는 문자열의 끝과 매치함을 의미한다. short$는 short로 끝나는 문자열의 의미를 갖는다.
>>> print(re.search('short$', 'Life is too short')) <re.Match object; span =(12, 17), match='short'> > >> print(re.search('short$', 'Life is too short, you need python')) None |
※ ^ 또는 $ 문자를 메타 문자가 아닌 문자 그 자체로 매치하고 싶은 경우에는 \^, \$ 로 사용하면 된다.
\ ?
\A | 문자열의 처음과 매치됨을 의미한다. re.MULTILINE 옵션을 사용하여도 \A 는 줄에 적용되는 것이 아닌 전체 문자열에 대해서 적용된다. |
\Z | 문자열의 끄톼 매치된다. \A와 동일하게 re.MULTILINE옵션에서도 전체 문자열에 대해 적용된다. |
\b | 단어 구분자로 보통 단어는 whiltespace에 의해 구분된다. 이는 따라서 단어의 whiltespace에 따라 매칭된다. >> p = re.compile(r'\bclass\b') >>> print(p.search('no class at all')) # 클래스의 앞뒤로 \b = whitespace 이므로 매칭 O <re.Match object; span=(3, 8), match='class'> >>> print(p.search('one subclass is')). # 앞에 whitespace가 아닌 'sub'로 매칭 X None |
\B | \b와 반대의 경우로 whiltespacer가 위치에 들어오게 되면 매칭이 되지 않고 다른 문자열이나 숫자가 들어오면 매칭이 된다. |
그루핑
어떤 매칭되는 문자열에 대한 반복을 찾기 위해서는 ( ) 를 이용한 그루핑을 통해서 정규식을 구현할 수 있다.
cat이라는 단어에 대해서 그루핑을 하고 싶다면 (cat)+ 이라고 작성할 수 있다. 즉 ()는 그룹을 만들어주는 메타문자이다.
>>> p = re.compile('(ABC)+') >>> m = p.search('ABCABCABC OK?') >>> print(m) <re.Match object; span=(0, 9), match='ABCABCABC'> > >> print(m.group()) ABCABCABC |
예시에서 처럼 ABC가 0번째부터 9번째 인덱스까지 추출되어서 group으로 묶인 것을 확인할 수 있다.
이번에는 이름 + "" + 전화번호의 형태의 문자열의 정규식에서 이름을 추출하기 위해서 어떻게 해야할까? 이 때 그루핑을 사용할 수 있다.
>>> p = re.compile(r"\w+\s+\d+[-]\d+[-]\d+") >>> m = p.search("park 010-1234-1234") >>> p = re.compile(r"(\w+)\s+\d+[-]\d+[-]\d+") >>> m = p.search("park 010-1234-1234") >>> print(m.group(1)) park |
위의 예시에서 보면 이름에 해당하는 \w 부분을 그룹 (\w+)으로 만들면 match 객체의 group 메서드를 사용하여 그루핑된 부분의 문자열이 추출된다. group 메서드에서 각 인덱스는 아래와 같은 의미를 지닌다.
- group(0) : 매치된 전체 문자열
- group(1) : 첫 번째 그룹에 해당되는 문자열
- group(2) : 두 번째 그룹에 해당되는 무자열
- group(n) : n번째 그룹에 해당되는 문자열
그렇다면 이런식으로 전화번호를 그룹2로 추가하여 추출한다면 아래의 코드와 같을 것이다.
>>> p = re.compile(r"(\w+)\s+(\d+[-]\d+[-]\d+)") >>> m = p.search("park 010-1234-1234") >>> print(m.group(2)) 010-1234-1234 |
(w+)의 이름부분을 포함해서 추가로 (\d+[-]\d+[-]\d+)의 전화번호 그룹을 만들었다. 이 상태에서 group(2)처럼 사용하면 전화번호만 그루핑 되어 추출할 수 있다.
추가로 만약 국번을 따로 그룹3으로 추출한다면 "(\w+)\s+(\d+[-]\d+[-]\d+)" 에서 "(\w+)\s+( ( \d )+[-]\d+[-]\d+)" 으로 한다면 group(3)에는 국번만 따로 추출되게 된다. ( 중첩 그룹에서는 바깥족부터 안쪽까지 그룹 인덱스 번호가 증가한다. )
그루핑된 문자열음 재참조를 할 수 있는 특징을 가진다.
>>> p = re.compile(r'(\b\w+)\s+\1') >>> p.search('Paris in the the rain').group() 'the the' |
정규식 (\b\w+)\s+\1은 (그룹) + " " + 그룹과 동일한 단어와 매치됨을 의미한다. 이렇게 정규식을 만들게 되면 2개의 동일한 단어를 연속적으로 사용해야만 매치된다.
즉 \1 은 그룹 중 첫번째를 가리키로 \2는 두번째 \n은 그룹 중 n번째를 가리킨다.
그루핑은 그룹들의 구별을 위해 별명을 지을 수 잇는 특징도 가진다.
(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)
위 정규식은 앞에서 본 이름과 전화번호를 추출하는 정규식이다. 기존과 달라진 부분은 다음과 같다.
(\w+) --> (?P<name>\w+)
이는 (\w+)라는 그룹에 name이라는 이름을 붙여준 것이다. 이 때 ?..은 정규표현식의 확장 구문으로 가동성이 조금 떨어지지만 강력함을 갖춘 구문이다.
일단 그룹에 이름을 짓는 방법은 (?P<그룹명>...) 을 사용하면 된다.
>>> p = re.compile(r"(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)") >>> m = p.search("park 010-1234-1234") >>> print(m.group("name")) # name이라는 이름으로 group(1)을 추출함 park 물론 재 참조도 가능하다. >>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)') >>> p.search('Paris in the the spring').group() 'the the' |
여기서 알아봐야 할 점은 선언시 와 다르게 재참조할 때에는 (?P=그룹이름)이라는 확장 구문을 사용해야 한다.