지난 포스트에서는 정규표현식의 기본 문법에 대해 학습했다. 아는 만큼 보인다더니, 정규식을 공부하고 나니 실무와 알고리즘 문제에서도 정말 유용하게 쓰이는 걸 느꼈다. 내가 정리한 블로그 글을 내가 제일 많이 들어가서 다시 본 것 같다(기술블로그의 장점을 잘 활용하는 것 같아 뿌듯하다). 이제 열심히 공부한 정규식 문법을 실전에서 써먹는 방법을 알아보려고 한다. 자바스크립트와 파이썬에서의 정규표현식 사용 방법을 예제코드와 함께 정리한다.
프로그래머스의 파일명 정렬이라는 문제를 풀다가 이 포스트를 작성하게 되었다. 2단계이지만 정규표현식을 이용하면 간단하게 풀린다. 이 문제도 풀이방법을 포스팅할 것이다.
re
라이브러리파이썬에서 정규표현식을 사용하려면 re
라는 라이브러리를 사용해야 한다. 문자열 내 특정 패턴을 검색하거나 치환, 삭제할 수 있는 기능을 제공하는 Regular Expression 라이브러리이다.
re
가 패턴을 인식하는 방법
- 사용자가 정규표현식을 정의한다.
re
에 1에서 정의한 정규표현식을 집어넣으면 정규식 객체가 생성된다.- 생성된 정규식 객체는 자동으로 문자열 내의 패턴을 감지해 원하는 기능을 수행한다.
import re
당연하게도 라이브러리이기 때문에 위처럼 import를 해 줘야 사용이 가능하다. 그러면 이제 re
에서 제공하는 주요 메소드들을 살펴보자.
re.search(pattern, string)
문자열을 스캔하여 정규식에 맞는 첫 번째 위치를 찾아 리턴한다. 찾지 못하면 None을 리턴한다.
import re
pattern = "([0-9]+년)\s*([0-9]+월)\s*([0-9]+일)"
ret = re.search(pattern, "오늘은 2022년 9월 16일이고요, 내일은 2022년 9월 17일입니다.")
print(ret) # <re.Match object; span=(4, 16), match='2022년 9월 16일'>
날짜를 찾아내는 간단한 정규식 예제이다. 첫 번째로 인식된 숫자 3이 ret
정규식 객체에 담겨 반환되었다.
# 패턴 시작 start index
ret.start() # 4
# 패턴 끝 end index
ret.end() # 16
# 인식한 문자열 출력
ret.group() # '2022년 9월 16일'
ret.group(0) # '2022년 9월 16일'
ret.group(1) # '2022년'
ret.group(2) # '9월'
ret.group(3) # '16일'
# 부분적으로 인식된 문자열 출력
ret.groups() # ('2022년', '9월', '16일')
정규식에서 그룹을 지정해주는 괄호()에 해당하는 패턴을 그룹으로 가져올 수도 있다.
re.match(pattern, string)
re.search()
와 유사하게 검색을 하는 함수지만, 차이가 있다.
search
는 문자열의 아무 곳에서나 일치하면 인식하는 반면, match
는 문자열의 시작 부분에서만 일치를 검사한다. 일치하는 패턴이 없는 경우 None을 리턴한다.
import re
string = 'a@a.com, b@b.net'
pattern = "[a-z]+@[a-z]+\.net"
match_result = re.match(pattern, string)
search_result = re.search(pattern, string)
print(match_result) # None
print(search_result) # <re.Match object; span=(9, 16), match='b@b.net'>
.net 이메일 패턴을 찾는 정규식을 match
의 파라메터로 넣어보았다. 일치하는 패턴은 string의 뒷 부분에 있으므로 match
의 결과는 None이다. 한편 search
는 위치 상관없이 인식하므로 .net을 잘 찾아낸다.
re.split(pattern, string)
문자열을 패턴을 기준으로 쪼개어 리스트를 반환해준다.
import re
pattern = "[0-9]"
ret = re.split(pattern, "abc1def")
print(ret) # ['abc', 'def']
re.sub(pattern, repl, string)
string에서 pattern을 찾아 repl으로 치환해준다.
import re
pattern = "[0-9]"
ret = re.sub(pattern, "x", "비밀번호는 1234입니다")
print(ret) # '비밀번호는 xxx입니다'
정규식 패턴에 맞는 숫자가 x로 치환된 것을 확인할 수 있다.
re.compile(pattern)
정규식 패턴을 정규색 객체로 컴파일한다.
import re
obj = re.compile("[0-9]+")
ret = obj.search("숫자검색 1234")
print(ret) # <re.Match object; span=(5, 9), match='1234'>
ret.start() # 5
ret.end() # 9
컴파일한 객체에 위에서 공부한 메소드들을 적용할 수 있다. 하나의 정규식 패턴을 여러 번 사용할 때 compile
을 사용하면 정규식 객체를 여러 개 생성하지 않아도 되니 효율적이다.
자바스크립트에도 파이썬과 유사한 기능을 하는 메서드들이 있다. 정규식 표현방법과 주요 메소드를 살펴보자.
// 리터럴 방식
const regex = /abc/;
// 생성자 방식
const regex = new RegExp("abc");
슬래시 사이에 정규식을 넣거나, new RegExp()
구문을 이용해서 생성하거나의 두 가지 방법으로 정규식을 선언할 수 있다.
자바스크립트에서는 정규식 뒤에 플래그를 붙여 탐색 옵션을 지정할 수 있다.
플래그 | 의미 | 설명 |
i | ignore | 대소문자를 구별하지 않고 검색한다. |
g | global | 문자열 내의 모든 패턴을 검색한다. |
m | multiline | 문자열의 행이 바뀌더라도 계속 검색한다. |
s | .(모든 문자 정규식)이 개행문자 \n도 포함하도록 | |
u | unicode | 유니코드 지원 |
g
(전역 검색)전역 검색 플래그가 없는 경우에는 최초 검색 결과만 반환하는 반면, 전역 검색 플래그가 있는 경우에는 모든 검색 결과를 배열로 반환한다. 마치 파이썬의 re.match()
와 유사하다.
const str = "abcabc";
// `g` 플래그 없이는 최초에 발견된 문자만 반환
str.match(/a/);
// ["a", index: 0, input: "abcabc", groups: undefined]
// `g` 플래그와 함께라면 모든 결과가 배열로 반환
str.match(/a/g);
// (2) ["a", "a"]
m
(줄바꿈 검색)여러 줄의 정규식 문자열이 실제 여러 줄로써 다루어져야 할 때 사용되며, 아래에서 알아볼 입력 시작(^
)과 입력 종료($
)가 전체 문자열이 아닌 각 줄 별로 대응된다.
// 줄바꿈이 포함된 문자열
var str = "\nIs th\nis it?";
str.match(/^is/m); // is
// `` 는 "",''과 달리 개행문자를 포함하여 문자열 구성 가능.
const str = `abc
add`;
// 한줄만 검사
str.match(/c$/g); // ["c"]
// 줄마다 검사
str.match(/c$/g); // (2) ["c", "c"]
i
(대소문자 무시)정규식은 기본적으로 대소문자를 구분하지만 i 플래그를 옵션으로 달면 대소문자르 무시한다.
const str = "abcABC"
str.match(/a/gi);
// (2) ["a", "A"]
문자열에 적용할 수 있는 주요 메소드를 정리한다.
str.search(pattern)
정규표현식을 인자로 받아 가장 처음 매칭되는 부분 문자열의 위치를 반환한다. 매칭되는 문자열이 없으면 -1을 반환한다. 파이썬이랑 똑같다.
"JavaScript".search(/script/); // -1 대소문자를 구분합니다
"JavaScript".search(/Script/); // 4
str.match(pattern)
문자열에서 패턴과 일치하는 결과를 배열로 리턴한다. 일치하는 결과가 없을 시 null을 리턴한다.
let pattern = /p/;
let str = 'apple';
str.match(pattern);
str.replace(pattern, repl)
정규식과 치환하려는 문자를 인자로 받고, 문자열에서 패턴을 검색 후 치환한다. 파이썬의 re.sub(pattern, repl, str)
과 같다.
let pattern = /\d/;
let str = '비밀번호는 1234입니다';
let ret = str.replace(pattern, 'x'); // '비밀번호는 x234입니다'
숫자를 x로 바꾸는 코드를 작성했는데, 첫 번째만 바뀌었다. 모두 바꾸고 싶으면 어떻게 해야 할까?
let pattern = /\d/g;
let str = '비밀번호는 1234입니다';
let ret = str.replace(pattern, 'x'); // '비밀번호는 xxxx입니다'
위에서 배운 전역 플래그를 사용하면 된다.
str.split(separator)
주어진 인자를 구분자로 삼아, 문자열을 부분 문자열로 나누어 그 결과를 배열로 반환한다.
"123,456,789".split(",") // ["123", "456", "789"]
"12304560789".split("0") // ["123", "456", "789"]