티스토리 뷰

728x90

정규 표현식에서 문자 클래스라는 표현 방법으로 [] 대괄호 기호를 사용할 수 있습니다.

 

그런데 한글과 한자, 또는 일본어가 섞여 있는 상황에서는 표현하려면 막막해 질때가 있습니다.

 

 

예를들어..

소년은 개울가에서 소녀를 보자 곧 윤 초시네 증손녀(曾孫女)딸이라는 걸 알 수 있었다. 소녀는 개울에다 손을 잠그고 물장난을 하고 있는 것이다. 서울서는 이런 개울물을 보지 못하기나 한 듯이. 벌써 며칠째 소녀는, 학교에서 돌아오는 길에 물장난이었다. 그런데, 어제까지 개울 기슭에서 하더니, 오늘은 징검다리 한가운데 앉아서 하고 있다.
소년은 개울둑에 앉아 버렸다. 소녀가 비키기를 기다리자는 것이다.
요행 지나가는 사람이 있어, 소녀가 길을 비켜 주었다. 다음 날은 좀 늦게 개울가로 나왔다.

이런 문장이 있습니다.

소년이란 단어부터 마침표까지 등장하는 모든 문장을 따로 출력하고 싶다면

import re
text = '''
소년은 개울가에서 소녀를 보자 곧 윤 초시네 증손녀(曾孫女)딸이라는 걸 알 수 있었다. 소녀는 개울에다 손을 잠그고 물장난을 하고 있는 것이다. 서울서는 이런 개울물을 보지 못하기나 한 듯이. 벌써 며칠째 소녀는, 학교에서 돌아오는 길에 물장난이었다. 그런데, 어제까지 개울 기슭에서 하더니, 오늘은 징검다리 한가운데 앉아서 하고 있다.
소년은 개울둑에 앉아 버렸다. 소녀가 비키기를 기다리자는 것이다.
요행 지나가는 사람이 있어, 소녀가 길을 비켜 주었다. 다음 날은 좀 늦게 개울가로 나왔다.
'''
p = re.compile('소년.*?\.')
result = p.findall(text)
print(result)
['소년은 개울가에서 소녀를 보자 곧 윤 초시네 증손녀(曾孫女)딸이라는 걸 알 수 있었다.', '소년은 개울둑에 앉아 버렸다.']

 

이런 방식으로 non-greedy를 나타내는 ?를 * 뒤에 붙여주는 방식도 있긴 합니다만

 

나는 반드시 [] 문자 클래스로 표현하겠다고 고집한다면

한글의 범위를 나타낼때 사용하는 "가-힣" 과 같이 한자도 같은 방식으로 표현해주면 됩니다.

 

출처 pixabay

 

한자의 유니코드는 u+4E00 부터 u+9FFF 이고

유니코드의 기호인 u+ 대신 16진수 기호인 0x로 바꿔주면 10진수 형태의 범위를 알 수 있습니다.

print(0x4E00, 0x9FFF)
19968 40959

 

이렇게 나온 결과를 "가-힣" 과 같이 문자로 표현해줘야 하므로

chr 함수를 사용하여 문자로 변환해줍니다.

 

for n in range(19968, 40960):
    print(n, chr(n), end=' / ')

시작 번호인 19968에 해당하는 문자는 一 이고

끝 번호인 40959에 해당하는 문자는 鿿 입니다.

끝 번호인 40959를 포함해서 몇몇 문자가 출력되지 않는 운영체제나 기기가 있고

안드로이드 스마트폰에서 확인해본 결과 40943 인 문자를 사용합니다.

 

그래서 다시 정규식으로 표현하면

import re
text = '''
소년은 개울가에서 소녀를 보자 곧 윤 초시네 증손녀(曾孫女)딸이라는 걸 알 수 있었다.
소녀는 개울에다 손을 잠그고 물장난을 하고 있는 것이다.
서울서는 이런 개울물을 보지 못하기나 한 듯이.
벌써 며칠째 소녀는, 학교에서 돌아오는 길에 물장난이었다.
그런데, 어제까지 개울 기슭에서 하더니, 오늘은 징검다리 한가운데 앉아서 하고 있다.
소년은 개울둑에 앉아 버렸다. 소녀가 비키기를 기다리자는 것이다.
요행 지나가는 사람이 있어, 소녀가 길을 비켜 주었다. 다음 날은 좀 늦게 개울가로 나왔다.
'''
p = re.compile('소년[가-힣一-鿯() ]*\.')
result = p.findall(text)
print(result)
['소년은 개울가에서 소녀를 보자 곧 윤 초시네 증손녀(曾孫女)딸이라는 걸 알 수 있었다.', '소년은 개울둑에 앉아 버렸다.']

문자 클래스로 표현하기 때문에 () 소괄호와 띄어쓰기도 표현해줘야 됩니다.

 

 

같은 방식으로 일본어의 유니코드를 찾아보면

시작은 u+3040, 끝은 u+30ff 입니다.

print(0x3040, 0x30ff)
12352 12543

 

위의 숫자로 실제 문자를 확인합니다.

for n in range(12352, 12544):
    print(n, chr(n), end=' / ')

그런데 시작 번호인 12352를 출력해보면 공백으로 보이게 됩니다.

이유는 모르겠네요.

어쨌건 그래서 12353에 해당하는 문자를 시작 번호라고 보고 확인해보면 ぁ

끝 번호인 12544에 해당하는 문자는 입니다.

 

마지막으로 일본어까지 반영해서 완성된 코드 입니다.

import re
text = '''
소년은 개울가에서 소녀를 보자 곧 윤 초시네 증손녀(曾孫女 ひまごむすめ)딸이라는 걸 알 수 있었다.
소녀는 개울에다 손을 잠그고 물장난을 하고 있는 것이다.
서울서는 이런 개울물을 보지 못하기나 한 듯이.
벌써 며칠째 소녀는, 학교에서 돌아오는 길에 물장난이었다.
그런데, 어제까지 개울 기슭에서 하더니, 오늘은 징검다리 한가운데 앉아서 하고 있다.
소년은 개울둑에 앉아 버렸다. 소녀가 비키기를 기다리자는 것이다.
요행 지나가는 사람이 있어, 소녀가 길을 비켜 주었다. 다음 날은 좀 늦게 개울가로 나왔다.
'''
p = re.compile('소년[가-힣一-鿯() ぁ-ヿ]*\.')
result = p.findall(text)
print(result)
['소년은 개울가에서 소녀를 보자 곧 윤 초시네 증손녀(曾孫女 ひまごむすめ)딸이라는 걸 알 수 있었다.', '소년은 개울둑에 앉아 버렸다.']

정리하자면

 

한글의 범위 [가-힣]

44032 (u+ac00 또는 0xac00) ~ 55203 (u+d7a3 또는 0xd7a3)

 

한자의 범위 [一-

19968 (u+4e00 또는 0x4e00) ~ 40959 (u+9fff 또는 0x9fff)  ← 실제 범위

19968 (u+4e00 또는 0x4e00) ~ 40943 (u+9fef 또는 0x9fef) ← 부득이하게 수정된 범위 (문자 표현이 되질 않아서)

 

일본어의 범위 [ぁ-ヿ]

12353 (u+3041 또는 0x3041) ~ 12543 (u+30ff 또는 0x30ff)

 

이렇게 됩니다.