Perl 정규표현식 빠른 시작

Perl 정규표현식 빠른 시작 (perlrequick)

Perl에서 정규표현식('regexes')을 이해하고, 만들고, 사용하는 아주 기본기를 다루는 페이지예요.

출처: Perl 공식 문서 - perlrequick

본문

가이드 (The Guide)

이 페이지는 여러분이 "패턴(pattern)"이 어떤 건지, 그리고 패턴을 쓰는 기본 문법을 이미 안다고 가정해요. 아니라면 perlretut을 먼저 보세요.

단순 단어 매치 (Simple word matching)

가장 단순한 정규식은 그냥 단어 하나, 더 일반적으로는 문자 문자열이에요. 단어로 이루어진 정규식은 그 단어를 포함한 어떤 문자열이든 매치해요:

"Hello World" =~ /World/;  # matches

이 문장에서 World가 정규식이고, /World/를 감싼 //는 Perl에게 문자열에서 매치를 검색하라고 알려줘요. 연산자 =~는 문자열을 정규식 매치에 연결하고, 정규식이 매치되면 참 값을, 매치되지 않으면 거짓 값을 돌려줘요. 여기서 World"Hello World"의 두 번째 단어와 매치되므로 이 표현식은 참이 돼요. 이 아이디어에는 여러 변형이 있어요.

이런 표현식은 조건문에서 유용해요:

print "It matches\n" if "Hello World" =~ /World/;

!~ 연산자를 쓰면 매치의 의미를 뒤집을 수 있어요:

print "It doesn't match\n" if "Hello World" !~ /World/;

정규식 안의 리터럴 문자열은 변수로 대체할 수 있어요:

$greeting = "World";
print "It matches\n" if "Hello World" =~ /$greeting/;

$_ 대상으로 매치한다면 $_ =~ 부분은 생략할 수 있어요:

$_ = "Hello World";
print "It matches\n" if /World/;

마지막으로, 매치의 기본 구분자 //는 앞에 'm'을 붙이면 임의의 구분자로 바꿀 수 있어요:

"Hello World" =~ m!World!;   # matches, delimited by '!'
"Hello World" =~ m{World};   # matches, note the matching '{}'
"/usr/bin/perl" =~ m"/perl"; # matches after '/usr/bin',
                             # '/' becomes an ordinary char

정규식이 문장을 참으로 만들려면 문자열의 일부와 정확히 매치해야 해요:

"Hello World" =~ /world/;  # doesn't match, case sensitive
"Hello World" =~ /o W/;    # matches, ' ' is an ordinary char
"Hello World" =~ /World /; # doesn't match, no ' ' at end

Perl은 항상 문자열에서 가장 이른 가능한 지점에서 매치해요:

"Hello World" =~ /o/;       # matches 'o' in 'Hello'
"That hat is red" =~ /hat/; # matches 'hat' in 'That'

모든 문자가 매치에 "그대로" 쓰일 수 있는 건 아니에요. 메타문자(metacharacters) 라고 불리는 일부 문자는 특별하게 취급되고, 정규식 표기법에 쓰도록 예약돼 있어요. 메타문자는 다음과 같아요.

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

메타문자는 그 앞에 백슬래시를 붙이면 리터럴로 매치할 수 있어요:

"2+2=4" =~ /2+2/;    # doesn't match, + is a metacharacter
"2+2=4" =~ /2\+2/;   # matches, \+ is treated like an ordinary +
'C:\WIN32' =~ /C:\\WIN/;                       # matches
"/usr/bin/perl" =~ /\/usr\/bin\/perl/;  # matches

마지막 정규식에서 슬래시 '/'도 백슬래시 처리됐어요. 정규식을 구분하는 문자기 때문이죠.

대부분의 메타문자는 항상 특별한 건 아니고, 다른 문자(패턴을 구분하는 문자 같은)는 다양한 상황에서 특별해져요. 이것은 혼란스럽고 예상치 못한 결과를 만들 수 있어요. use re 'strict'가 잠재적 함정을 알려줄 수 있어요.

인쇄 불가능한 ASCII 문자는 이스케이프 시퀀스로 나타내요. 흔한 예로 탭에 \t, 새 줄에 \n, 캐리지 리턴에 \r이 있어요. 임의의 바이트는 8진 이스케이프 시퀀스(예: \033)나 16진 이스케이프 시퀀스(예: \x1B)로 나타내요:

