컴퓨터 공부/🐍 Python

정규식을 사용해 여러개의 구분자로 split 하는 방법

letzgorats 2024. 9. 21. 21:41

 

Python의 string 내장함수인 split을 사용하면 구분자를 기준으로 string을 list로 나눌 수 있다.

str = "Hello Aloo! I love how you treat me!"
str = str.split()
print(str)
# ['Hello', 'Aloo', 'I', 'love', 'how', 'you', 'treat', 'me!']

str = "What's up, Aloo, Nice to meet you!"
str = str.split(',')
print(str)
# ['What's up', ' Aloo', ' Nice to meet you!']

 

알고리즘 문제를 풀다 보면, 여러 개의 구분자로 string을 list로 나누고 싶을 때가 있다.

이와 같은 경우에 re(정규식) library 를 사용하면 해결할 수 있다.


re.split()으로 여러 구분자 사용하기

re 라이브러리는 정규표현식에 관련된 Python 표준 라이브러리이다. 이를 이용해 여러 구분자를 쉽게 처리할 수 있다.

(※ 이전에 정규식과 관련한 파이썬 포스팅을 한 적이 있다.)

 

예를 들어, "100-200*300-500+600" 이라는 문자열을, '-, +, *' 와 같은 연산자 기호를 기준으로 나누고 싶다고 해보겠다.

import re

expression = "100-200*300-500+600"
print (re.split('[-|+|*]', expression))

# ['100','200,'300','500','600']

 

위 코드는 '-, +, *'를 기준으로 문자열을 분리한다. 그런데, 만약 구분자 자체도 리스트에 포함시키고 싶다면, 정규식에서 그룹핑을 사용해야 한다. () 괄호로 구분자를 감싸면 구분자도 함께 리스트에 포함된다.

import re

expression = "100-200*300-500+600"
print (re.split('([-\+*]', expression))

# ['100', '-', '200', '*', '300', '-', '500', '+', '600']

 

여기서 중요한 부분은, '-' 기호가 정규식에서 범위를 나타내는 데 사용될 수 있기 때문에, 제대로 인식하려면, 이를 문자 그대로 인식하게 만들어야 한다는 점이다. [-\+*] 에서' \+' 처럼 '\'를 사용하여 구분자를 명시적으로 처리할 수 있다.


 

위에 살펴봤던 예시가 잘 이해가 안되면, 더 구체적인 예시를 들어보겠다.

import re

expression = "100-200*300-500+600"
print (re.split('[+|-|*]', expression))

# ['100-200', '300-500', '600']

 

위 코드에서 re.split('(+|-\*]', expression) 에서 '+' 와 '-' 위치만 바꿔줬을 뿐인데, 제대로 구분자를 인식 못하는 출력결과가 나왔다. 이유는 정규식 내에서 '[]'는 문자 클래스를 의미하기 때문이다. 문자 클래스 안에서는 모든 문자가 개별 문자로 처리되며, '|' 는 파이프(OR 연산자)가 아니라 그냥 문자로 인식된다.

 

따라서, [+|-|*] 는 "문자 +, 문자 -, 문자 |, 문자 * 중 하나" 라는 뜻으로 해석될 여지가 있다. 즉, '|' 를 OR 연산자로 인식하지 않으며,

'[-|*+]' 는 "이 중 하나라도 매칭되면 분리한다" 는 의미가 된다.

 

이를 해결하려면 [] 문자 클래스가 아니라, OR 연산자로서의 | 를 사용해야 하니까 각각의 구분자를 묶어주는 괄호를 사용하지 않고, '\'를 사용해서 표현해줘야 한다.

import re

expression = "100-200*300-500+600"
print(re.split('[+\-*]', expression))

# ['100', '200', '300', '500', '600']

 

이렇게 말이다. 아래와 같이 각 구분자를 그룹핑해도 된다.

import re

expression = "100-200*300-500+600"
print(re.split(r'[\+\-\*]', expression))

# ['100','200','300','500','600']
 
여기서 '+,-,*' 는 모두 특수문자이므로 이를 이스케이프(\) 해줘야 정확히 구분자로 인식된다. 이처럼 [] 내부에서는 파이프(|)가 OR 연산자로 작동하지 않는다는 것을 기억하면 더 명확하게 이해할 수 있다.

근데, 왜 숫자말고, 연산자도 리스트로 나누고 싶어서 아래와 같이 구분자를 분리하려고 하면,
import re 
expression = "100-200*300-500+600"

print (re.split('[+|-|*]', expression))

# ['100-200', '300-500', '600']
 
이건 제대로 인식하지 못하는데,
import re
expression = "100-200*300-500+600"

print (re.split('[-|+|*]', expression))

# ['100', '200', '300', '500', '600']
 
이건 왜 제대로 인식하는걸까? 
 
 
사실, '+'와 '-'의 위치만 서로 달라졌을 뿐이지만, 이러한 결과가 나오는 이유는 정규식에서 '[](문자 클래스)' 와 '-'역할 때문이다.

1. re.split('[+|-|*]', expression)의 경우

여기서 [] 안에 '+,|,-,*' 가 들어가 있다. 그런데, 중요한 점은 [] 안에서 '-' 는 범위 연산자로 사용될 수 있다.
즉, [+|-|*] 라고 하면 파이썬은 '-'를 범위 지정자로 인식해서 '+' 와 '*' 사이에 있는 모든 문자를 포함하려고 시도한다.
하지만, '+' 와 '*' 사이에 알파벳이나 숫자가 있는 게 아니기 때문에 이 정규식은 제대로 작동되지 않게 되는 것이다. 그리고 '|' 역시 OR 연산자가 아니라 문자 그대로 인식된다.
 
정리하자면, 이 표현식에서는 
  • '-' 를 범위로 인식하려다가 오류를 일으키고, 
  • '|' 는 OR 연산자가 아니라 문자 그대로 처리하려고 해서 오류를 일으키는 것이다. 

2. re.split('[-|+|*]', expression)의 경우

여기서 [] 안에 '-,|,+,*' 가 있는 형태인데, '-'의 위치가 처음에 있으면, 범위로 인식되지 않는다! 이게 바로 중요한 차이점이다.
[] 안에서 '-'가 범위 연산자로 사용되려면 반드시 두 문자 사이에 있어야 하기 때문이다.
 
  • 정리하자면, 이 표현식은
  • '-' 는 문자 그대로 인식되고,
  • '|' 는 사실 여전히 문자로 인식되고,
  • '+' 와 '*' 도 문자 그대로 인식된다.

 
그래서, 가장 좋은 방법은 특수 문자를 명확하게 이스케이프(\)로 처리하는 것이다. 이 방법이 의도하지 않은 오류나 예상 밖의 동작을 피할 수 있고, 코드의 가독성도 높여주기 때문이다.
 
가장 추천하는 방식
import re

expression = "100-200*300-500+600"
print(re.split(r'[\+\-\*]', expression))
# ['100', '200', '300', '500', '600']
 
만약, 연산자도 구분자에 포함시키려면, 괄호로 묶어 주어야 구분자도 함께 리스트에 포함된다.
import re

expression = "100-200*300-500+600"
print(re.split(r'([\+\-\*])', expression))

# ['100', '-', '200', '*', '300', '-', '500', '+', '600']

 

반응형