정규식

어떤 문자열 속에 특별한 내용의 단어 또는 짧은 구문이 있는지 검사하는 기능은 웬만한 편집기면 다 가지고 있지요. 그러나 이러한 찾기 기능은 대개 한계가 있어서 어떠한 '패턴'의 구문을 찾는데는 조금 문제가 있지요.
예를 들어 "hello world"와 "hello my world"를 같은 패턴으로 생각하고 찾아야 한다면 찾기에 애로를 느끼지 않겠습니까 ?.
이러한 패턴을 한 문자열 안에 설정시켜 주는 것이 바로 정규식, Regular Expression, RE 입니다.

한가지씩 봅시다.

. : 점이 보이세요 ? 이것은 딱 한 자(영문 한 자)입니다. 어떤 글자도 좋습니다. 단 개행문자인 "\n"만은 제외입니다. 그래서 위의 예제 contain.pl에서 /hat/대신에 /h.t/를 해도 같은 결과가 출력됩니다. /h.t/은 hat, hot, hut, hit, 등등에 다 해당되지만 heat, heart, hoot, ht 등은 아닙니다. 기억하십시오. "개행문자를 제외한 임의의 한 글자." 밑줄 치세요. 그리고 간단한 예제로 실험해 보세요. 혹시 제가 잘못했으면 제게 삿대질이라도 해야 할 것 아닙니까 ?

