javascript에서의 정규식 설명이지만, 쉽고 명쾌하게 잘 돼 있음.
java에서 정규식 문법을 참조할때도 좋을 듯.

======================================================

1. 개념잡기

일반화 시킨 표현. 이것을 정규표현이라고 요약할 수 있을 것 같다.
다음의 과정을 너무 쉽다 생각말고 따라오길 바란다.

- 감잡기

"12354" -> 숫자
"asdfasf" -> 알파벳
두 가지의 간단정규표현을 만들었다. 실생활의 보기와 비추어보자.
"길이가 3인 이름!"
위의 표현은 길이를 표시하는 방법이 없다.
조금 더 발전시켜서 "알파벳{3}"이런식으로 길이를 표현할 수 있도록 한다. 그리고, "알파벳"란 것도 너무 길다 "알" 이라고 한 글자로 표현한다. 그러면 "길이가 3인 이름"은 "알{3}"으로 표시가 가능하다.
길이가 10인 숫자는 "수{10}"
길이가 1인 알파벳이 나오고 그 다음에 길이가 3인 숫자가 나오는 문자열 "알{1}수{3}" 얼핏이나마 감이 올 것이다.
"첫 글자는A, 그 다음은 아무 알파벳 5글자" -> "A알{5}"

- 조금 더

아이디는 대개 첫 글자는 영문이고 두 번째부터는 영문이나 숫자가 온다. 이것을 표현하기 위해선 이것 들 중에 하나란 의미를 갖는 새로운 표현이 필요하다.
"a,b,c,d 중에 하나" -> [abcd]
응용하면,
"알파벳이나, 숫자중 하나" -> [알수]
"[" 안에 있는 문자들의 순서는 의미가 없으며, 그 표현은 (클래스라고 한다.)
결국 한 글자를 말한다.
위에서 말한 "첫 글자는 영문, 두 번째 부터는 영문이나 숫자가 11자"를 표현하면, "알[알수]{11}".
그런데, 실제로 모든 아이디가 12자인 것은 아니다, 대개 4자부터 12자를 지원한다.
새로운 표현이 등장한다. "몇 자부터 몇 자"
"A가 3글자부터 12자" -> "A{3,12}"
"알파벳이나 숫자가 1자부터 100자" -> "[알수]{1,100}"
이제 아이디를 다시 정의하자.
"첫 글자는 영문, 영문이나 숫자가 3자부터 11자" -> "알[알수]{3,11}"

2. 표현식

지금 까지의 규칙에서 설명한 용어를 실제 정규표현에서 사용하는 표현으로 바꾸고, 다른 세부적인 옵션에 대해 알아보자.

\ : 다음의 글자가 특별한 문자임을 나타낸다. 때론, 그 다음 문자 자체를 의미하기도 한다.
보기를 들면, "\n"은 문자"\"과 문자"n" 두 글자와 매치되는 것을 의미하는 것이 아닌, 새줄(New Line)을 의미하며, "\\"은 첫 "\" 다음 문자인 "\" 자체를 의미한다. 즉, "\\"은 "\"과 매칭된다.

^ : 입력문자열의 맨 처음을 의미한다. (맨 첫 글자가 아니라, 맨 처음이란 문맥적 의미를 말한다. 아주 중요하다) 기본적으로 정규표현은 입력 문자열의 한 줄에만 적용된다. 하지만, 옵션에 따라 여러줄에 적용할 수도 있다. 그럴 경우에는 "^"는 "\n" 나 "\r" 다음의 위치를 의미한다.

$ : "^"는 반대로 입력 문자열의 맨 끝을 의미한다. 역시 여러줄에 정규표현이 적용될 경우에는 "\n"이나 "\r"의 앞의 위치를 의미한다.

* : 이 문자 앞의 표현이 0번내지 무한번 반복될 수 있음을 말한다.
보기를 들면, /a*/은 "a", "", "aaaa", "aaaaa"와 매칭된다.
(0번이상은 없어도 된다는 것을 의미한다.)

