정규 표현식을 공부하면서, 간단한 패턴이나 토큰 등을 한눈에 보기 쉽게 정리해두려고 한다.

참고로 regex101.com/에 가면 우측 하단에 regex dictionary가 있어서 토큰 등을 쉽게 찾아볼 수 있다.

 

정규표현식은

  • 숫자 혹은 문자를 나타내는 토큰 ([a-zA-Z], \s, \d 등)
  • 앵커(anchor) : 주어진 string의 어느 위치부터 매칭을 시작할 것인가
  • 수량자(quantifier) : 앞선 패턴이 몇 번 반복될때 매칭할 것인가
  • 그룹 캡처

등의 패턴을 직접 입력해서, 주어진 문자열로부터 원하는 패턴의 substring을 뽑아내는 기술이다.

 

 

토큰

자주쓰는 것만 빠르게 볼 수 있게

. any single char (Except \n) .{3}
문자 3개 (F15, 0x0 등)
\d any digit [0-9] \d\d\d
0 ~ 9 범위의 숫자가 3개를 의미 (123, 000 등)
\w any word [a-zA-Z0-9_] \w\w\w
문자가 3개를 의미
(xyz, ABC 등)
\s white space [\t\n\r\f]  \s\s
화이트 스페이스 문자
2개 의미 (\r\n, \t\t 등)
[문자들] 문자들 중에 하나이어야 함. 
가능한 문자들의 집합을 정의함.
[Pp]ython
"Python" 혹은 "python"
[^문자들] [문자들]의 반대로 피해야할 문자들의 집합을 정의함. [^aeiou]
소문자 모음이 아닌 문자들

 

^ 이 패턴으로 시작 ^abc
abc로 시작해야 함
(abcd, abc12 등)
$ 이 패턴으로 종료  xyz$
xyz로 종료되어야 함 
(123xyz, strxyz 등)

참고) http://pythonstudy.xyz/python/article/401-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D-Regex

 

Quantifier

* 0 이상 (있든 없든)  \d*
숫자가 없거나 하나 이상
? 0 또는 하나  \d?
숫자가 없거나 하나
+ 하나 이상  \d+
숫자가 하나 이상
패턴{N} 패턴 N개 반복 \d{3}
숫자가 3개 있어야 함
패턴{N, } N개 이상 \d{3}
숫자가 3개 이상 있어야 함
패턴{N,M} N개 이상 M개 이하  \d{3,5}
숫자가 3개, 4개 혹은 5개

 

중복 검색

(\w)\1 
\w로 캡쳐한 문자가 뒤에서 한번 더 나타나면 catch
(.)\1{2}
.로 캡쳐한 문자가 뒤에서 두번 더 나타나면 catch
(.)\1+
.로 캡쳐한 문자가 뒤에서 한번 이상 나타나면 catch)

 

 

그룹

그룹 0는 매칭된 부분 전체를 지칭.

그룹 1~ 은 사용자가 ( ) 등을 활용해서 원하는 부분을 캡쳐 그룹으로 캡쳐 가능

 

캡처한 그룹은 재활용 가능 → regex 범용성을 크게 늘려줌

(보통은 \n 으로 backreference, 그러나 replacement에서는 $n를 쓰는 듯)

(...) 캡쳐 그룹
(Id 1부터 부여, \n 으로 참조)
(\w*)\1
연속된 두 단어
abcdabcd
zzzzzz
(?: ...) Non-capture 그룹 
(캡쳐와 동일, ID만 없음)
(?:\d{1,3}\.){3}
숫자. 표현이 3번 반복
123.12.3.
1.12.123.
111.111.111.

 

q.append((new_cost, nxt))
q.append((testing, hello))
를 
heapq.heappush(q, (new_cost, nxt))
heapq.heappush(q, (testing, hello))
로 바꾸고자 함.

search  : (q).append\(\((\w+), (\w+)\)\) 그룹 1은 q, 그룹 2는 첫번째 단어, 그룹 3은 두번째 단어
replace : heapq.heappush\($1, \($2, $3\)\) 재사용

 

이 예시

레퍼런스 (API)

 

 

실활용 예시)

목표

![[TITLE]] 형태로 이루어진 파일 이름을

![](img/TITLE) 형태로 바꾸려고 함

 

Find : !\[\[(.*)]] - 안쪽 TITLE을 1번 그룹으로 catch

Replace : ![](img/$1) - 1번 그룹을 그대로 paste

(Sublime3 find는 regex를 기본 지원하길래 이걸로 편하게 바꿈)

 

 

프로그래밍 언어 내의 regex 지원 함수

C++

regex_match : string 전체가 입력한 regex 패턴에 매치되야 함.

// 참고) \를 입력하기 위해 escape 문자인 \를 또 써줘야 함; 그래서 \d가 아닌 \\d
std::regex re("[01]{3}-(\\d{3,4})-\\d{4}"); // 010-1234-5678 같은 전화번호 매칭
std::smatch match;  // 매칭된 string 결과를 컨테이너에 보관 (const char * 은 cmatch)
for (const auto &number : phone_numbers) {
  if (std::regex_match(number, match, re)) {
    // 주어진 문자열이 패턴을 만족한다면, match에는 매칭된 그룹들이 저장 (첫번째는 항상 문자열 전체)
    for (size_t i = 0; i < match.size(); i++) {
    	std::cout << "Match : " << match[i].str() << std::endl;
    }
    std::cout << "-----------------------\n";
  }
}

만약 주어진 문자열이 regex 패턴을 만족한다면, 캡처한 그룹에 해당하는 substring들이 smatch (cmatch) 컨테이너에 저장됨. For문 써서 배열 흝듯이 탐색 가능.

