wEb/regex

regular expression

dd2i 2012. 4. 25. 13:14
반응형
Regular Expression(정규 표현식)은 문자열의 패턴을 기술하는 객체로 보면 된다. 
JavaScript에서RegExp 클래스는 정규 표현식을 표현하기 위해 사용되며, String과 같이 자체적으로 정규식 표현식을 이용하는 메소드들을정의하고 있는데 이들 메소드들은 패턴 매칭Search-and-Replace와 같은 기능을 수행한다. 

JavaScript에서 사용되는 정규 표현식 ECMAScript v3에서 표준화가 되었으며 JavaScript 1.2에서는ECMAScript v3에서 제공하는 기능들 중 일부만을 제공하고 있다. JavaScript 1.5가 되면서 비로소 관련된 모든기능(full spec)을 지원하고 있다. 

JavaScript에서 사용되는 정규 표현식은 Perl 프로그래밍 언어에서 지원하는정규 표현식에 기초를 하고 있으며, 일반적으로 JavaScript 1.2는 Perl4의 정규 표현식중 일부를 지원하고JavaScript 1.5는 Perl5에서 제공하는 정규 표현식 중 상당 부분을 지원한다고 보면 된다.


정규 표현식 정의(Defining Regular Expression)

JavaScript에서 정규 표현식은 RegExp 객체로 표현이 된다. RegExp객체는 RegExp() 생성자를 통해 생성하지만 특별한 문법을 통해서도 생성할 수 있다. 일반 문자열은 따옴표 안에 명시하는 것과 마찬가지로 정규 표현식은 slash(/) 문자(pair) 안에 기술을 한다. 따라서 다음과 같이 기술할 수 있다.

var attern = /s$/; // matches any string that ends with the letter "s"

위의 코드는 다음과 같이 RegExp() 생성자를 이용하여 생성할 수도 있다. 표현 형태는 다르지만 결과는 동일하다고 보면 된다.

var attern = new RegExp("s$");
RegExp객체를 생성하는 것은 어렵지 않게 할 수 있다. 하지만 정규 표현식을 이용하여 문자열에 대한 패턴을 기술하는것은 그리 쉬운 작업은 아니다. JavaScript는 Perl에서 사용되는 문법을 그대로 사용하기 때문에 Perl 프로그래밍언어에 익숙한 사람이라면 JavaScript에서 정규 표현식을 기술하는 방법을 이미 알고 있다고 봐도 된다.

Literal Characters

앞에서 설명한 것처럼 모든 알파벳 문자와 숫자는 정규 표현식에서 문자 자체적으로 매칭에 적용된다. JavaScript 정규표현식 문법은 escape sequence(backslash)을 이용하여 알파벳 문자가 아닌 문자도 기술할 수 있도록 지원한다.예를 들어 \n 은 문자열에서 개행과 매칭된다. 다음은 이들 문자들을 나타낸다.


Character Matches
Alphanumeric character itself
\0 The NUL character(\u0000)
\t Tab(\u0009)
\n Newline(\u000A)
\v Vertical tab(\u000B)
\f Form feed(\u000C)
\r Carriage return(\u000D)
\x nn The latin character specified by the hexadecimal number nn
\u xxxx The Unicode character specified by the hexadecimal number xxxx
\c X The control character ^X


punctuation character들은 정규 표현식에서 특별한 의미를 가진다. 이들 문자들은 다음과 같다.

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

이들 문자중에서 일부는 정규 표현식의 특정 context에서는 특별한 의미를 가지지만 다른 context에서는 일반 문자로취급되는 것도 있다. 일반적으로 이들 문자들을 정규 표현식에 기술하고자 한다면 이들 문자 앞에 '\' 를 기술해줘야 한다. 그이외의 punctuation character들 예를 들어 따옴표(")와 @ 와 같은 문자는 특별한 의미를 가지지 않으며 단지 문자 그대로 매칭을 수행한다.

어떤 punctuation character가 escape되어야 하는지 정확히 기억하기 어렵다면 그냥 문자 앞에 백슬래쉬를붙이면 안전할 수 있다. 반면 많은 문자와 숫자가 표현식에서 백슬래쉬와 같이 사용하면 특별한 의미를 가지는 경우가 있기 때문에매칭하고자 하는 모든 문자 또는 숫자에 escape를 해서는 안된다. 백슬래쉬를 정규 표현식에 기술하고자 할 경우에는escape시켜야 한다. 즉 백슬래쉬를 표현하기 위해서는 /\ \/ 와 같이 기술해야 한다.