+ : *와 같지만, 0번이상이 아니라 1번이상이라는 점을 제외하곤 /*/와 같다.

? : 앞의 표현이 0번 또는 1번. /do(es)?/는 "do", "does"와 매칭된다.

{n} : 앞의 표현이 n은 음수가 아닌 정수이어야 하며, 앞의 표현이 n번 매치되는 것을 말한다.

{n,} : 앞의 표현이 n은 음수가 아닌 정수이어야 하며, n번 이상 매치되는 것을 말한다.

{n,m} : 앞의 표현이 n번 이상 부터 m번 이하까지 매칭되는 것을 말하며, /*/는 /{0,}/과 같으며, /+/는 /{1,}/과 /?/는 /{0,1}/으로 표현 가능하다.

. : "\n"을 제외한 한 글자를 뜻한다. 만일 모든 글자를 표현하고 싶다면("\n"마저도 합친) /[.\n]/을 사용하면 된다.

x|y : x 또는 y와 매칭된다. 보기를 들면, /z|food/는 "z" 또는 "food"와 매칭된다. /(z|f)ood/는 "zood" 또는 "food"와 매칭된다.
(참고로 괄호는 묶어준 것 이상의 의미가 있다.)

(패턴) : 해당 패턴과 매칭시키고, 그 부분을 특정 변수에 담는다.
그 변수 이름은 JScript는 $0~$9까지의 변수에 저장이 되고(Perl과 같다.),
VBScript!에서는 SubMatches 컬렉션에 저장된다.
괄호기호 자체와 매치시키고 싶다면? /\(/와 /\)/를 사용한다.

(?:패턴) : 해당 패턴과 매칭은 시키지만, 그 부분을 특정 변수에 담지 않는다. 왜 이게 필요할까?
위의 보기에서 /(z|f)ood/는 "zood" 또는 "food"와 매칭된다고 했는데, 단순히 매칭의 목적으로 사용했지만, "zood"의 경우 "z"가 $0 이란 변수에 저장이 되고 말았다. 이러한 것을 막기 위해서 사용하는 것이 (?:패턴)이다.

(?=패턴) : (?:패턴)과 동일하지만, 패턴과 일치한 부분이후부터 다음 매치가 일어나지 않고 패턴 앞부터 다시 매칭이 진행된다. 즉, 룩업(lookup, lookahead)을 할 뿐이다. /Windows (?=95|98|NT|2000)/ 은 "Windows 2000"의 "Windows" 부분과 매칭이 되며 다음 매칭은 "2000" 다음 부터가 아닌 "Windows" 다음 부터 진행이 된다.

(?!패턴) : (?=패턴)과 반대다. /Windows (?=95|98|NT|2000)/ 은 "Windows 3.1"의 "Windows" 부분과 매칭이 된다.

[xyz] : "["안에 있는 표현중 하나를 의미한다.

[^xyz] : "["안에 있는 표현을 제외한 것중 하나를 의미한다. "[^abc]"는 "plain"의 "p"때문에 매칭된다.

[a-z] : "a"부터 "z" 까지의 문자중 하나

[^a-z] : "a"부터 "z" 까지의 문자를 제외한 하나

\b : 단어의 경계(단어와 공백, "\n", "\r"의 사이)와 매칭된다. 보기를 들면, "er\b"는 "never"와는 매칭되지만, "verb"와는 매칭되지 않는다.

\B : 단어의 경계가 아닌 것과 매칭된다. "er\B"는 "verb"와는 매칭되지만, "never"와는 매칭되지 않는다.

\cx : Ctrl+x 키와 매칭된다. "\cc"는 Ctrl+C와 매칭된다. x의 범위는 [a-zA-Z]이며, 만일 이 이외의 문자를 사용한다면 "\c"는 "c"와 동일하다.

\d : [0-9]와 같다.

\D : [^0-9]와 같다. 참고로 대문자는 소문자의 반대 의미를 갖는다.

\f : 폼피드(form-feed) 문자를 의미하며, "\x0c"와 "\cL"과 동일하다.

