kpw2012 조금 깊이 들여다보는 정규표현식
DESCRIPTION
Korean Perl Workshop 2012 에서 발표한 내용입니다.TRANSCRIPT
조금 깊이 들여다보는
qr/정규표현식/
Korean Perl Workshop 2012
2012. 10. 20.
박근영
@gypark [email protected]
http://gypark.pe.kr/wiki/Perl
이 문서의 내용과 코드는 주로…
• perlretut (regular expression tutorial) – 번역: http://gypark.pe.kr/wiki/Perl/정규표현식
• perlre
• 손에 잡히는 정규표현식
– 벤 포터, 김경수 번역, 인사이트
• 한 권으로 끝내는 정규표현식
– 잰 고이바에르츠 외, 김지원 번역, 한빛미디어
• WWW …등에서 발췌
Korean Perl Workshop 2012 3
• 일치해야 될 문자열에 일치하는 정규식을 만드는 것은 비교적 쉽다
• 일치하지 말아야 할 문자열에 일치하지 않는 정규식을 만드는 것이 어렵다
Korean Perl Workshop 2012 6
• 일치해야 될 문자열에 일치하는 정규식을 만드는 것은 비교적 쉽다
• 일치하지 말아야 할 문자열에 일치하지 않는 정규식을 만드는 것이 어렵다
– 경우의 수가 무한함
– 테스트하기 힘듦
– 상황에 따라 적절한 타협이 필요
Korean Perl Workshop 2012 7
• 간단한 예 "cats and dogs" =~ / dog | cat | bird /x;
• 후보들의 순서에 무관하게, 앞에서부터 일치
Korean Perl Workshop 2012 10
• 수량자와 같이 사용할 때 '"10" cats and "20" dogs' =~ m/".+"/;
Greedy quantifier
Korean Perl Workshop 2012 12
• 수량자와 같이 사용할 때 '"10" cats and "20" dogs' =~ m/".+?"/; Non-greedy quantifier로 교체
Korean Perl Workshop 2012 13
• 수량자와 같이 사용할 때 '"10" cats and "20" dogs' =~ m/".+?" dogs/;
이것을 기대하지만…
Korean Perl Workshop 2012 16
우리가 원한 것
• 수량자와 같이 사용할 때 '"10" cats and "20" dogs' =~ m/".+?" dogs/;
???
Korean Perl Workshop 2012 17
우리가 원한 것 실제로 일치하는 부분
• 수량자와 같이 사용할 때 '"10" cats and "20" dogs' =~ m/".+?" dogs/;
Non-greedy 수량자는 정규식이 일치할 때까지
문자열을 소모하며 일치부를 확장지킨다.
Korean Perl Workshop 2012 18
실제로 일치하는 부분
• 수량자와 같이 사용할 때 '"10" cats and "20" dogs' =~ m/"[^"]+" dogs/; /./ 대신에 따옴표를 제외한 문자들만 일치하도록 수정
Korean Perl Workshop 2012 19
• 경계가 한 글자가 아니면…… "<b>10</b> cats and <b>20</b> dogs" =~ m'<b>((?!</b>).)+</b> dogs'; 부정 전방탐색
Negative Lookahead
Korean Perl Workshop 2012 22
같은 위치에서는 왼쪽 후보부터
"Her name is Janet." =~ / Jane | Janet /x; 이 문자열은…
Korean Perl Workshop 2012 23
* 코드 출처: perlretut
"Her name is Janet." =~ / Jane | Janet /x; 이 문자열은…
절대로 'Janet'에 일치하지 않을 것임
Korean Perl Workshop 2012 24
"Her name is Janet." =~ / Janet | Jane /x / \bJane\b | \bJanet\b /x / \bJanet?\b /x 정규식 리터럴에서는 이런 실수가 적지만, 패턴이 변수에 들어가 있으면 눈에 띄지 않음
Korean Perl Workshop 2012 25
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; "TGA" codon의 위치를 검색
Korean Perl Workshop 2012 27
* 코드 출처: perlretut
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /TGA/g ) { print "TGA at ", pos $dna, "\n"; } 1차 시도
Korean Perl Workshop 2012 28
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /TGA/g ) { print "TGA at ", pos $dna, "\n"; } TGA at 8 TGA at 18 TGA at 23
Korean Perl Workshop 2012 29
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /(\w\w\w)*?TGA/g ) { print "TGA at ", pos $dna, "\n"; } 2차 시도
세 글자씩 끊어가며 검색
Korean Perl Workshop 2012 30
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /(\w\w\w)*?TGA/g ) { print "TGA at ", pos $dna, "\n"; } TGA at 18 TGA at 23 ???
… 어째서?
Korean Perl Workshop 2012 31
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /(\w\w\w)*?TGA/g ) { print "TGA at ", pos $dna, "\n"; } TGA at 18 마지막으로 성공한 후
Korean Perl Workshop 2012 32
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /(\w\w\w)*?TGA/g ) { print "TGA at ", pos $dna, "\n"; } TGA at 18 마지막으로 성공한 후 그 직후부터 일치를 시도하여 실패하고
Korean Perl Workshop 2012 33
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /(\w\w\w)*?TGA/g ) { print "TGA at ", pos $dna, "\n"; } TGA at 18 마지막으로 성공한 후 그 직후부터 일치를 시도하여 실패하고 그 다음 글자부터 재시도하여 실패하고
Korean Perl Workshop 2012 34
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /(\w\w\w)*?TGA/g ) { print "TGA at ", pos $dna, "\n"; } TGA at 18 마지막으로 성공한 후 그 직후부터 일치를 시도하여 실패하고 그 다음 글자부터 재시도하여 실패하고 TGA at 23 그 다음 글자부터 재시도하여 성공
Korean Perl Workshop 2012 35
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /\G(\w\w\w)*?TGA/g ) { print "TGA at ", pos $dna, "\n"; }
3차 시도
/\G/ - /g flag를 사용하여 일치된 마지막 지점
Korean Perl Workshop 2012 36
# "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; while ( $dna =~ /\G(\w\w\w)*?TGA/g ) { print "TGA at ", pos $dna, "\n"; } TGA at 18 성공 앵커에 일치해야 하기 때문에, 그 다음 글자부터 진행할 수 없다
Korean Perl Workshop 2012 37
• 완전한 단어 “cat”에 일치 /\bcat\b/ • 어딘가에 포함되어 있는 “cat”?
• “cat”을 제외한 모든 단어?
• “cat”이 포함되지 않은 단어?
Korean Perl Workshop 2012 42
• 어딘가에 포함되어 있는 “cat”
bobcat, category, staccato, 123cat456 …
Korean Perl Workshop 2012 43
* 코드 출처: 한 권으로 끝내는 정규표현식, 67
• 어딘가에 포함되어 있는 “cat”
bobcat, category, staccato, 123cat456 …
• 오답 /\Bcat\B/
Korean Perl Workshop 2012 44
• 어딘가에 포함되어 있는 “cat”
bobcat, category, staccato, 123cat456 …
• 오답 /\Bcat\B/
• 정답 /\Bcat|cat\B/
Korean Perl Workshop 2012 45
• “cat”을 제외한 모든 단어 snake, bat, dog, bobcat, …
Korean Perl Workshop 2012 46
* 코드 출처: 한 권으로 끝내는 정규표현식, 372
• “cat”을 제외한 모든 단어 snake, bat, dog, bobcat, …
• 흔히 보이는 오답
/\b[^cat]+\b/ – [^…]를 단어를 배제하는 용도로 쓰면 안 됨
Korean Perl Workshop 2012 47
• “cat”을 제외한 모든 단어 snake, bat, dog, bobcat, …
• 흔히 보이는 오답
/\b[^cat]+\b/ – [^…]를 단어를 배제하는 용도로 쓰면 안 됨
• 마찬가지
/\b[^c][^a][^t]\b/
Korean Perl Workshop 2012 48
• “cat”이 포함되어 있지 않은 모든 단어 snake, bat, dog, bobcat, …
/\b(?:(?!cat)\w)+\b/
Korean Perl Workshop 2012 50
* 코드 출처: 한 권으로 끝내는 정규표현식
“일치하지 않음” vs “아닌 것에 일치함”
$str = 'I paid $30 for 100 apples';
Korean Perl Workshop 2012 51
* 코드 출처: 손에 잡히는 정규표현식, 93
$str = 'I paid $30 for 100 apples'; # '$'는 제외하고 숫자에만 일치 $str =~ /(?<=\$)\d+/; # 후방탐색
Korean Perl Workshop 2012 53
my $str = 'I paid $30 for 100 apples'; # 가격이 아닌 수량에만 일치?? $str =~ /(?<!\$)\d+/; # 부정형 후방탐색
Korean Perl Workshop 2012 54
my $str = 'I paid $30 for 100 apples'; # 가격이 아닌 수량에만 일치?? $str =~ /(?<!\$)\d+/; # 부정형 후방탐색 # 실패
Korean Perl Workshop 2012 55
my $str = 'I paid $30 for 100 apples'; # 가격이 아닌 수량에만 일치?? $str =~ /(?<!\$)\d+/; # 부정형 후방탐색 # 실패 $str =~ /\b(?<!\$)\d+/; # 앵커가 필요함
Korean Perl Workshop 2012 56
• 전체 문자열이 4~6 글자인 것만 /^\d{1}(?:[a-z]){3,5}$/ | /^\d{2}(?:[a-z]){2,4}$/ | /^\d{3}(?:[a-z]){1,3}$/ | /^\d{4}(?:[a-z]){1,2}$/ | /^\d{5}(?:[a-z]){1}$/ … 무리입니다
Korean Perl Workshop 2012 59
• 정규식 안의 Perl 코드 /^(\d++) (??{ my $d = length($1); # 숫자열의 길이에 따라 my $min = 4-$d; # 문자열의 최소 최대 길이를 $min = 1 if $min < 1;# 결정하고 my $max = 6-$d; "[a-z]{$min,$max}"; # 정규식을 구성 }) $/x; … 무리입니다
Korean Perl Workshop 2012 60
전방탐색과 조건식 결합
올바른 우편번호 찾기 11111 (o) 22222 (o) 33333- (x) 44444-4444 (o)
Korean Perl Workshop 2012 63
* 코드 출처: 손에 잡히는 정규표현식, 102
올바른 우편번호 찾기 11111 (o) 22222 (o) 33333- (x) 44444-4444 (o) /\d{5}(-\d{4})?/ 세번째 문자열을 걸러내지 못함
Korean Perl Workshop 2012 64
올바른 우편번호 찾기 11111 (o) 22222 (o) 33333- (x) 44444-4444 (o) /\d{5}(?(?=-)-\d{4})/ 전방탐색이 성공하면 그 뒤의 패턴도 검사
Korean Perl Workshop 2012 65
Case shifting
\Q \L \l \U \u \E
– Perl에서는 큰따옴표 문자열 문법의 일부
– 정규표현식 문법이 아님
Korean Perl Workshop 2012 66
$str1 = "\uhello"; # $str1 = 'Hello' 'Hello' =~ /$str1/; # 일치됨 $str2 = '\uhello'; # $str2 = '\uhello' 'Hello' =~ /$str2/; # 런타임 에러
“Unrecognized escape \u …”
• 사용자 입력을 받은 경우도 마찬가지
Korean Perl Workshop 2012 68
• 쌍따옴표 문자열 내에 적용되는 순서 – 변수 치환 – 이스케이프, 논리 캐릭터 – Case shift
$ perl -e 'print "\QP*rl\n"' P\*rl\ <- (\n 있음)
Korean Perl Workshop 2012 69
• 묘하다 "\n" =~ qr/\n/ # 일치 "\n" =~ qr/\Q\n/ # 불일치 (qr/\\n/, '\n'에 일치) "\n" =~ qr/\U\n/ # 불일치! (qr/\N/, Perl5.8 이하 에러) "\n" =~ qr/\l\n/ # 일치! "\n" =~ qr/\L\n/ # 일치!
Korean Perl Workshop 2012 71
• 심지어 "\Q\n" =~ qr/\Q\n/ # 불일치 "\U\n" =~ qr/\U\n/ # 불일치 "\L\n" =~ qr/\L\n/ # 일치
Korean Perl Workshop 2012 72
$str = "abc - 123"; $str =~ / \S+ # 단어 - # 대시 \d+ # 숫자 /x;
이것은 일치하지 않는다!
Korean Perl Workshop 2012 75
$str = "abc - 123"; $str =~ / \S+\ # 단어,공백 -\ # 대시,공백 \d+ # 숫자 /x;
스페이스는 /[ ]/ 또는 /\ /로 바꿔야 되는 걸 잊지 말자
Korean Perl Workshop 2012 76
Perl 5.10 ~ Perl 5.14 에서 도입된 문법들 \N 메타캐릭터
Relative Backreferences \g{3} \g{-2}
Named Backreferences (?<year>) \g<year>
Branch Reset
PREMATCH, MATCH, POSTMATCH
Possessive Quantifier <[^>]++>, 백트래킹하지 않는 부분식
/r flag
Named Subpattern
Recursive Pattern
Korean Perl Workshop 2012 77
\N metacharacter
/./ 임의의 캐릭터 하나
– /s 옵션이 있을 때는 뉴라인"\n"에 일치
– 없을 때는 뉴라인에 일치하지 않음
/\N/ /s 옵션에 무관하게 항상 뉴라인을 제외한
임의의 캐릭터 하나
78 Korean Perl Workshop 2012
Relative backreferences
# 오동작 my $a99a = '([a-z])(\d)\g2\g1'; # a11a, g22g, ... my $line = 'code=e99e'; if ( $line =~ /^(\w+)=$a99a$/ ) { print "$1 is valid\n"; }
앞에 괄호가 추가되면서 번호가 안 맞는다
Korean Perl Workshop 2012 79
* 코드 출처: perlretut
# 오동작 my $a99a = '([a-z])(\d)\g2\g1'; # a11a, g22g, ... my $line = 'code=e99e'; if ( $line =~ /^(\w+)=$a99a$/ ) { print "$1 is valid\n"; } my $a99a = '([a-z])(\d)\g{-1}\g{-2}' 현재 위치 기준.
Korean Perl Workshop 2012 80
Named backreferences
• 매치되는 그룹에 이름을 붙임
• 앞의 예 다시 my $a99a = '(?<char>[a-z])(?<digit>\d)\g{digit}\g{char}';
Korean Perl Workshop 2012 81
• 그룹의 순서가 서로 다른 패턴을 일괄처리 $fmt1 = '(?<y>\d\d\d\d)-(?<m>\d\d)-(?<d>\d\d)'; $fmt2 = '(?<m>\d\d)/(?<d>\d\d)/(?<y>\d\d\d\d)'; $fmt3 = '(?<d>\d\d)\.(?<m>\d\d)\.(?<y>\d\d\d\d)'; for my $d ( qw( 2006-10-21 15.01.2007 10/31/2005 ) ) { if ( $d =~ m{$fmt1|$fmt2|$fmt3} ){ print "day=$+{d} month=$+{m} year=$+{y}\n"; } } 정규식 외부에서는 %+ 해시를 써서 접근
Korean Perl Workshop 2012 82
* 코드 출처: perlretut
Branch Reset
$time = '2305'; if ( $time =~ /(\d\d|\d):(\d\d)|(\d\d)(\d\d)/ ) { print "hour = $1 minute = $2\n"; # ?? print "hour = $3 minute = $4\n"; # ?? }
몇 번째 그룹에 캡처가 될 지 모름
defined 검사 필요
Korean Perl Workshop 2012 83
* 코드 출처: perlretut
$time = '2305 GMT'; if ( $time =~ /(?|(\d\d|\d):(\d\d)|(\d\d)(\d\d))\s+([A-Z]{3})/ ) { print "hour = $1 minute = $2 zone = $3\n"; } (?| 후보1 | 후보2 | 후보3 | … )
Korean Perl Workshop 2012 84
$time = '2305 GMT'; if ( $time =~ /(?|(\d\d|\d):(\d\d)|(\d\d)(\d\d))\s+([A-Z]{3})/ ) { print "hour = $1 minute = $2 zone = $3\n"; } 각 후보들마다 그룹 번호가 동일하게 시작
Korean Perl Workshop 2012 85
1 1 2 2 3
• 각 후보 패턴마다 그룹의 개수가 다를 경우, 리셋 그룹 이후 나타나는 그룹은 가장 높은 번호의 다음 번호부터 할당
• 주의: Perl 5.14 전 버전에는 버그 • 의도한 대로 캡처되지 않음
• 캡처 그룹이 가장 많은 후보 패턴을 제일 먼저 적어주거나
• 빈 그룹 ()을 넣어 모든 후보의 그룹 개수를 동일하게 만들 것
Korean Perl Workshop 2012 86
PREMATCH, MATCH, POSTMATCH
$` $& $'
• 정규식 연산에 성능 저하를 가져옴
• 코드 내에 한 번이라도 사용되면, 모든 정규식 연산마다 저 변수들을 세팅하는 과정을 거침
Korean Perl Workshop 2012 87
* 코드 출처: www.effectiveperlprogramming.com
'My cat is Buster Bean' =~ m/Buster/; print "I matched $&\n"; # 여기서 매치 변수를 사용해버렸기 때문에 while (<>) { next unless /Bean/; # 매치할 때마다 매번 오버헤드가 발생 }
Korean Perl Workshop 2012 88
대신 다음 변수들을 사용
• ${^PREMATCH} $` 역할 • ${^MATCH} $& • ${^POSTMATCH} $' • /p flag 명시
• /p flag가 있는 연산에서만 오버헤드 발생
Korean Perl Workshop 2012 90
'My cat is Buster Bean' =~ m/\s\w+\sBean/p; # /p 옵션 사용 say "I matched ${^MATCH}"; while (<>) { next unless /Bean/; # 오버헤드 없음 }
Korean Perl Workshop 2012 91
5.10 전 버전 - 캡처 그룹과 매칭 변수로 대체
• /pattern/ + $` 대신 /(.*?)pattern/ + $1 • /pattern/ + $& 대신 /(pattern)/ + $1 • /pattern/ + $' 대신 /pattern(.*)/ + $+
• use English qw(-no_match_vars)
• 로드한 외부 모듈에서 사용해 버리면 역시 소용 없음
Korean Perl Workshop 2012 92
'abcda' =~ /[a-d]+Z/; # 일치하지 않는 문자열 • [a-d]+ 는 일단 'abcda' 에 일치 • Z가 일치될 문자열이 남아 있지 않으므로
Korean Perl Workshop 2012 99
'abcda' =~ /[a-d]+Z/; # 일치하지 않는 문자열 • [a-d]+ 는 일단 'abcda' 에 일치 • Z가 일치될 문자열이 남아 있지 않으므로
– 'a'를 내어놓고 재시도
Korean Perl Workshop 2012 100
'abcda' =~ /[a-d]+Z/; # 일치하지 않는 문자열 • [a-d]+ 는 일단 'abcda' 에 일치 • Z가 일치될 문자열이 남아 있지 않으므로
– 'a'를 내어놓고 재시도 – 'd'를 내어놓고 재시도 – …
Korean Perl Workshop 2012 101
'abcda' =~ /[a-d]+Z/; # 일치하지 않는 문자열 • [a-d]+ 는 일단 'abcda' 에 일치 • Z가 일치될 문자열이 남아 있지 않으므로
– 'a'를 내어놓고 재시도 – 'd'를 내어놓고 재시도 – …
• 'bcda'를 대상으로 처음부터 다시 – …
Korean Perl Workshop 2012 102
'abcda' =~ /[a-d]++Z/; • [a-d]++ 는 'abcda'에 일치
• Z는 일치에 실패
• 백트래킹하지 않고 곧바로 실패 판정
Korean Perl Workshop 2012 109
'abcda' =~ /[a-d]++Z/; • [a-d]++ 는 'abcda'에 일치
• Z는 일치에 실패
• 백트래킹하지 않고 곧바로 실패 판정
• 단, 여기서 끝은 아님 – 문자열의 다음 위치에서 재시도
Korean Perl Workshop 2012 110
• 주의 'aaaaa' =~ /a++a/; 이것은 일치하지 않는다
• a++가 'aaaaa'에 일치해버리기 때문에 뒤에 a가 일치할 문자열이 남지 않음
• 아무 때나 Greedy 수량자 대신 사용할 수는 없음
Korean Perl Workshop 2012 112
• 독점적 수량자를 써도 괜찮은지 판단 'aaaaa' =~ / a++ a /x; # No 'aaaaa' =~ / a++ b /x; # Yes
Korean Perl Workshop 2012 113
• 독점적 수량자를 써도 괜찮은지 판단 'aaaaa' =~ / a++ a /x; # No 'aaaaa' =~ / a++ b /x; # Yes
• 수량자가 적용되는 부분의 패턴이, 뒤에 오는 패턴과
일치하지 않으면 괜찮음
Korean Perl Workshop 2012 114
백트래킹을 하지 않는 부분식
• 독점적 수량자의 일반형
/[a-d]++Z/ /(?>[a-d]+)Z/; • 주의 /(?>[a-d])+Z/; # 다르다
Korean Perl Workshop 2012 115
/r flag in substitution
$str =~ s/pattern/replacement/ • $str 의 내용을 변경
• 치환 횟수를 반환
$str =~ s/pattern/replacement/r • $str 을 변경하지 않음
• 복제본을 만들어서 복제본을 변경
• 변경된 복제본 문자열을 반환
Korean Perl Workshop 2012 116
• 기존 코드 my $old = 'hello'; (my $new = $old) =~ s/h/H/; # 괄호 필수!
• 새 코드 my $old = 'hello'; my $new = $old =~ s/h/H/r;
Korean Perl Workshop 2012 117
• 좀 더 유용한 예 my @in = qw( Bu1s5ter Mi6mi Roscoe Gin98ger El123la ); # @in의 각 원소에서 숫자를 제거하여 # @out 배열에 저장하고 싶다 my @out = map { my $s = $_; $s =~ s/\d+//g; $s } @in; # 원소를 일일이 복사해야 함
Korean Perl Workshop 2012 119
* 코드 출처: www.effectiveperlprogramming.com
• 좀 더 유용한 예 my @in = qw( Bu1s5ter Mi6mi Roscoe Gin98ger El123la ); # @in의 각 원소에서 숫자를 제거하여 # @out 배열에 저장하고 싶다 my @out = map { my $s = $_; $s =~ s/\d+//g; $s } @in; # 원소를 일일이 복사해야 함 my @out = map { s/\d+//gr } @in;
Korean Perl Workshop 2012 120
Named Subpattern
"수"에 일치되는 정규식의 예 (3, -7, .25, 12.34, -5.67e10 등)
/^ [+-]?\ * # 먼저, 부호와 공백에 매치 ( # 그 다음 정수 또는 소수의 유효숫자부에 매치 \d+ # a로 시작하면서... ( \.\d* # a.b 또는 a. 형태의 유효숫자 )? # ?는 a 형태의 정수가 오는 경우를 고려한 것 |\.\d+ # .b 형태의 유효숫자 ) ([eE][+-]?\d+)? # 추가적으로 올 수 있는 지수부에 매치 $/x;
Korean Perl Workshop 2012 121
* 코드 출처: perlretut
패턴을 정의하고 이름을 붙인 후 재사용 /^ (?&osg)\ * ( (?&int)(?&dec)? | (?&dec) ) (?: [eE](?&osg)(?&int) )? $ (?(DEFINE) (?<osg>[-+]?) # 생략 가능한 부호 (?<int>\d++) # 정수 (?<dec>\.(?&int)) # 소수부 )/x;
Korean Perl Workshop 2012 122
• 부등호로 둘러싸인 문자열에 일치 /<.*?>/ 또는 /<[^<>]>/
• 중첩된 형태에도 일치 "< … < … > … < … < … > > … >"
/????/
Korean Perl Workshop 2012 124
• 정규식은 정규문법으로 생성되는 문자열을 표현
• 문맥 무관 문법으로 생성되는 문자열을 표현하는 것에는 한계가 있음 – Balanced text
– Program code
• Text::Balanced
• Regexp::Common
• Parser용 모듈들
Korean Perl Workshop 2012 125
• 재귀 패턴을 사용한 정규식
중첩되지 않는 패턴 <[^<>]++> 중첩된 패턴 = < > 안에 부등호를 제외한 것들 또는 패턴 이 반복 Korean Perl Workshop 2012 127
/ < > /x; # 부등호 안에 / < [^<>]++ > /x; # 부등호 외의 것들 / (< [^<>]++ | (?1) >) /x; # 또는 패턴이
Korean Perl Workshop 2012 130
/ < > /x; # 부등호 안에 / < [^<>]++ > /x; # 부등호 외의 것들 / (< [^<>]++ | (?1) >) /x; # 또는 패턴이 / (< (?: [^<>]++ | (?1) )* >) /x; # 반복
Korean Perl Workshop 2012 131
my $string =<<"HERE"; I have some <brackets in <nested brackets> > and <another group <nested once <nested twice> > > and that's it. HERE my @groups = $string =~ m/ < (?: [^<>]++ | (?1) )* > )/xg;
Korean Perl Workshop 2012 132
* 코드 출처: perlfaq6
• 세 단어가 순서 무관하게 가까이 붙어 있는 경우를 검색
… word1 … word3 … … … word2 …
(8단어 이내에 세 단어 존재)
Korean Perl Workshop 2012 138
* 코드 출처: 한 권으로 끝내는 정규표현식
… word1 … word3 … … … word2 …
/ \b (?: (?>word1()|word2()|word3()|(?>\1|\2|\3)\w+)\b\W*? ){3,8} \1\2\3 /x
Korean Perl Workshop 2012 139
… word1 … word3 … … … word2 …
/ \b (?: (?>word1()|word2()|word3()|(?>\1|\2|\3)\w+)\b\W*? ){3,8} \1\2\3 /x 일단 word1 이 일치하면 ()가 같이 일치하고
\1은 그때부터 일치
Korean Perl Workshop 2012 140
… word1 … word3 … … … word2 …
/ \b (?: (?>word1()|word2()|word3()|(?>\1|\2|\3)\w+)\b\W*? ){3,8} \1\2\3 /x 일단 word1 이 일치하면 ()가 같이 일치하고
\1은 그때부터 일치
Korean Perl Workshop 2012 141
… word1 … word3 … … … word2 …
/ \b (?: (?>word1()|word2()|word3()|(?>\1|\2|\3)\w+)\b\W*? ){3,8} \1\2\3 /x 일단 word1 이 일치하면 ()가 같이 일치하고
\1은 그때부터 일치
Korean Perl Workshop 2012 142
… word1 … word3 … … … word2 …
/ \b (?: (?>word1()|word2()|word3()|(?>\1|\2|\3)\w+)\b\W*? ){3,8} \1\2\3 /x 일단 word1 이 일치하면 ()가 같이 일치하고
\1은 그때부터 일치
Korean Perl Workshop 2012 143
이제부터는 아무 단어나 일치 가능
… word1 … word3 … … … word2 …
/ \b (?: (?>word1()|word2()|word3()|(?>\1|\2|\3)\w+)\b\W*? ){3,8} \1\2\3 /x 일단 word1 이 일치하면 ()가 같이 일치하고
\1은 그때부터 일치
Korean Perl Workshop 2012 144
최대 8번까지 단어들을 일치시킨 후
… word1 … word3 … … … word2 …
/ \b (?: (?>word1()|word2()|word3()|(?>\1|\2|\3)\w+)\b\W*? ){3,8} \1\2\3 /x 일단 word1 이 일치하면 ()가 같이 일치하고
\1은 그때부터 일치
Korean Perl Workshop 2012 145
전체 패턴이 일치하기 위해서는 여기를 통과해야 함 즉 word1, word2, word3 이 한번씩 일치되었어야 함
정규식으로만 해결하려 하지 말자
my $str = '1 <b>2</b> 3 4 <b>5 6 7</b> 8'; # 볼드체로 표시되는 숫자들만 추출하고 싶다 # 2, 5, 6, 7 # "5 6 7"을 추출하는 게 아니라서 까다롭다
Korean Perl Workshop 2012 147
my $str = '1 <b>2</b> 3 4 <b>5 6 7</b> 8'; # 볼드체로 표시되는 숫자들만 추출하고 싶다 # 2, 5, 6, 7 my @numbers = ( $str =~ m|\d+(?=(?:(?!<b>).)*</b>)|g );
Korean Perl Workshop 2012 148
my $str = '1 <b>2</b> 3 4 <b>5 6 7</b> 8'; # 볼드체로 표시되는 숫자들만 추출하고 싶다 # 2, 5, 6, 7 while ( $str =~ m|<b>(.*?)</b>|gs ) { push @numbers, ($& =~ /\d+/g); } 쉽고, 백트래킹 등을 생각하면 효율도 이게 낫다
Korean Perl Workshop 2012 149
grep '\d\{4\}\s\+' file1 file2 grep -P '\d{4}\s+' file1 file2 Perl 정규식을 사용 가능
Korean Perl Workshop 2012 151
vim
/\(a\|b\)\+
• vim의 패턴 해석 방식 4가지 – \V : 백슬래시만 특수하게 해석 – \M : set nomagic – \m : set magic – \v : "very magic"
• Perl 정규식과 비슷해짐 /\v(a|b)+
Korean Perl Workshop 2012 152
• vim의 Lookaround – \@= \@! Lookahead – \@<= \@<! Lookbehind
• 예 /foo\(bar\)\@! "bar"가 뒤에 오지 않는 "foo"
:help pattern :help perl-patterns
Korean Perl Workshop 2012 153
Regexp::Debugger
• Damian Conway
• CPAN
use Regexp::Debugger; 또는
$ rxrx
Korean Perl Workshop 2012 154