Character Classes

각각의 문자들은 [] 안에 위치시킴으로써 character class를 형성할 수 있다. Character class는 []에 포함된 문자들 모두에 대해 매칭한다. 따라서 정규 표현식이 /[abc]/와 같다면 여기에 기술된 a, b, 또는 c에 대해 매칭을 수행한다. Negate 문자를 통해 []에 기술된 문자를 제외하고매칭을 수행할 수도 있다. Negate character class는 []안에 기술된 문자들 중에 첫번 째 문자 앞에^(caret) 를 기술함으로써 정의할 수 있다. 예를 들어 정규 표현식 /[^abc]/ 는 a, b, c를 제외한 모든 문자에 대해 매칭을 수행한다. Character class에서 범위를 나타낼 때는 hyphen(-)을 사용한다. 소문자 알파벳을 매칭하기 위해서는 /[a-z]/ 을 사용하면 되고 숫자와 문자를 모두 매칭하고자 할 경우에는 /[a-zA-Z0-9]/ 와 같이 사용하면 된다.

일반적으로 자주 사용되는 character class들이 많이 있기 때문에 JavaScript 정규 표현식 문법은 이들을 위해 특수 문자와 escape sequence를 제공한다. 예를들어 \s 는 공백문자, 탭 문자, 그리고 Unicode 공백문자가 아닌 문자를 매칭시키며 \S 는 Unicode 공백문자가 아닌 모든 문자들을 매칭시킨다. 다음 표는 이들 문자들을 나열하고 character class 문법을 설명한다.



Character Matches
[...] Any one character between the brackets.
[^...] Any one character not between the brackets.
. Any character except newline or another Unicode line terminator.
\w Any ASCII word character. Equivalent to [a-zA-Z0-9_].
\W Any character that is not an ASCII word character. Equivalent to [^a-zA-Z0-9_].
\s Any Unicode whitespace character.
\S Any character that is not Unicode whitespace. Note that \w and \S are not the same thing.
\d Any ASCII digit. Equivalent to [0-9].
\D Any character other than an ASCII digit. Equivalent to [^0-9].
[\b] A literal backspace (special case).


Repetition

지금까지 배운 내용을 이용하면 두개의 숫자을 기술하기 위해 /\d\d/ 와 같이 기술하며, 4개의 숫자를 기술하려면/\d\d\d\d/와 같이 기술할 것이다. 하지만 모든 개수의 숫자를 표현하거나 3개의 문자에 이어서 오는 숫자를 표현하기는힘들 것이다. 이런 복잡한 패턴들은 정규 표현식의 문법 중 반복 회수를 지정하는 방법을 이용함으로써 기술할 수 있다.

반복회수를 지정하는 문자는 항상 패턴 다음에 기술한다. 특정 형태의 반복 패턴은 자주 사용되기 때문에 이들 경우를 나타내는 특별한 문자를 정의하고 있다. 예를 들어 + 은 앞에 기술된 패턴에 대해 1회 이상 반복되는 것과 매칭한다. 다음은 반복을 기술하는 문법을 요약한 것이다.



Character Meaning
{n,m} Match the previous item at least n times but no more than m times.
{n,} Match the previous item n or more times.
{n} Match exactly n occurrences of the previous item.
? Match zero or one occurrences of the previous item. That is, the previous item is optional. Equivalent to {0,1}.
+ Match one or more occurrences of the previous item. Equivalent to {1,}.
* Match zero or more occurrences of the previous item. Equivalent to {0,}.

다음은 이와 관련된 예제를 보여준다.

/\d{2,4}/     // Match between two and four digits

/\w{3}\d?/    // Match exactly three word characters and an optional digit

/\s+java\s+/  // Match "java" with one or more spaces before and after

