Skip to content

Regular expression

정규 표현식(正規表現式, 영어: regular expression, 간단히 regexp 또는 regex) 또는 정규식(正規式)은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어이다. 정규 표현식은 많은 텍스트 편집기와 프로그래밍 언어에서 문자열의 검색과 치환을 위해 지원하고 있으며, 특히 펄과 Tcl은 언어 자체에 강력한 정규 표현식을 구현하고 있다.

컴퓨터 과학의 정규 언어로부터 유래하였으나 구현체에 따라서 정규 언어보다 더 넓은 언어를 표현할 수 있는 경우도 있으며, 심지어 정규 표현식 자체의 문법도 여러 가지 존재하고 있다. 이 중 표준화된 것으로는 POSIX의 확장 정규 표현식이 있으며, 표준화되지는 않았지만 펄의 정규 표현식과 그 대체 구현인 PCRE도 널리 사용된다.

문자열을 토큰으로 변환하는 과정에서 텍스트를 좌에서 우로 검사하면서 여러 규칙과 일치될 문자의 수량을 다양하게 시도하는 처리에 가장 적합할 뿐 파싱에는 적절치 않다.

표현식(expression)이란 문자 그대로의 의미 이상으로 해석되는 메타문자(metacharacters)라고 부르는 문자들의 집합이다. 예를 들어, 인용 부호(quote symbol)는 어떤 사람이 말한 것을 나타내 주기도 하지만 또한 그 뒤에 나오는 심볼에 대해서 메타적 의미를 부여하기도 합니다. 정규 표현식(Regular Expressions)은 유닉스에 특별한 특징을 부여하는 문자들과 메타문자들의 집합이다. 주로 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용된다.

Cheat Sheet

Regular_expressions_cheat_sheet_v2.png

논리적 관계를 나타내는 기호

|
선택적기호는 기호는 여러 식 중에서 하나를 선택한다.
abc|adcabc라는 문자열과 adc라는 문자열을 모두 포함한다.
()
묶기기호는 여러 식을 하나로 묶을 수 있다.
abc|adca(b|d)c는 같은 의미를 가진다.
*
0개 이상.
a*bb, ab, aab, aaab 등을 포함한다.
+
1개 이상.
a+bab, aab, aaab를 포함하지만 b는 포함하지 않는다.
?
0개 또는 1개.
a?bb, ab를 포함한다.
{m, n}
m개 이상 n개 이하.
a{1,3}bab, aab, aaab를 포함하지만, baaaab는 포함하지 않는다.
이에 따라 (fa|mo|b?o)therfather, mother, bother, other를 나타낸다.
  • {M,} M자 이상
  • {,N} N자 이하
[]
[] 사이의 문자 중 하나를 선택한다. |를 여러 개 쓴 것과 같은 의미를 가진다.
예를 들면 [abc]dad, bd, cd를 뜻한다.
또한, - 기호와 함께 쓰면 문자의 범위를 지정할 수 있다.
[a-z]a부터 z까지 중 하나, [1-9]1부터 9까지 중의 하나를 뜻한다.
[^]
[^] 사이의 문자를 제외한 나머지 하나를 선택한다.
[^abc]dad, bd, cd는 포함하지 않고 ed, fd 등을 포함한다.
[^a-z]는 알파벳 소문자로 시작하지 않는 모든 문자를 나타낸다.
^, $
각각 문자열의 처음과 끝을 나타낸다.

비탐욕적(Non-greedy) 매칭

기본적으로 *은 가능한 한 많이 매칭하는 탐욕적(Greedy) 동작을 합니다.

하지만 뒤에 ?를 붙이면 가능한 한 적게 매칭하려고 시도하는 비탐욕적(Non-greedy) 매칭을 시도한다.

예: 문자열 "abcabc"에서 정규식 a.*c는 "abcabc" 전체를 매칭하지만, a.*?c는 "abc"만 매칭합니다.

문자 클래스(character class)

패턴을 [a-z][~A-Z], [0-9]로 표현하는 대신 다음과 같이 문자 클래스(character class)로 간단하게 표현할 수도 있다.

  • [[:alpha:]]: 알파벳 문자 중 하나를 의미하며 [a-zA-Z], 또는 [a-Z]와 동일한 표현이다.
  • [[:digit:]]: 숫자 한 자를 의미하며 [0-9]와 동일한 표현이다.
  • [[:alnum:]]: 알파벳 문자나 숫자 중 하나를 의미하며 [a-zA-Z0-9]와 동일한 표현이다.
  • [[:space:]]: 공백 문자 ( )를 의미한다.

Escape