"1000\t2000" =~ m(0\t2)  # matches
"cat" =~ /\143\x61\x74/  # matches in ASCII, but
                         # a weird way to spell cat

정규식은 대부분 이중 인용 문자열처럼 취급되므로, 변수 치환이 동작해요:

$foo = 'house';
'cathouse' =~ /cat$foo/;   # matches
'housecat' =~ /${foo}cat/; # matches

위의 모든 정규식에서, 정규식이 문자열의 어디에든 매치되면 매치로 간주했어요. 어디서 매치할지를 지정하려면 앵커 메타문자 ^$를 쓰면 돼요. 앵커 ^는 문자열의 시작에서, $는 문자열의 끝(또는 끝의 새 줄 앞)에서 매치하라는 뜻이에요. 몇 가지 예를 볼게요:

"housekeeper" =~ /keeper/;         # matches
"housekeeper" =~ /^keeper/;        # doesn't match
"housekeeper" =~ /keeper$/;        # matches
"housekeeper\n" =~ /keeper$/;      # matches
"housekeeper" =~ /^housekeeper$/;  # matches

문자 클래스 사용하기 (Using character classes)

문자 클래스(character class) 는 단일 문자 대신 가능한 문자들의 집합이 정규식의 특정 지점에서 매치되게 해 줘요. 문자 클래스에는 여러 종류가 있는데, 사람들이 이 용어를 쓸 때는 보통 이 섹션에서 설명하는 종류, 기술적으로 "브래킷 문자 클래스(Bracketed character classes)"라고 불리는 걸 가리켜요. 문자가 매치될 수 있는 집합을 안에 담은 대괄호 [...]로 표기하기 때문이죠. 아래에서는 흔한 용법에 맞춰 "bracketed"를 생략할게요. (브래킷) 문자 클래스의 몇 가지 예를 볼게요:

/cat/;            # matches 'cat'
/[bcr]at/;        # matches 'bat', 'cat', or 'rat'
"abc" =~ /[cab]/; # matches 'a'

마지막 문장에서, 'c'가 클래스의 첫 번째 문자지만 정규식이 매치할 수 있는 가장 이른 지점은 'a'예요.

/[yY][eE][sS]/; # match 'yes' in a case-insensitive way
                # 'yes', 'Yes', 'YES', etc.
/yes/i;         # also match 'yes' in a case-insensitive way

마지막 예시는 'i' 수정자가 붙은 매치를 보여줘요. 이 수정자는 매치를 대소문자 구분 없이 만들어요.

문자 클래스도 보통 문자와 특수 문자가 있지만, 클래스 안의 보통·특수 문자 집합은 클래스 밖과 달라요. 문자 클래스의 특수 문자는 -]\^$이고 이스케이프로 매치해요:

/[\ ]c]def/; # matches ']def' or 'cdef'
$x = 'bcr';
/[$x]at/;   # matches 'bat, 'cat', or 'rat'
/[\$x]at/;  # matches '$at' or 'xat'
/[\\$x]at/; # matches '\at', 'bat, 'cat', or 'rat'

특수 문자 '-'는 문자 클래스 안에서 범위 연산자로 동작해요. 그래서 다루기 힘든 [0123456789][abc...xyz]가 날씬한 [0-9][a-z]가 돼요:

/item[0-9]/;  # matches 'item0' or ... or 'item9'
/[0-9a-fA-F]/;  # matches a hexadecimal digit

'-'가 문자 클래스의 첫 번째나 마지막 문자라면 보통 문자로 취급돼요.

특수 문자 ^가 문자 클래스의 첫 번째 위치에 오면 부정 문자 클래스(negated character class) 를 나타내요. 대괄호 안의 문자를 제외한 어떤 문자든 매치하죠. [...][^...] 모두 반드시 한 문자를 매치해야 하고, 그렇지 않으면 매치가 실패해요. 그래서:

/[^a]at/;  # doesn't match 'aat' or 'at', but matches
           # all other 'bat', 'cat, '0at', '%at', etc.
/[^0-9]/;  # matches a non-numeric character
/[a^]at/;  # matches 'aat' or '^at'; here '^' is ordinary

Perl은 흔한 문자 클래스에 대한 여러 약어를 갖고 있어요. (이 정의들은 Perl이 /a 수정자와 함께 ASCII-안전 모드에서 쓰는 것입니다. 그렇지 않으면 훨씬 더 많은 비-ASCII Unicode 문자도 매치할 수 있어요. 자세한 내용은 "Backslash sequences" in perlrecharclass를 보세요.)

  • \d는 숫자(digit)이고 다음을 나타내요.