\n : 새 줄(newline)를 의미하며, "\x0a"와 "\cJ"와 동일하다.

\r : 캐리지 리턴(carriage return)을 의미하며, "\x0d"와 "\cM"과 동일하다.

\t : 탭. "\x09", "\cI"과 동일

\v : 버티컬 탭. "\x0b", "\cK"과 동일

\s : 화이트스페이스를 의미한다. 화이트스페이스란 공백, 탭, 폼피드, 캐리지리턴등을 의미한다. [ \f\n\r\t\v]과 동일("\f"앞에 공백이 있다. 주의!)

\S : "[^ \f\n\r\t\v]"

\w : "_"를 포함한 일반적인 단어에 사용되는 문자를 말한다. "[A-Za-z0-9_]" 과 동일

\W : "[^A-Za-z0-9_]"

\xn : n은 2자리 16진수이며, 해당 16진수 코드와 매칭된다. "\x412"는 16진수 41은 "A"이기 때문에 "A2"와 매칭된다.

\num : 캡쳐한 매칭을 가리킨다(백레퍼런스, backreference).
"(.)\1"은 연속된 두개의 문자열을 의미한다.
\n : "\1"은 위에서 캡쳐한 매칭(backreference)를 가리킨다고 했는데, 만일 이 패턴앞에 어떠한 n개의 캡쳐한 표현이 있다면 백레퍼런스이지만, 그렇지 않은 경우에는 8진수로 간주하여 해당 코드의 문자와 매칭된다.

\un : n은 4자리 UNICODE 이다. "\u00A9"은 copyright 심볼인 "ⓒ"와 매칭된다.


greedy, non-greedy

? : 앞에서 설명했는데, 왜 또? 라고 생각할 것이다.
?은 문맥에 따라 특별한 의미를 갖는다.
패턴 "o*"는 "foooood"와 매칭된다. 당연하다! 하지만, "f"앞의 "o"와 매칭되는 것이 아니다!! "ooooo"와 매칭된 것이다. 즉, 기본으로 정규표현 매칭은 가장 큰 범위를 선택한다. 이것을 greedy하다고 한다.
하지만, 때론 작은 범위에 매칭시킬 필요가 있을 경우가 있다.
(이의 적절한 보기는 잠시 후에 나온다.) "o*?"가 방금 말한 non-greedy 매칭이다.
수량관련 문자인 "*", "+", "?", "{n}", "{n,}", "{n,m}" 다음에 "?"가 나오면 non-greedy 매칭이된다.
잠시, 위에서 "o*?"가 "o"와 매칭된다고 했는데 이상하게 생각한 분이 있었을 것이다. 맞다. "o*?"는 ""와 매칭되었다. "*"는 0개이상임을 잊어선 안된다. "o+?"가 "o"와 매칭된다.

4. 보기

- 웹 주소

"http://msdn.microsoft.com/scripting/default.htm"
위의 주소를 표현할 수 있는 정규표현은 아래와 같다.
/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/
$1 : http
$2 : msdn.microsoft.com
$3 : 80
$4 : /scripting/default.htm

- 중복된 단어를 하나로

중복된 영어단어를 하나로 합치기 위해선, 우선 단어를 찾아야한다.
그리고 단어는 앞 뒤가 단어의 경계이어야한다. (말이 참 이상하지만..)
따라서, 아래와 같은 1차 정규표현을 얻을 수 있다.

/\b([a-z]+)\b/

연속해서 동일한 두개의 단어... 앞에서 캡쳐한 표현을 다시 활용하면 된다.
그리고, 단어와 단어 사이엔 화이트스페이스가 있다.

/\b([a-z]+)\s+\1\b/

- HTML 태그 제거

HTML문서에서 태그를 제거한 문서를 추출하고자 한다.
태그는 "<"와 ">"로 감싸여 있다.

/<.*>.*</.*>/

그런데, 위의 정규표현을 HTML문서에 적용하여 해당 패턴을 "", 빈문자열로 바꾸면 문서는 빈 문서가 되고 만다.