* 문자열 전체가 regex 패턴을 만족하므로, 항상 문자열 전체가 group 0에 해당됨.

즉, smatch의 첫번째 원소는 항상 문자열 전체.

 

 

regex_search : string 중 regex 패턴을 만족하는 substring을 다 찾아줌

std::regex pattern("[1-9][0-9]{0,2}"); // 3자리까지의 자연수 찾기 (ex. 1, 23, 456, 999)
std::smatch match;
while (std::regex_search(sample_str, match, pattern)) {
    std::cout << "Match : " << match.str() << "\n"; // std::string 반환
    ...

    sample_str = match.suffix();
}

 

regex_replace : 원하는 패턴의 문자열을 다른 문자열로 치환

 

 

실전 예시)

BOJ5430 - AC
다음과 같이 문자열이 주어질 때, 숫자만 '각각' 그룹핑

 

[1,2,3,4]
위 문자열 전체가 Full match가 뜨게 하고,
안의 4개 숫자를 각각 그룹핑하려면?

 

* 괄호 (), [] 등은 그냥 쓰면 특수 커맨드로 인식되어서,
char 그 자체로 사용하고 싶다면 escape character '\' 앞에 써줘야 함.
(참고로 C++ 코드 내에서 사용할 땐 \도 특수문자라, \ 앞에 \ 써줘서 '\\(' 처럼 써야함)

여러 가지 시도들 :
\[.+\] 괄호 [ ] 안의 모든 문자 전체 캐치
(\[).+(\]) 전체 캐치 + 두 괄호 [ 랑 ] 가 각각 그룹으로 캡처 (\1, \2로 불러오기)
\[(\d|,)+\] 전체 캐치 + 1,2,3,4 중 4만 그룹1으로 캐치
\[((\d|,)+)\] 전체 캐치 / 1,2,3,4 그룹 1 / 4 그룹 2
\[((?:\d|,)+)\] 전체 캐치 + 1,2,3,4 그룹 1

 

한 가지 regex 패턴으로는, 정확히 일치하는 한 가지 그룹밖에 캡처 못함.

 

전체 match 따지는 regex_match 말고,
숫자만 캐치하는 regex 짠다음에 regex_search 쓰셈.
https://stackoverflow.com/questions/37003623/how-to-capture-multiple-repeated-groups

 

실전 예시 2)

연속으로 나타나는 문자 캐치
ex) RRDDDR 에서 RR / DDD / R 각각 캐치
(\w)\1*
https://stackoverflow.com/questions/3779477/regex-for-detecting-the-same-character-more-than-five-times

 

실전 예시 3)

BOJ2044 - windows

+-|a|-+

+--|hello|---+

등의 문자열이 주어질 때, 중간의 a, hello 같은 문자열만 빼내기

 

방법 1 - 안쪽의 타이틀 문자열만 매칭해서, regex_search 함수로 substring 찾기

[a-zA-Z]+

std::regex pattern("[a-zA-Z]+");
std::smatch match;
string sample_str = line;
while (std::regex_search(sample_str, match, pattern)) {
  windows.push_back({match.str(), row, col, right_extend});
  sample_str = match.suffix();
}

 

방법 2 - 전체를 완벽히 매칭하면서 타이틀 문자열을 그룹으로 캐치, regex_match 함수로 캐치한 그룹 빼오기 

\\+-+\\|([a-zA-Z]+)\\|-+\\+

/*
+--|title|---+ 뭉치와 양 옆의 . - + 탐지
뭉치 내의 +와 | 는 한번 씩만 등장하도록
뭉치 내의 - 개수와 title 문자 길이는 '+' 수량자를 써서 쓰고 싶은 만큼 디텍트
뭉치 양 옆의 . - + 는 '*' 수량자를 써서, 나오거나 나오지 않거나 어찌됬든 디텍트
*/
std::regex re("[\\.\\-\\+]*\\+-+\\|([a-zA-Z]+)\\|-+\\+[\\.\\-\\+]*");
std::smatch match;
string sample_str = line;
if (std::regex_match(sample_str, match, re)) {
	windows.push_back({match[1].str(), row, col, right_extend});
}

 

[8Mar21] 일단 둘 다 통과 못함.

방법 1은 .+-|a|-++-|b|-+ 같은 반례에서 segfault

방법 2는 그냥 일반 예시도 제대로 통과 못함. 공부 필요


그냥 인터넷 돌아다니면서 공부하다가 본 괜찮은 패턴들 모아둠 

 

%(?:\d+)?[dfsc]
printf, scanf 등에 쓰이는 placeholder 잡아냄
ex) printf("%3f", 3) 에서 %3f 부분만 캐치
(좀더 엄밀한 버전 : https://regex101.com/r/bV9nT8/3)

stackoverflow.com/questions/6915025/regexp-to-detect-if-a-string-has-printf-placeholders-inside/29403060

 

* non-capturing group
(?:regex)?
(?:regex)


\<(?.+?)\> [^<]*? \<\/\k\>
HTML tag opening + closing 캡쳐
ex) hello

 

* name-capturing group
캡처 : (?regex)
Back-reference : \k

https://stackoverflow.com/questions/3512471/what-is-a-non-capturing-group-in-regular-expressions

 


튜토리얼

modoocode.com/303

evan-moon.github.io/2020/07/24/about-regular-expression/

불규칙 속에서 규칙을 찾아내는 정규 표현식 _ Evans Library.mhtml
0.59MB
정규식은 어떻게 사용되는 것일까_ _ Evans Library.mhtml
0.54MB

 

파이썬 regex 가이드 https://wikidocs.net/4308#_2

 

 

Reference

www.regular-expressions.info/refwordboundaries.html

 

+ Recent posts