[0-9]
  • \s는 공백 문자(whitespace)이고 다음을 나타내요.
[\ \t\r\n\f]
  • \w는 단어 문자(영숫자 또는 _)이고 다음을 나타내요.
[0-9a-zA-Z_]
  • \D는 부정 \d예요. 숫자를 제외한 어떤 문자든 나타내요.
[^0-9]
  • \S는 부정 \s예요. 어떤 비-공백 문자든 나타내요.
[^\s]
  • \W는 부정 \w예요. 어떤 비-단어 문자든 나타내요.
[^\w]
  • 마침표 '.'"\n"을 제외한 어떤 문자와도 매치해요.

\d\s\w\D\S\W 약어는 문자 클래스 안팎 모두에서 쓸 수 있어요. 사용 예시를 볼게요:

/\d\d:\d\d:\d\d/; # matches a hh:mm:ss time format
/[\d\s]/;         # matches any digit or whitespace character
/\w\W\w/;         # matches a word char, followed by a
                  # non-word char, followed by a word char
/..rt/;           # matches any two chars, followed by 'rt'
/end\./;          # matches 'end.'
/end[.]/;         # same thing, matches 'end.'

단어 앵커 \b는 단어 문자와 비-단어 문자 사이의 경계 \w\W 또는 \W\w와 매치해요:

$x = "Housecat catenates house and cat";
$x =~ /\bcat/;  # matches cat in 'catenates'
$x =~ /cat\b/;  # matches cat in 'housecat'
$x =~ /\bcat\b/;  # matches 'cat' at end of string

마지막 예시에서 문자열의 끝은 단어 경계로 간주돼요.

자연어 처리용으로(예를 들어 아포스트로피를 단어에 포함시키려면) 대신 \b{wb}를 써요.

"don't" =~ / .+? \b{wb} /x;  # matches the whole string

이것 아니면 저것 매치하기 (Matching this or that)

교체(alternation) 메타문자 '|'로 서로 다른 문자열을 매치할 수 있어요. dog 또는 cat을 매치하려면 정규식 dog|cat을 만들어요. 앞서처럼 Perl은 문자열의 가장 이른 지점에서 정규식을 매치하려고 해요. 각 문자 위치에서 Perl은 먼저 첫 번째 대안 dog을 매치해 봐요. dog이 매치되지 않으면 다음 대안 cat을 시도해요. cat도 매치되지 않으면 매치가 실패하고 Perl은 문자열의 다음 위치로 이동해요. 몇 가지 예를 볼게요:

"cats and dogs" =~ /cat|dog|bird/;  # matches "cat"
"cats and dogs" =~ /dog|cat|bird/;  # matches "cat"

두 번째 정규식에서 dog이 첫 번째 대안이지만, cat이 문자열에서 더 일찍 매치될 수 있기 때문이에요.

"cats"          =~ /c|ca|cat|cats/; # matches "c"
"cats"          =~ /cats|cat|ca|c/; # matches "cats"

주어진 문자 위치에서 정규식 매치를 성공시키는 첫 번째 대안이 매치돼요. 여기서는 모든 대안이 첫 번째 문자열 위치에서 매치되므로, 첫 번째가 매치돼요.

그룹화와 계층적 매치 (Grouping things and hierarchical matching)

그룹화 메타문자 ()는 정규식의 일부를 하나의 단위로 취급하게 해 줘요. 정규식의 일부는 괄호로 감싸 그룹화돼요. 정규식 house(cat|keeper)house 다음에 cat 또는 keeper가 오는 걸 매치하라는 뜻이에요. 몇 가지 예를 더 볼게요:

/(a|b)b/;    # matches 'ab' or 'bb'
/(^a|b)c/;   # matches 'ac' at start of string or 'bc' anywhere

/house(cat|)/;  # matches either 'housecat' or 'house'
/house(cat(s|)|)/;  # matches either 'housecats' or 'housecat' or
                    # 'house'.  Note groups can be nested.

"20" =~ /(19|20|)\d\d/;  # matches the null alternative '()\d\d',
                         # because '20\d\d' can't match

매치 추출하기 (Extracting matches)

그룹화 메타문자 ()는 매치된 문자열의 일부를 추출할 수도 있게 해 줘요. 각 그룹에 대해 안에서 매치된 부분은 특수 변수 $1, $2 등으로 들어가요. 보통 변수처럼 쓸 수 있어요:

# extract hours, minutes, seconds
$time =~ /(\d\d):(\d\d):(\d\d)/;  # match hh:mm:ss format
$hours = $1;
$minutes = $2;
$seconds = $3;

리스트 문맥에서 그룹이 있는 매치 /regex/는 매치된 값의 목록 ($1,$2,...)을 돌려줘요. 그래서 위를 이렇게 다시 쓸 수 있어요:

($hours, $minutes, $second) = ($time =~ /(\d\d):(\d\d):(\d\d)/);

정규식의 그룹이 중첩돼 있으면 $1은 가장 왼쪽의 여는 괄호를 가진 그룹에, $2는 다음 여는 괄호에 들어가요. 예를 들어, 복잡한 정규식과 그 아래에 표시된 매치 변수를 볼게요:

/(ab(cd|ef)((gi)|j))/;
 1  2      34

매치 변수 $1, $2, ...와 연관된 것이 백레퍼런스(backreferences) \g1, \g2, ...예요. 백레퍼런스는 정규식 안에서 쓸 수 있는 매치 변수예요:

/(\w\w\w)\s\g1/; # find sequences like 'the the' in string

$1, $2, ...는 정규식 밖에서만, \g1, \g2, ...는 정규식 안에서만 써야 해요.

반복 매치하기 (Matching repetitions)

수량자 메타문자 ?, *, +, {}는 정규식의 일부가 매치로 간주되는 반복 횟수를 정하게 해 줘요. 수량자는 지정하려는 문자, 문자 클래스, 그룹 바로 뒤에 놓아요. 다음 의미를 갖고 있어요:

  • a? = 'a'를 1번 또는 0번 매치
  • a* = 'a'를 0번 이상, 즉 몇 번이든 매치
  • a+ = 'a'를 1번 이상, 즉 최소한 한 번 매치
  • a{n,m} = 최소 n번, 많아야 m번 매치
  • a{n,} = 최소 n번 이상 매치
  • a{,n} = n번 이하로 매치 (v5.34에서 추가)
  • a{n} = 정확히 n번 매치

몇 가지 예를 볼게요:

/[a-z]+\s+\d*/;  # match a lowercase word, at least some space, and
                 # any number of digits
/(\w+)\s+\g1/;    # match doubled words of arbitrary length
$year =~ /^\d{2,4}$/;  # make sure year is at least 2 but not more
                       # than 4 digits
$year =~ /^\d{ 4 }$|^\d{2}$/; # better match; throw out 3 digit dates

이 수량자들은 정규식이 매치되는 걸 여전히 허용하면서 문자열을 최대한 많이 매치하려고 해요. 그래서:

$x = 'the cat in the hat';
$x =~ /^(.*)(at)(.*)$/; # matches,
                        # $1 = 'the cat in the h'
                        # $2 = 'at'
                        # $3 = ''   (0 matches)

첫 번째 수량자 .*는 정규식이 매치되게 하면서 문자열을 최대한 잡아요. 두 번째 수량자 .*는 남은 문자열이 없으므로 0번 매치돼요.

더 매치하기 (More matching)

매치 연산자에 대해 알면 좋을 것들이 몇 가지 더 있어요. 전역 수정자 /g는 매치 연산자가 문자열 안에서 가능한 한 여러 번 매치하게 해 줘요. 스칼라 문맥에서 문자열에 대한 연속 매치는 /g가 매치에서 매치로 점프하면서 문자열 안의 위치를 추적해요. 위치는 pos() 함수로 얻거나 설정할 수 있어요. 예를 들어:

$x = "cat dog house"; # 3 words
while ($x =~ /(\w+)/g) {
    print "Word is $1, ends at position ", pos $x, "\n";
}

이것은 다음과 같이 출력해요.

Word is cat, ends at position 3
Word is dog, ends at position 7
Word is house, ends at position 13

실패한 매치나 대상 문자열의 변경은 위치를 재설정해요. 매치 실패 후 위치가 재설정되는 걸 원하지 않으면 /c를 추가해요. 예: /regex/gc.

리스트 문맥에서 /g는 매치된 그룹의 목록을 돌려주고, 그룹이 없으면 정규식 전체의 매치 목록을 돌려줘요. 그래서:

@words = ($x =~ /(\w+)/g);  # matches,
                            # $word[0] = 'cat'
                            # $word[1] = 'dog'
                            # $word[2] = 'house'

검색과 치환 (Search and replace)