/[^"]*/       // Match zero or more non-quote characters

* 와 ? 를 사용할 때는 주의가 필요한데 이들 문자는 한번도 매칭이 안될 경우도 적용되기 때문에 매칭되는 결과가 없을수도 있다. 예를 들어 정규 표현식 /a*/ 가 문자열 "bbbb"와 매칭될 수도 있다 왜냐하면 문자열이 a 문자를 포함하지않아도 되기 때문이다.


Nongreedy repetition

위의 표에서 설명된 반복 문자들은 최대로 매칭되는 패턴을 찾아냄과 동시에 다음 패턴 매칭작업을 계속해서 수행해 나간다.이런 종류의 반복을 "greedy"라고 말한다. JavaScript 1.5 이후 부터는 nongreedy한 방법으로 반복 패턴을지정할 수도 있다. 단지 ??, +?, *?, {1,5}?와 같이 반복 문자 다음에 물음표(?)를 붙여주면 된다. 예를들어정규표현식 /a+/은 하나 이상의 a문자와 매칭된다. 이 패턴을 "aaa"라는 문자열에 적용했을 때 3자 모두에 매칭이 되지만/a+?/ 는 하나 이상의 a가 가장 최소인 것과 매칭된다. 따라서 이 패턴을 적용하면 a 문자 하나만 매칭되게 된다.

Nongreedy 반복을 사용할 경우 항상 예상했던 결과만 얻는 것은 아니다. 패턴 /a*b/를 생각해보자. 이 패턴은0개 이상의 a문자로 시작하면서 b문자로 이어지는 패턴과 매칭된다. 이 패턴을 "aaab"에 적용시키면 모든 문자열과 매칭이 될것이다. 이제 nongreedy 버전 /a*?b/ 를 적용시켜보자. 가장 작게 a 문자가 나나타면서 b로 이어지는 패턴과일치해야 한다. 이 정규표현식을 "aaab"에 적용시켜 보면 마지막 글자인 b만 매칭될 것이라는 예상과 달리 greedy버전처럼 모든 문자열에 대해 매칭된 다는 것을 알 수 있다. 왜냐하면 정규 표현식의 패턴 매칭은 문자열에서 패턴과 일치하는 첫번째위치를 먼저 찾기 때문이다. 여기서 적용한 nongreedy버전은 문자열에서 첫번째 문자와 매칭이 되기 때문에 이와 같은 결과가발생하게 된다.


Alternation, Grouping, and References

정규 표현식 문법은 특수한 문자를 이용하여 alternative, grouping subexpression, 이전subexpression에 대한 참조(referring)을 기술한다. '|' 문자는 alternavice들을 구분시키는 역할을한다. 예를들어 /ab|cd|ef/는 ab 또는 cd, 또는 ef와 매칭된다. 그리고 /\d{3}|[a-z]{4}/는 3자리의숫자 또는 알파벳 4개의 문자와 매칭된다.

alternative는 왼쪽에서 오른쪽으로 매칭이 이루어지기 때문에 왼쪽 alternative가 먼저 매칭이 이루어지면오른쪽 alternative는 무시되어 버린다. 따라서 /a|ab/ 정규 표현식을 "ab"에 적용시킬 경우 첫번째 문자만 매칭이된다.

괄호는 정규 표현식에서 몇가지 특별한 목적으로 사용된다. 하나는 정규 표현식에서 그룹을 하나의 subexpression으로구분짓는 것이다. 이렇게 하면 이들 subexpression들은 하나의 단위로 취급이 되는데 |, *, +, ?와 같이 사용할수 있게 된다. 예를들어 정규 표현식 /java(script)?/ 은 java 다음에 script가 올 수도 있고 오지 않을수도 있을 경우와 매칭이 된다. 또 /(ab|cd)+|ef/ 는 하나 이상의 ab 또는 cd가 오거나 ef가 올 경우 매칭이된다.

괄호를 사용하는 다른 목적은 하나의 패턴 내에서 sub 패턴을 정의하기 위해 사용한다. 정규 표현식이 하나의 문자열과매칭이 될 경우 이 문자열 내부에서 다시 괄로로 묶은 sub 패턴을 적용하여 해당 부분을 추출할 수가 있다. 예를 들어 하나이상의 소문자로 구성된 문자열과 뒤따라 오는 하나 이상의 숫자를 검색한다고 하면 /[a-z]+\d+/와 같은 정규 표현식을사용할 것이다. 하지만 이 패턴에서 뒷 부분에 나오는 숫자에만 관심이 있다면 이 부분을 괄호로 묶어주면 된다./[a-z]+(\d+)/ 이와 같이 하면 숫자 부분만을 추출할 수가 있게 된다.

정규 표현식에서 괄호로 묶어 sub expression을 구성할 경우 뒷 부분에서 괄호로 묶은 sub expression을참조할 수도 있는데 \ 다음에 숫자를 기입하여 표현하며 이때 숫자는 정규 표현식 내에서 사용된 sub expression의위치를 의미한다. 예를 들어 \1은 첫번째 subexpression을 가리키고 \3은 3번째 subexpression을가리킨다. 예를 들어 다음과 같은 정규 표현식에서

/(\[Jj]ava([Ss]cript)?)\sis\s(fun\w*)/

([Ss]cript) 부분은 \2로 참조된다.

앞 부분에 있는 subexpressioin을 참조는 subexpression의 패턴 자체를 참조하는 것이 아니라 이 패턴에의해 패칭되는 텍스트를 참조한다는 것을 유의해야 한다. 따라서 Constraint 처리(예; 특정 부분은 동일한 문자들이나타나야 한다)와 같은 부분에서 많이 사용될 수 있다. 예를 들어 다음의 정규표현식은

/\['"]\[^'"]*\['"]/
따옴표 또는 쌍따옴표 안에 문자열이 있는 경우와 매칭을 하게 되는데 이 경우는 앞부분의 따옴표 또는 쌍따옴표가 올 경우 뒷부분에 반드시 앞에 부분과 일치해야 한다는 보장이 없다. 하지만 다음과 같이 기술할 경우 따옴표로 시작하면 따옴표로 끝나고쌍따옴표로 시작하면 쌍따옴표로 끝난는 것을 보장할 수 가 있다.
/(\['"])\[^'"]*\1/
character class dksdp reference를 사용할 수 없기 때문에 다음과 같이 기술하는 것은 문법에 어긋난다.
/(\['"])\[^\1]*\1/

JavaScript 1.5에서는 reference를 생성하지 않고도 항목들을 grouping할 수 있도록 지원하고 있다.이렇게 하기 위해서는 '('와 ')'를 이용하여 grouping하는 것이 아니라 '(?:'와 ')'를 이용하여 grouping을해야 한다. 예를들어 다음과 같은 패턴이 있다면

/(\[Jj]ava(?:[Ss]cript)?)\sis\s(fun\w*)/
(?:[Ss]cript)은 단순히 grouping을 위해 사용되었으므로 반복을 나나태는 문자 '?'를 이 그룹에 적용시킬 수가 있으며, 이 정규 표현식에서 \2는 (fun\w*)와 매칭되는 텍스트를 가리키게 된다.

지금까지 설명한 내용들을 다음 표와 같이 요약된다.


Character Meaning
| Alternation. Match either the subexpression to the left or the subexpression to the right.
(...) Grouping. Group items into a single unit thatcan be used with *, +, ?, |, and so on. Also remember the charactersthat match this group for use with later references.
(?:...) Grouping only. Group items into a single unit, but do not remember the characters that match this group.
\n Match the same characters that were matchedwhen group number n was first matched. Groups are subexpressions within(possibly nested) parentheses. Group numbers are assigned by countingleft parentheses from left to right. Groups formed with (?: are notnumbered.


Specifying Match Position

정규 표현식에 사용되는 \b는 문자열 내에서 매칭을 위해 사용되는 문자를 기술하는 것이 아니라 매칭이 발생되는 위치를기술한다. 때로 이와 같은 것들을 정규 표현식 Anchor라고 부르는데 문자열 내에서 특정 위치와 매칭시키기 때문이다. 가장흔히 사용되는 anchor는 ^와 $가 있는데 각각 문자열의 시작부분과 마지막 부분에 매칭된다.

문자열 내에서 "JavaScript"와 매칭되는 문자열을 찾고자 한다면 /^JavaScript$/와 같이 기술하면 된다."Java"라는 단어를 찾고 싶다면 /\sJava\s/와 같이 기술하면 되는데 이 경우는 Java 단어 앞 뒤로 공백문자를필요로 한다. 그러나 이와 같이 할 경우 두 가지의 문제점이 발생한다. 첫 번째 "Java"라는 글자가 문자열의 시작 부분과끝부분에 있다면 매칭이 되지 않는다(Java 앞, 뒷 부분에 공백이 없다는 가정하에). 두번째 문제는 매칭이 발생하더라도 매칭된문자열은 앞과 뒷 부분에 공백을 포함하게 된다. 따라서 \s'를 이용하여 공백 문자를 매칭하는 대신 \b 를 이용하여 단어의경계와 매칭하면 된다. 따라서 \bJava\b/ 와 같은 정규 표현식을 사용하면 된다.

JavaScript 1.5에서는 임의의 표현식을 anchor 조건으로 사용할 수 있도록 지원한다. 표현식을 (?= 와 )사이에 기술하면 lookahead assertion이라고 하는데 실제 매칭을 수행하기 전에 (?= 와 ) 사이에 기술된 표현식을먼저 매칭한다. 예를들어 일반 프로그래밍 언어 다음에 콜론(:)이 기술된 패턴을 매칭하기 위해/[Jj]ava([Ss]cript)?(?=:)/와 같이 기술할 수 있다. 이 패턴을 "JavaScript : TheDefinitive Guide"에 적용하면 "JavaScript"와 매칭이 되지만 "Java in a Nutshell"에적용하면 "Java"와 매칭이 되지 않는다. 왜내하면 콜론이 기술되지 않았기 때문이다.

표현식을 (?!와 ) 사이에 기술하면 negative lookahead assertion이라고 하는데 lookaheadassertion에 부정적인 개념이 된다. 예를들어 정규 표현식 /Java(?!Script)([A-Z]\w*)/ 은Java시작하면서 이어지는 문자가 대문자 그리고 임의의 ASCII 문자가 이어지지만 Java다음에 바로 Script가 이어지지않는 문자열과 매칭한다. 이 패턴은 "JavaBeans"와는 매칭되지만 "Javanese"와는 매칭되지 않는다. 그리고"JavaScrip"과는 매칭되지만 "JavaScript" 또는 "JavaScripter"와는 매칭되지 않는다.

다음의 표는 위의 내용을 요약 정리한다.


Character Meaning
^ Match the beginning of the string and, in multiline searches, the beginning of a line.
$ Match the end of the string and, in multiline searches, the end of a line.
\b Match a word boundary. That is, match theposition between a \w character and a \W character or between a \wcharacter and the beginning or end of a string. (Note, however, that[\b] matches backspace.)
\B Match a position that is not a word boundary.
(?=p) A positive lookahead assertion. Require thatthe following characters match the pattern p, but do not include thosecharacters in the match.
(?!p) A negative lookahead assertion. Require that the following characters do not match the pattern p.


Flags

정규 표현식 flag는 높은 수준의 패턴 매칭 규칙을 명시한다. 일반 정규표현식과는 달리 flag는 슬래쉬(/)문자 사이에기술되는 것이 아니라 / 문자 밖에서 기술된다. JavaScript 1.2는 두가지의 flag를 지원하는데 i 는 패턴을 매칭할 때 대소문자를 구분하지 않는다는 것을 의미하며 g flag는 전역에 걸쳐 패턴을 매칭한다는 것을 의미한다. 이 두 flag가 합쳐져서 전역에 걸쳐 대소문자를 가리지 않고 패턴을 매칭시킬 수도 있다.

대소문자를 구분하지 않으면서 처음으로 매칭되는 "java" 문자열("Java", "JAVA"등)을 찾기 위해서/\bjava\b/i 와 같이 정규 표현식을 작성할 수 있다. 매칭되는 모든 것을 찾기 위해서는 /\bjava\b/ig 와 같이g flag를 추가할 수도 있다.

JavaScript 1.5에서는 m flag를 추가적으로 지원하고 있는데 이 flag는 multilinemode를 나타낸다. 이 mode에서 대상 문자열이 개행(newline)을 포함하고 있다면 ^와 $ anchor는 대상 문자열의시작과 끝을 매칭할 뿐만 아니라 각 라인의 시작과 끝도 같이 매칭작업을 수행한다. 예를들어, 패턴 /java$/im 은"java" 뿐만 아니라 "Java\nis fun"도 같이 매칭이 된다.

다음의 표는 위의 설명을 요약정리한다.


CharacterMeaning
i Perform case-insensitive matching.
g Perform a global matchthat is, find all matches rather than stopping after the first match.
m Multiline mode. ^ matches beginning of line or beginning of string, and $ matches end of line or end of string.




출처: http://cafe.naver.com/javaserverfaces/63


반응형

'wEb > regex' 카테고리의 다른 글

빈 줄 지우기  (0) 2013.04.24