* : 별, asterisk 입니다. 도스에서 많이 보신 와일드 카드로 쓰이는 문자죠. 점(.)은 한 글자를 대신할 수 있지만 별(*)은 0 또는 그 이상의 어떤 수의 문자로도 대치될 수 있습니다. 또 다른 점은 점(.)은 그 자체로 어떤 문자를 대신하지만 별(*)은 그 앞에 어떤 문자가 있어야 합니다. 즉 o*p 하면 0 또는 몇 개의 o와 p가 있는 것을 말하며 p, op, oop, oooooooop 등이 모두 해당됩니다. 그러나 * 앞에 o와 같은 어떤 문자가 있어야 하고 만일 $str =~ /*p/라고 하면 perl.exe는 에러 메시지를 내고 실행을 시키지 않습니다.

# contain2.pl

$long = "I wrote it at the beginning.";
print $long, "\n";

if( $long =~ /h*at/ )   # /*at/, /a.t/도 실험해 보세요.
  {  print "Yes, it contains.\n";  }
else
  {  print "No, it does not.\n";  }
결과가 어떻게 나올까요 ?

+ : 보태기 표입니다. *가 0 또는 몇 개인 데에 반해 +는 '1' 또는 몇 개 입니다. 그래서 o+p는 op, oop, ooooop와 일치되지만 p에는 일치되지 않습니다.

? : 물음표입니다. 0 또는 1개의 문자에 일치됩니다. 즉 o?p는 p 아니면 op입니다.

+? 모두 그 앞에 어떤 문자가 있어야 하는 것은 *과 동일합니다. 주의하세요.

익숙해지는 방법은 자꾸 해보는 수밖에 없습니다. 자꾸 만들어 보세요.

문자열의 어디에나 있는 구문이 아니라 문자열의 처음이나 맨 끝에 있는 것만을 찾으려 할 때에는 어떻게 할까요 ?

^는 문자열의 맨 처음을, $는 맨 끝을 나타냅니다. $는 일반변수의 $와 혼동될 염려가 없으니 신경 쓰지 마십시오.

생각할 수 있는 것들을 적어보도록 하지요.

t.e             the, tie, toe, 등등. settle에도 포함되어 있죠.
^t.e            위와 같으나 문자열/줄의 처음에 있을 경우.
                settle은 안됨.
^.e             he, me, regard 등이 문자열/줄의 처음에 있을 경우.
t.e$            toe, necktie 등으로 끝나는 문자열.
s*he            she, he, ssssssssshe, the, tshe
s+he            she, sshe, sssssshe, tshe 등. he, the는 안 됨.
s?he            he 또는 she.
^s?he$  "he" 또는 "she". 한 줄에 이 두가지만이 허용됩니다.
.*              개행문자를 제외한 모든 글자들.
^$              빈 줄/문자열.
또 드리는 잔소리: 백견이 불여 일행.

조금 더 심오한 선택을 해 볼까요 ?
[]로 쌓여있는 글자들은 몇 자가 되든 단 한 글자만을 대변하며, 포함된 모든 글자 중 어떤 글자라도 있으면 일치되는 것으로 인정됩니다. []를 character class라고 하더군요.

[st]he  she 또는 the.
^[st]he 문자열의 처음에 있는 she 또는 the.
[a-z]           소문자 한 자. '-'는 between의 의미로 범위를 나타냄.
[a-zA-Z]        소문자 또는 대문자 한 자.
[0-9]           십진수 한 자.
[-0-9]          '-'부호와 십진수 중 한 자. 빼기 부호를 사용하려면
                맨 앞에 두어야만 합니다. 안 그러면 범위를 나타내는
                '-'부호와 헷갈리겠죠 ?
[0-9a-fA-F]     십육진수 한 자.
h[eao]y         hey, hay, hoy.
^h[eao]y        문자열의 첫머리에 있는 hey, hay, hoy.
h[^eao]y        hey, hay, hoy를 "제외한" /h.y/. 즉 hiy, hfy, huy 등.
                문자클래스(character class)안에서의 ^ 부호는 not 또는
                except를 의미합니다.
[^a-z]          소문자 빼고 모두 다.
[a-z]+          소문자가 한 자 또는 줄줄이 있는 것들.
                w, kdfns, oie, jfowjdngjwerh 등등.
[tT][hH][eE]    the, thE, tHe, tHE, The, ThE, THe, THE, 등.
마지막 예는 대소문자 구분없는 the를 찾기위한 방법의 하나였습니다. 그런데 조금 더 편리한 방법이 있습니다.
$long = "The world of mine !";
if( $long =~ /World/i ) { print "Yes\n"; } # 이상한게 있지요 ?
else { print "No.\n"; }
이 예에서는 yes가 출력됩니다. /World/뒤에 i 자 하나가 매미처럼 붙어 있죠 ? "ignore case"의 첫 글자로 대소문자 구분을 하지 말란 뜻입니다. HTML의 태그들은 대소문자를 구분하지 않습니다. 그런 경우 <table>과 <TaBle>을 동시에 찾으려면 아주 유용한 선택이 되겠지요.

앞에서 $_라는 특수 변수에 대하여 이야기 한 적이 있습니다. 찾아보세요. 다시 한 번 말씀드리면 어떤 변수가 사용되어져야 할 장소에 아무 변수도 보이지 않으면 PERL은 $_가 있는것으로 생각합니다 그래서 위의 간단한 예제는 이렇게 고쳐도 같은 결과를 보여줍니다.

$_ = "The world of mine !";
if( /World/i ) { print "Yes\n"; } # $_ =~ ... 로 생각합니다.
else { print "No.\n"; }
또 다른 재미있는 특수 변수를 두개 더 소개하지요. 정신 사나우시면 그냥 그런게 있구나 하고만 넘어가세요.
위의 예제를 이렇게 고쳐봅시다.
$_ = "The world of mine !";
if( /World/i ) { print "Yes\n"; } # $_ =~ ... 로 생각합니다.
else { print "No.\n"; }
print "Fore : [$`]\nAft  : [$']\n";  # $`, $' ??
위의 예제를 직접 실행해 보시기 바랍니다 출력되는 결과는
Yes
Fore : [The ]       # The 뒤에 공백이 하나 있습니다.
Aft  : [ of mine !] # of 앞에도 공백...
입니다.
어떤 문자열의 일치를 실험해 본 후 결과가 성공적이면 $`에는 찾은 문구의 바로 앞부분이, $'는 뒷부분이 할당되어 있습니다.
My name is Jongpil
My name is Gildong
My name is Jungkwang
My name is Baram
My name is Dooli
자, 위의 각 줄에서 이름만 따로 저장할 수 있겠습니까 ? $'를 사용하면 되겠지요 ?

문자열의 일치에 대해서는 이 정도만 아셔도 웬만한 일은 하실 수 있으실 겁니다. 다음은 그저 참고 삼아서 보시고 더 알고 싶으신 분은 영문 참고서(perlre.htm)를 한번 보세요.

- 수직 막대, | 는 or 부호이고, ()는 글자들의 그룹입니다. -

/clean|dirty/           clean 또는 dirty
(eg|le)gs               eggs 또는 legs
ba(na)+                 bana, banana, bananananana
- ()를 사용하는 그룹화된 패턴은 한글에 적용할 때 유용하리라 생각됩니다. 다음 예문을 보시지요.
$long = "닐리리 닐리리 닐리리야";
if ( $long = /닐(리)+/ )
  { print "닐리리야 니나노.\n"; }
else
  { print "어쩔시구리.\n"; }
- 특수문자들도 유용합니다. -

\n      개행문자.
\t      탭.
\w      문자 또는 숫자. [a-zA-Z0-9]와 같습니다.
\W      \w가 아닌 것. [^a-zA-Z0-9]와 같습니다.
\d      십진수. [0-9]와 같습니다.
\D      십진수 아닌 것. [^0-9]와 같습니다.
\s      공백문자. space, 탭, 개행문자 등.
\S      공백문자 아닌 것.
\b      단어의 경계(boundary). [ ]의 바깥에서만 사용.
        "the green grass of home"의 \bthe\b, \bgreen\b,
           \bgrass\b, \bof\b, \bhome\b.
        \bthe\b와 \bhome\b도 포함됨에 유의하세요.
\B      단어의 경계가 아닌 것.
- {}는 문자, 그룹의 개수를 지정합니다. -

ba(na){2}       banana만 해당.
ba(na){1,2}     bana, banana만 해당.
ba(na){2,5}     banana, bananana, banananana, bananananana만 해당.
o{2,4}p         oop, ooop, oooop.
중요: 위에서 쓰였던 특수한 문자들 ^$|[]()\/{}*.?+ 등을 그 자체로 사용할 때는 그 앞에 \를 붙여 주세요.

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

정말 중요한 것은 직접 간단한 프로그램을 만들어서 시험해 보는 것입니다.
이전 | 목록 | 다음
Comments