검색과 치환은 s/regex/replacement/modifiers로 수행해요. replacement는 Perl 이중 인용 문자열로, 문자열에서 regex가 매치한 것을 대체해요. =~ 연산자는 여기서도 문자열을 s///에 연결하는 데 쓰여요. $_ 대상으로 매치한다면 $_ =~는 생략할 수 있어요. 매치가 있으면 s///는 수행된 치환 횟수를 돌려주고, 그렇지 않으면 false를 돌려줘요. 몇 가지 예를 볼게요:

$x = "Time to feed the cat!";
$x =~ s/cat/hacker/;   # $x contains "Time to feed the hacker!"
$y = "'quoted words'";
$y =~ s/^'(.*)'$/$1/;  # strip single quotes,
                       # $y contains "quoted words"

s/// 연산자로 매치 변수 $1, $2 등이 치환 표현식에서 즉시 사용 가능해요. 전역 수정자와 함께 쓰면 s///g는 문자열 안의 정규식의 모든 출현을 검색·치환해요:

$x = "I batted 4 for 4";
$x =~ s/4/four/;   # $x contains "I batted four for 4"
$x = "I batted 4 for 4";
$x =~ s/4/four/g;  # $x contains "I batted four for four"

비파괴 수정자 s///r$_(또는 =~로 치환이 묶인 다른 변수)를 수정하는 대신 치환 결과를 돌려줘요:

$x = "I like dogs.";
$y = $x =~ s/dogs/cats/r;
print "$x $y\n"; # prints "I like dogs. I like cats."

$x = "Cats are great.";
print $x =~ s/Cats/Dogs/r =~ s/Dogs/Frogs/r =~
    s/Frogs/Hedgehogs/r, "\n";
# prints "Hedgehogs are great."

@foo = map { s/[a-z]/X/r } qw(a b c 1 2 3);
# @foo is now qw(X X X 1 2 3)

평가 수정자 s///e는 치환 문자열 주위에 eval{...}을 감싸고, 평가된 결과를 매치된 하위 문자열에 치환해요. 몇 가지 예를 볼게요:

# reverse all the words in a string
$x = "the cat in the hat";
$x =~ s/(\w+)/reverse $1/ge;   # $x contains "eht tac ni eht tah"

# convert percentage to decimal
$x = "A 39% hit rate";
$x =~ s!(\d+)%!$1/100!e;       # $x contains "A 0.39 hit rate"

마지막 예시는 s///s!!!, s{}{} 같은 다른 구분자, 심지어 s{}//도 쓸 수 있음을 보여줘요. 작은따옴표를 쓰면 s''', 정규식과 치환은 단일 인용 문자열로 취급돼요.

split 연산자 (The split operator)

split /regex/, stringstring을 하위 문자열 목록으로 나누고 그 목록을 돌려줘요. 정규식은 string이 나뉘는 기준이 되는 문자 시퀀스를 정해요. 예를 들어 문자열을 단어로 나누려면:

$x = "Calvin and Hobbes";
@word = split /\s+/, $x;  # $word[0] = 'Calvin'
                          # $word[1] = 'and'
                          # $word[2] = 'Hobbes'

쉼표로 구분된 숫자 목록을 추출하려면:

$x = "1.618,2.718,   3.142";
@const = split /,\s*/, $x;  # $const[0] = '1.618'
                            # $const[1] = '2.718'
                            # $const[2] = '3.142'

빈 정규식 //를 쓰면 문자열이 개별 문자로 나뉘어요. 정규식에 그룹이 있으면 생성된 목록에 그룹에서 매치된 하위 문자열도 포함돼요:

$x = "/usr/bin";
@parts = split m!(/)!, $x;  # $parts[0] = ''
                            # $parts[1] = '/'
                            # $parts[2] = 'usr'
                            # $parts[3] = '/'
                            # $parts[4] = 'bin'

$x의 첫 문자가 정규식과 매치됐으므로, split은 목록의 앞에 빈 초기 요소를 붙였어요.

use re 'strict'

v5.22에 새로 추가된 것으로, 정규식 패턴을 컴파일할 때 그보다 더 엄격한 규칙을 적용해요. 합법적이지만 의도한 바가 아닐 수 있는 것들을 찾아낼 수 있어요.

'strict' in re를 보세요.

더 알아보기 (SEE ALSO)

이건 빠른 시작 가이드일 뿐이에요. 정규식에 대한 더 깊이 있는 튜토리얼은 perlretut, 레퍼런스 페이지는 perlre를 보세요.

더 알아보기