<html>
<title>...</title>
<body>
<font>.... </font>
...
</body>
greedy한 매칭이 기본값이라고 위에서 언급을 했다. 따라서, 위의 HTML 문서를 보면, <html>....</body>로 생각할 수 있다.
따라서, 문서 전체가 사라지는 것이다. 이것을 막기 위해선 "*"뒤에 "?"를 추가하면 된다.

/<.*?>.*?</.*?>/

아직 끝나지 않았다. :)

좀더 정제를 한다면, 올바른 HTML 문서는 <태그명>과 </태그명>이 서로 일치한다. 이것도 적용한다면,

/<.(*?)>.(*?)</\1>/

위의 $1에 해당되는 부분을 좀 더 생각해보면, ">"를 제외한 문자로 볼 수 있다. 따라서 최종적으로 아래와 같이 정리된다.

/<(\w+)[^>]*?>(.*?)</\1>/

- URL

/(?:^|")(http|ftp|mailto):(?://)?(\w+(?:[\.:@]\w+)*?)(?:/|@)([^"\?]*?)(?:\?([^\?"]*?))?(?:$|")/

- float 상수

/^(((+|-)?\d+(\.\d*)?)|((+|-)?(\d*\.)?\d+))$/ -1.1 1.1 .9 .8



정규식 구문
정규식은 일반 문자(예: a에서 z)와 메타문자 로 알려진 특수 문자로 구성된 텍스트 패턴입니다. 패턴은 텍스트 본문을 검색할 때 일치하는 문자열을 하나 이상 설명합니다. 정규식은 검색되는 문자열과 일치하는 문자 패턴을 찾는 템플릿의 역할을 합니다.

일반적으로 볼 수 있는 몇 가지 정규식 예는 다음과 같습니다.

JScript VBScript! 검색 /^\[ \t]*$/ "^\[ \t]*$" 빈 줄을 찾습니다.
/\d{2}-\d{5}/ "\d{2}-\d{5}" 2자리, 하이픈 및 5자리로 구성된 ID 번호를 찾습니다.
/<(.*)>.*<\/\1>/ "<(.*)>.*<\/\1>" HTML 태그를 찾습니다.



아래 표는 정규식 컨텍스트에 사용되는 모든 메타문자와 메타문자의 동작을 보여줍니다.

문자 설명 \ 그 다음 문자를 특수 문자, 리터럴, 역참조, 또는 8진수 이스케이프로 표시합니다. 예를 들어, "n"은 문자 "n"을 찾고 "\n"은 줄 바꿈 문자를 찾습니다. "\\" 시퀀스는 "\"를 찾고 "\("는 "("를 찾습니다.
^ 입력 문자열의 시작 위치를 찾습니다. Multiline 속성이 설정되어 있으면 ^는 '\n' 또는 '\r'앞의 위치를 찾습니다.
$ 입력 문자열의 끝 위치를 찾습니다. Multiline 속성이 설정되어 있으면 $는 '\n' 또는 'r'뒤의 위치를 찾습니다.
* 부분식의 선행 문자를 0개 이상 찾습니다. 예를 들어, "zo*"는 "z", "zoo" 등입니다. *는 {0,}와 같습니다.
+ 부분식의 선행 문자를 한 개 이상 찾습니다. 예를 들어, "zo+"는 "zo", "zoo" 등이지만 "z"는 아닙니다. +는 {1,}와 같습니다.
? 부분식의 선행 문자를 0개 또는 한 개 찾습니다. 예를 들어, "do(es)?"는 "do" 또는 "does"의 "do"를 찾습니다. ?는 {0,1}과 같습니다.
{ n } n 은 음이 아닌 정수입니다. 정확히 n 개 찾습니다. 예를 들어, "o{2}"는 "Bob"의 "o"는 찾지 않지만 "food"의 o 두 개는 찾습니다.
{ n ,} n 은 음이 아닌 정수입니다. 정확히 n 개 찾습니다. 예를 들어, "o{2}"는 "Bob"의 "o"는 찾지 않지만 "foooood"의 모든 o는 찾습니다. "o{1,}"는 "o+"와 같고, "o{0,}"는 "o*"와 같습니다.
{ n , m } m 과 n 은 음이 아닌 정수입니다. 여기서 m 은 n 보다 크거나 같습니다. 최소 n 개, 최대 m 개 찾습니다. 예를 들어, "o{1,3}"은 "fooooood"의 처음 세 개의 o를 찾습니다. "o{0,1}"은 "o?"와 같습니다. 쉼표와 숫자 사이에는 공백을 넣을 수 없습니다.
? 이 문자가 다른 한정 부호(*, +, ?, { n }, { n ,}, { n , m })의 바로 뒤에 나올 경우 일치 패턴은 제한적입니다. 기본값인 무제한 패턴은 가능한 많은 문자열을 찾는 데 반해 제한적인 패턴은 가능한 적은 문자열을 찾습니다. 예를 들어, "oooo" 문자열에서 "o+?"는 "o" 한 개만 찾고, "o+"는 모든 "o"를 찾습니다.
. "\n"을 제외한 모든 단일 문자를 찾습니다. "\n"을 포함한 모든 문자를 찾으려면 '[.\n]' 패턴을 사용하십시오.
( pattern ) pattern 을 찾아 검색한 문자열을 캡처합니다. 캡처한 문자열은 VBScript!의 경우 SubMatches 컬렉션, Jscript의 경우 $0 ... $9 속성을 이용하여 결과로 나오는 Matches 컬렉션에서 추출할 수 있습니다. 괄호 문자인 ( )를 찾으려면 "\(" 또는 "\)"를 사용하십시오.
(?: pattern ) pattern 을 찾지만 검색한 문자열을 캡처하지 않습니다. 즉, 검색한 문자열을 나중에 사용할 수 있도록 저장하지 않는 비캡처 검색입니다. 이것은 패턴의 일부를 "or" 문자(|)로 묶을 때 유용합니다. 예를 들어, 'industr(?:y|ies)는 'industry|industries'보다 더 경제적인 식입니다.
(?= pattern ) 포함 예상 검색은 pattern 과 일치하는 문자열이 시작하는 위치에서 검색할 문자열을 찾습니다. 이것은 검색한 문자열을 나중에 사용할 수 있도록 캡처하지 않는 비캡처 검색입니다. 예를 들어, "Windows(?=95|98|NT|2000)"는 "Windows 2000"의 "Windows"는 찾지만 "Windows 3.1"의 "Windows"는 찾지 않습니다. 예상 검색은 검색할 문자열을 찾은 후 예상 검색 문자열을 구성하는 문자 다음부터가 아니라 마지막으로 검색한 문자열 바로 다음부터 찾기 시작합니다.
(?! pattern ) 제외 예상 검색은 pattern 과 일치하지 않는 문자열이 시작하는 위치에서 검색할 문자열을 찾습니다. 이것은 검색한 문자열을 나중에 사용할 수 있도록 캡처하지 않는 비캡처 검색입니다. 예를 들어, "Windows(?!95|98|NT|2000)"는 "Windows 3.1"의 "Windows"는 찾지만 "Windows 2000"의 "Windows"는 찾지 않습니다. 예상 검색은 검색할 문자열을 찾은 후 예상 검색 문자열을 구성하는 문자 다음부터가 아니라 마지막으로 검색한 문자열 바로 다음부터 찾기 시작합니다.
x | y x 또는 y 를 찾습니다. 예를 들어, "z|food"는 "z" 또는 "food"를 찾습니다. "(z|f)ood"는 "zood" 또는 "food"를 찾습니다.
[ xyz ] 문자 집합입니다. 괄호 안의 문자 중 하나를 찾습니다. 예를 들어, "[abc]"는 "plain"의 "a"를 찾습니다.
[^ xyz ] 제외 문자 집합입니다. 괄호 밖의 문자 중 하나를 찾습니다. 예를 들어, "[^abc]"는 "plain"의 "p"를 찾습니다.
[ a-z ] 문자 범위입니다. 지정한 범위 안의 문자를 찾습니다. 예를 들어, "[a-z]"는 "a"부터 "z" 사이의 모든 소문자를 찾습니다.
[^ a-z ] 제외 문자 범위입니다. 지정된 범위 밖의 문자를 찾습니다. 예를 들어, "[^a-z]"는 "a"부터 "z" 사이에 없는 모든 문자를 찾습니다.
\b 단어의 경계, 즉 단어와 공백 사이의 위치를 찾습니다. 예를 들어, "er\b"는 "never"의 "er"는 찾지만 "verb"의 "er"는 찾지 않습니다.
\B 단어의 비경계를 찾습니다. "er\B"는 "verb"의 "er"는 찾지만 "never"의 "er"는 찾지 않습니다.
\c x X 가 나타내는 제어 문자를 찾습니다. 예를 들어, \cM은 Control-M 즉, 캐리지 리턴 문자를 찾습니다. x 값은 A-Z 또는 a-z의 범위 안에 있어야 합니다. 그렇지 않으면 c는 리터럴 "c" 문자로 간주됩니다.
\d 숫자 문자를 찾습니다. [0-9]와 같습니다.
\D 비숫자 문자를 찾습니다. [^0-9]와 같습니다.
\f 폼피드 문자를 찾습니다. \x0c와 \cL과 같습니다.
\n 줄 바꿈 문자를 찾습니다. \x0a와 \cJ와 같습니다.
\r 캐리지 리턴 문자를 찾습니다. \x0d와 \cM과 같습니다.
\s 공백, 탭, 폼피드 등의 공백을 찾습니다. "[ \f\n\r\t\v]"와 같습니다.
\S 공백이 아닌 문자를 찾습니다. "[^ \f\n\r\t\v]"와 같습니다.
\t 탭 문자를 찾습니다. \x09와 \cI와 같습니다.
\v 수직 탭 문자를 찾습니다. \x0b와 \cK와 같습니다.
\w 밑줄을 포함한 모든 단어 문자를 찾습니다. "[A-Za-z0-9_]"와 같습니다.
\W 모든 비단어 문자를 찾습니다. "[^A-Za-z0-9_]"와 같습니다.
\x n n 을 찾습니다. 여기서 n 은 16진수 이스케이프 값입니다. 16진수 이스케이프 값은 정확히 두 자리여야 합니다. 예를 들어, '\x41'은 "A"를 찾고 '\x041'은 '\x04'와 "1"과 같습니다. 정규식에서 ASCII 코드를 사용할 수 있습니다.
\ num num 을 찾습니다. 여기서 num 은 양의 정수입니다. 캡처한 문자열에 대한 역참조입니다. 예를 들어, '(.)\1'은 연속적으로 나오는 동일한 문자 두 개를 찾습니다.
\ n 8진수 이스케이프 값이나 역참조를 나타냅니다. \ n 앞에 최소한 n개의 캡처된 부분식이 나왔다면 n 은 역참조입니다. 그렇지 않은 경우 n 이 0에서 7 사이의 8진수이면 n 은 8진수 이스케이프 값입니다.
\ nm 8진수 이스케이프 값이나 역참조를 나타냅니다. \ nm 앞에 최소한 nm개의 캡처된 부분식이 나왔다면 nm 은 역참조입니다. \ nm 앞에 최소한 n개의 캡처가 나왔다면 n 은 역참조이고 뒤에는 리터럴 m이 옵니다. 이 두 경우가 아닐 때 n과 m이 0에서 7 사이의 8진수이면 \ nm 은 8진수 이스케이프 값 nm을 찾습니다.
\ nml n 이 0에서 3 사이의 8진수이고 m 과 l 이 0에서 7 사이의 8진수면 8진수 이스케이프 값 nml 을 찾습니다.
\u n n 은 4 자리의 16진수로 표현된 유니코드 문자입니다. 예를 들어, \u00A9는 저작권 기호(©)를 찾습니다.


+ Recent posts