다음 특수 시퀀스 목록은 완전하지 않습니다. 유니코드 문자열 패턴에 대한 시퀀스와 확장 클래스 정의의 전체 목록은, 표준 라이브러리 레퍼런스에서 정규식 문법의 마지막 부분을 참조하십시오. 일반적으로, 유니코드 버전은 유니코드 데이터베이스의 적절한 범주에 있는 모든 문자와 일치합니다.

  • \d - 모든 십진 숫자와 일치합니다; 이것은 클래스 [0-9]와 동등합니다.
  • \D - 모든 비 숫자 문자와 일치합니다; 이것은 클래스 [^0-9]와 동등합니다.
  • \s - 모든 공백 문자와 일치합니다; 이것은 클래스 [ \t\n\r\f\v] 와 동등합니다.
  • \S - 모든 비 공백 문자와 일치합니다; 이것은 클래스 [^ \t\n\r\f\v] 와 동등합니다.
  • \w - 모든 영숫자(alphanumeric character)와 일치합니다; 이것은 클래스 [a-zA-Z0-9_]와 동등합니다.
  • \W - 모든 비 영숫자와 일치합니다; 이것은 클래스 [^a-zA-Z0-9_]와 동등합니다.

정규 표현식에서 특수 문자의 표현

지금까지 정규 표현식에서 사용한 여러 특수 문자를 특수 문자가 아닌 그 문자 자체의 의미로 사용하려면 특수 문자 앞에 역슬래시('\') 문자를 붙여 escape시켜야 한다. 정규 표현식에서 escape시켜야 하는 특수 문자로는 다음과 같은 것들이 있다.

^ . [ ] $ ( ) | * + ? { } \

Extended Regular Expressions

The extended regular expression (ERE) notation and construction rules shall apply to utilities defined as using extended regular expressions; any exceptions to the following rules are noted in the descriptions of the specific utilities using EREs.

Examples

^[0-9]+$
부호 없는 정수형 숫자
^[0-9]+([.][0-9]+)?$
부호 없는 부동형 숫자
^[+-]?[0-9]+([.][0-9]+)?$
부호 있는 부동형 숫자
find . -mindepth 1 -maxdepth 1 -name '*.m4a' | grep -E '.*_23[0-9]{4}_[0-9]{6}.m4a$' | xargs -I{} mv "{}" ./2023/
통화 녹음 15662566_230711_203603.m4a과 같은 파일을 2023 디렉토리로 이동. (날짜가 Suffix 에 위치한 파일 필터링)

Special Groups

Feature

Syntax

Description

Comment

(?#comment)

Everything between (?# and ) is ignored by the regex engine.

Branch reset group

(?|regex)

If the regex inside the branch reset group has multiple alternatives with capturing groups, then the capturing group numbers are the same in all the alternatives.

Atomic group

(?>regex)

Atomic groups prevent the regex engine from backtracking back into the group after a match has been found for the group. If the remainder of the regex fails, the engine may backtrack over the group if a quantifier or alternation makes it optional. But it will not backtrack into the group to try other permutations of the group.

Positive lookahead

(?=regex)

Matches at a position where the pattern inside the lookahead can be matched. Matches only the position. It does not consume any characters or expand the match. In a pattern like one(?=two)three, both two and three have to match at the position where the match of one ends.

Negative lookahead

(?!regex)

Similar to positive lookahead, except that negative lookahead only succeeds if the regex inside the lookahead fails to match.

Positive lookbehind

(?<=regex)

Matches at a position if the pattern inside the lookbehind can be matched ending at that position.

Negative lookbehind

`(?

Matches at a position if the pattern inside the lookbehind cannot be matched ending at that position.

Lookbehind

(?<=regex|longer regex)

Alternatives inside lookbehind can differ in length.

Lookbehind

(?<=x{n,m})

Quantifiers with a finite maximum number of repetitions can be used inside lookbehind.

Lookbehind

(?<=regex)

The full regular expression syntax can be used inside lookbehind.

Lookbehind

(group)(?<=\1)

Backreferences can be used inside lookbehind. Syntax prohibited in lookbehind is also prohibited in the referenced capturing group.

Keep text out of the regex match

\K

The text matched by the part of the regex to the left of the \K is omitted from the overall regex match. Other than that the regex is matched normally from left to right. Capturing groups to the left of the \K capture as usual.

Lookaround conditional

(?(?=regex)then|else) where (?=regex) is any valid lookaround and then and else are any valid regexes

If the lookaround succeeds, the “then” part must match for the overall regex to match. If the lookaround fails, the “else” part must match for the overall regex to match. The lookaround is zero-length. The “then” and “else” parts consume their matches like normal regexes.

Implicit lookahead conditional

(?(regex)then|else) where regex, then, and else are any valid regexes and regex is not the name of a capturing group

If “regex” is not the name of a capturing group, then it is interpreted as a lookahead as if you had written (?(?=regex)then|else). If the lookahead succeeds, the “then” part must match for the overall regex to match. If the lookahead fails, the “else” part must match for the overall regex to match. The lookaround is zero-length. The “then” and “else” parts consume their matches like normal regexes.

Named conditional

(?(name)then|else) where name is the name of a capturing group and then and else are any valid regexes

If the capturing group with the given name took part in the match attempt thus far, the “then” part must match for the overall regex to match. If the capturing group did not take part in the match thus far, the “else” part must match for the overall regex to match.

Named conditional

(?(<name>)then|else) where name is the name of a capturing group and then and else are any valid regexes

If the capturing group with the given name took part in the match attempt thus far, the “then” part must match for the overall regex to match. If the capturing group did not take part in the match thus far, the “else” part must match for the overall regex to match.

Named conditional

(?('name')then|else) where name is the name of a capturing group and then and else are any valid regexes

If the capturing group with the given name took part in the match attempt thus far, the “then” part must match for the overall regex to match. If the capturing group did not take part in the match thus far, the “else” part must match for the overall regex to match.

Conditional

(?(1)then|else) where 1 is the number of a capturing group and then and else are any valid regexes

If the referenced capturing group took part in the match attempt thus far, the “then” part must match for the overall regex to match. If the capturing group did not take part in the match thus far, the “else” part must match for the overall regex to match.

Relative conditional

(?(-1)then|else) where -1 is a negative integer and then and else are any valid regexes

Conditional that tests the capturing group that can be found by counting as many opening parentheses of named or numbered capturing groups as specified by the number from right to left starting immediately before the conditional. If the referenced capturing group took part in the match attempt thus far, the “then” part must match for the overall regex to match. If the capturing group did not take part in the match thus far, the “else” part must match for the overall regex to match.

Forward conditional

(?(+1)then|else) where +1 is a positive integer and then and else are any valid regexes

Conditional that tests the capturing group that can be found by counting as many opening parentheses of named or numbered capturing groups as specified by the number from left to right starting at the “then” part of conditional. If the referenced capturing group took part in the match attempt thus far, the “then” part must match for the overall regex to match. If the capturing group did not take part in the match thus far, the “else” part must match for the overall regex to match.

Conditional

(?(+1)then|else) where 1 is the number of a capturing group and then and else are any valid regexes

The + is ignored and the number is taken as an absolute reference to a capturing group. If the referenced capturing group took part in the match attempt thus far, the “then” part must match for the overall regex to match. If the capturing group did not take part in the match thus far, the “else” part must match for the overall regex to match.

Lookaround

패턴

타입

matches

X(?=Y)

긍정형 전방탐색 (Positive Lookahead)

X가 Y앞에 있을 때

X(?!Y)

부정형 전방탐색 (Negative Lookahead)

X가 Y앞에 없을 때

(?<=Y)X

긍정형 후방탐색 (Positive Lookbehind)

X가 Y뒤에 있을 때

`(?

부정형 후방탐색 (Negative Lookbehind)

X가 Y뒤에 없을 때

(참고로 전방과 후방 문법이 다른 이유는 X{Pattern}X와 같이 있을 때 전방을 검색할지 후방을 검색할지 모르기 때문이다.)

LookaheadLookbehind 기능을 모아서 Lookaround 라고 합니다.

본 기능이 동작하는 방식은, 이해하기 쉽게 설명을 하자면,

  1. 본래 패턴에 맞는 곳을 우선 찾고,
  2. 그 다음 추가적으로 해당 Lookaround 의 조건이 맞는지 아닌지를 체크합니다.
  3. 그 다음 Positive 이냐, Negative 이냐에 따라서 결과를 포함시키거나 제외시킵니다.

Positive와 Negative 차이는

  • Positive 는 조건 Y 가 만족한다면 매칭 결과에 그대로 포함시킨다는 것입니다. 즉 조건 Y가 충족하지 않는다면 매칭 결과에 포함이 되지 않겠죠.
  • Negative 는 조건 Y 가 만족한다면 매칭 결과에서 제외시킵니다. 즉 조건 Y가 충족하지 않는다면 매칭 결과에 포함이 될 것입니다.

Lookahead와 Lookbehind 의 차이점은 간단합니다.

  • Lookahead 는 꼬리말(suffix)을 확인하는 용도이고,
  • Lookbehind는 머리말(prefix)를 확인하는 용도입니다.

말이 약간 거꾸로 되어 있지요? 실제 문법 상으로는 <가 포함되어 있냐 그렇지 않냐로 구분할 수 있습니다. < 방향이 왼쪽이니까 앞쪽이라고 기억하고 있으면 될 것 같습니다.

일반적으로는 간단히 결과를 제외시키고 싶을 때 Negative Lookaround 로 하여 많이 사용합니다. (참고, 스택 오버플로우)

한 가지 특징으로는, Lookaround 기능은 그 결과가 소모되지 않습니다. 즉 조건이 겹칠 필요가 있을 때 유용하게 사용될 수 있다는 것입니다. 이는 원문 사이트의 예제로 확인.

일치하지 않는 문장

간단한 예제로:

  • install(?!ed) 라고 쓰면 접미사에 ed가 없는 install 글자를 찾는다.
  • `(?

grep 에서 사용하는 방법

-P 플래그로 Perl호환 표현식을 사용해야 한다.

간단한 예제로:

dpkg --get-selections | grep -P '(?<!de)install$'

Examples

AWS Amplify의 SPA 리디렉션 패턴

See also

Favorite site

Online Tools

References


  1. MSDN_-Regular_Expressions-_C++.pdf 

  2. Regular_expression_theorem.pdf