RegExp(regular express): 정규식

Set oRegExp = New RegExp 

이와 같은 방법으로 생성된 RegExp 객체는 사용이 모두 끝나고 나면 언제나처럼 Nothing 키워드를 사용하여 참조를 해제해줘야 한다.

Set oRegExp = Nothing 

이제 RegExp 객체를 생성하는 방법을 알았으므로 RegExp 객체에서 지원해주는 세 개의 메소드 (Method) 들 중 먼저 비교적 간단한 Test() 메소드에 대해서 알아본다. Test() 메소드는 지정한 문자열 내에 지정한 정규 표현식 패턴이 존재하는지 여부를 Boolean 형으로, 다시 말하자면 True 나 False 의 형태로 리턴해준다.

여기에서 주의해야 할 점은 Test() 메소드는 지정한 정규 표현식 패턴이 대상 문자열의 내부에 존재하기만하면 무조건 True 를 리턴해주므로, 아무리 Test() Method 의 실행 결과가 True 인 경우라도 '정규 표현식 패턴 = 대상 문자열' 이라는 등식이 항상 성립하는 것은 아니라는 것이다. 따라서 보다 엄격한 패턴 검사를 하기 위해서는 지난번 글에서 우리가 만들어 보았던 핸드폰 번호의 정규 표현식 패턴에서와 같이 '^' 또는 '$' 와 같은 메타 문자들을 적절히 사용하여 정규 표현식 패턴 자체를 그 당시의 요구 조건에 맞도록 충실하게 작성해야만 한다.

다음의 코드는 ASP 상에서 Test() 메소드를 사용하기 편리하도록 필자가 미리 함수로 만들어 놓은 것이다. 설명의 편의상 오류 처리는 생략했으므로 필요하신 분들은 각자 상황에 맞게 수정하여 사용하는 편이 좋을 것이다.

<%

'******************************************************
'*
'* Public Function RegExpTest(Patrn, TestStr)
'*
'*    RegExp.Test() 메소드를 일반화한 함수
'*
'******************************************************


Public Function RegExpTest(Patrn, TestStr)

Dim ObjRegExp

On Error Resume Next    

Set ObjRegExp = New RegExp

ObjRegExp.Pattern = Patrn               '** 정규 표현식 패턴
ObjRegExp.IgnoreCase = True             '** 대.소문자 구분 안함

RegExpTest = ObjRegExp.Test(TestStr)

Set ObjRegExp = Nothing

End Function

%>

일단 이 글의 맨 처음에서 설명한 바와 같이 New 연산자를 사용하여 RegExp 의 객체를 만든다. 그 다음에는 RegExp 객체가 가지고 있는 세 개의 프로퍼티 (Property) 인, Pattern, Global, IgnoreCase 의 값을 상황에 알맞게 설정해 주어야 한다. 그러나 이 중에서 Global 프로퍼티는 그 설정값에 상관없이 Test() 메소드의 실행 결과에는 하등 영향을 미치지 않으므로 위의 함수에서 볼 수 있는 것처럼 Test() 메소드의 사용시에는 무시하는 것이 일반적이다.

세 개의 프로퍼티 중에서 가장 중요한 프로퍼티라고 말할 수 있는 Pattern 프로퍼티에는 문자열 형식의 정규 표현식 패턴을 설정한다. 그리고 IgnoreCase 프로퍼티에는 정규 표현식 패턴 검색시 대.소문자를 구분할지 여부를 Boolean 형식으로 설정한다. 이처럼 필요한 프로퍼티값들을 모두 설정했으면 마지막으로 정규 표현식 검색의 대상이 되는 대상 문자열을 인자로하여 Test() 메소드를 호출하기만 하면 된다.

따라서, 위의 함수를 사용하여 임의의 핸드폰 번호, '011-1234-5678' 이 올바른 핸드폰 번호인지를 확인하려면 다음의 코드와 같이 사용하면 된다.

Response.Write RegExpTest("^0(?:11|16|17|18|19)-(?:\d{3}|\d{4})-\d{4}$", "011-1234-5678") 

아마, 이 코드를 직접 실행시켜 본다면 True 가 출력될 것이다. 이 말은 곧 '011-1234-5678' 이라는 핸드폰 번호는 우리가 지정한 정규 표현식 패턴과 일치하는 올바른 형태의 핸드폰 번호라는 의미가 된다. 이처럼 정규 표현식 패턴을 작성하는 작업에만 조금 익숙해지면 이를 활용하기 위한 코드 그 자체는 매우 쉽고 게다가 일정한 패턴이 있기까지 하므로 여러가지 상황에서 매우 편리하게 활용할 수 있다.

예를 들어 다음의 코드는 지정한 문자열이 'yyyy-mm-dd' 형식으로 입력된 날짜 문자열인지를 검사하고 결과를 출력한다.

Response.Write RegExpTest("^\d{4}-\d{2}-\d{2}$", "2002-01-31") 

물론 이 밖에도 활용예는 충분히 많다. 아마도 일반적으로 가장 생각하기 쉬운 것이 E-Mail 주소나 URL 등의 사례들일 것이다. 하지만 사실 이와 같은 응용이 보다 효율적으로 사용되려면 Test() 메소드가 실행되는 위치가 클라이언트 측, 즉 웹 브라우저인 편이 휠씬 더 좋다. 그러나 이는 현실적으로 잡다한 많은 제약이 존재한다. 일단 Netscape 에서는 VBScript 자체가 지원되지 않으며 JavaScript 도 정규 표현식을 지원하지 않는다.

물론 Internet Explorer 상에서 실행되는 JavaScript 에서는 정규 표현식이 지원된다. 그러나 한 가지 분명히 해 두어야 할 것은 Internet Explorer 상에서 실행되는 JavaScript 는 엄밀히 말해서 JavaScript 가 아니라 JScript 라는 사실이며 이는 역시 VBScript 에서와 마찮가지로 Microsoft Windows Script 5.X 로부터 지원되는 것이므로 클라이언트에 설치된 Scripting Engine 의 버전에 영향을 받는다는 뜻이 된다는 것이다.

따라서, 클라이언트상의 웹 브라우저의 버전이나 종류, 혹은 Target Device 의 종류 등에 관계없이 언제나 정규 표현식 기능을 일관되게 처리해주기 위한 최선의 방법은, 정규 표현식 처리 그 자체는 서버 측에서 실행하고 그 결과와 렌더링 정보만 클라이언트의 상황에 맞도록 처리하여 Posting 해주는 것인데, 바로 이와 같은 아이디어를 현실 세계에서 구현해 놓은 것이 ASP.NET 의 RegularExpressionValidator Control 인 것이다.

이번에는 Execute() 메소드에 관해서 알아보자. Execute() 메소드는 지정한 대상 문자열 내에서 지정한 정규 표현식 패턴을 만족하는 모든 문자열들의 집합을 Matches Collection 형태로 리턴해준다. 그리고 이 Collection 은 Match 라는 객체 형식의 Item 을 검색된 문자열의 갯수만큼 가지고 있게 되는데 우리는 이 Match 객체를 사용하여 검색된 문자열들의 위치, 길이, 값에 대한 정보에 접근할 수 있다. 간단하게 말해서 매우 강력한 검색 기능을 제공해주는 것이다.

지금까지의 경우 VBScript 를 사용하여 어떤 임의의 문자열 내부에 임의의 검색 대상 문자열이 몇 개나 존재하는지, 문자열의 위치는 어디에서부터 어디까지인지 등에 관한 정보를 얻기 위해서는 루프문을 돌면서 문자열의 처음부터 끝까지 InStr() 함수를 사용하는 등의 방법외에는 대안이 없었다. 그리고 지금까지 계속 강조해 왔지만 이런 방식은 융통성이 부족할 뿐더러 검색 결과에 대한 정보를 저장해두기 위해서는 별도의 처리 과정을 관리해야 하는 번거로움이 있다.

다음의 코드는 Test() 메소드의 경우와 마찮가지로 ASP 상에서 Execute() 메소드를 사용하기 편리하도록 필자가 미리 함수로 만들어 놓은 것이다. 역시 필요하신 분들은 각자 상황에 맞게 수정하여 사용하면 된다.

<%

'******************************************************
'*
'* Public Function RegExpExec(Patrn, TestStr)
'*
'*    RegExp.Execute() 메소드를 일반화한 함수
'*
'******************************************************


Public Function RegExpExec(Patrn, TestStr)

Dim ObjRegExp

On Error Resume Next    

Set ObjRegExp = New RegExp

ObjRegExp.Pattern = Patrn               '** 정규 표현식 패턴
ObjRegExp.Global = True                 '** 문자열 전체를 검색함
ObjRegExp.IgnoreCase = True             '** 대.소문자 구분 안함

Set RegExpExec = ObjRegExp.Execute(TestStr)

Set ObjRegExp = Nothing

End Function

%>

위의 코드를 보면 바로 느낄 수 있겠지만 Execute() 메소드를 사용하는 방법이나 Test() 메소드를 사용하는 방법이나 양자간에 그다지 차이점이 없다는 것을 알 수 있다. 다만 Execute() 메소드는 Test() 메소드와는 달리 실행 결과를 Collection 객체 형식으로 리턴해주므로, 파란색으로 강조해 놓은 부분에서처럼 Set 문을 사용해야 한다는 점에 유의하기 바란다.

또한, 이번엔 Global 프로퍼티가 사용되고 있다는 것을 알 수 있는데, 이 프로퍼티는 정규 표현식 패턴 검색시 검색 대상 문자열 내에 존재하는 정규 표현식 패턴과 일치하는 모든 문자열을 찾을 것인지, 아니면 가장 첫 번째로 찾은 문자열만 리턴하고 검색을 중단할 것인지를 결정한다.

위의 코드에서처럼 Global 프로퍼티가 True 로 설정되면 대상 문자열 내에 존재하는 모든 정규 표현식 패턴 문자열을 대상으로 정규 표현식 검색이 이루어지며, 그 반대로 False 로 설정되면 가장 첫 번째로 찾은 정규 표현식 패턴 문자열만을 리턴한 상태로 검색이 중지된다. 그러나 두 경우 모두 Execute() 메소드로부터 리턴되는 것은 역시 Matches Collection 이므로 특별히 두 경우의 코드를 따로따로 작성할 필요는 없다.

이제 실제로 이 함수를 사용해보자. 다음의 문장은 필자가 설명을 위하여 임의로 야후의 백과 사전 서비스에서 '메모리' 라는 단어를 검색하여 나온 결과 중 아무 부분이나 가져와본 것이다. 이 문장에서 '저장' 이라는 단어가 모두 네 차례 나온다는 것을 확인할 수 있다.

다음과 같이 코드를 작성하면 이 문장에 존재하는 '저장' 이라는 문자열을 모두 찾을 수 있다. 이 때, 위에서 작성한 RegExpExec() 함수를 아래의 코드가 작성된 ASP 파일에 Include 하거나 동일한 ASP 파일에 복사하여 붙여넣기 등의 방법을 사용해 첨부하여 RegExpExec() 함수를 자유롭게 사용할 수 있는 상황이라고 전제한다.


<%

Dim Test_String                   '** 검색 대상 문장을 담을 변수
Dim Result_Match                  '** Match 를 참조할 변수
Dim Result_Matches_Collection     '** Matches Collection 을 참조할 변수


'** 검색 대상 문장
Test_String = "컴퓨터가 자료를 처리하려면 그 자료와 이를 처리할 수 있는 프로그" &amp; _
        "램을 저장할 수 있는 곳이 있어야 한다. 처리장치로 프로그램을 불러" &amp; _
        "내어 처리하는 과정에서 일시적인 저장이 필요하기도 하고, 또한 지" &amp; _
        "금 바로 처리장치가 필요로 하지 않는 자료와 프로그램을 얼마 동안 " &amp; _
        "보존했다가 필요할 때 꺼내서 사용할 수 있는 저장장치도 필요하다. " &amp; _
        "이러한 저장 기능을 담당하는 장치를 통털어 기억장치라 일컫는다."

        
'** RegExpExec() 함수 실행  
Set Result_Matches_Collection = RegExpExec("저장", Test_String)


'** 루프를 돌면서 정보를 출력한다.  
For Each Result_Match In Result_Matches_Collection

With Response
.Write "<font size="2">;"
.Write "문자열의 첫 글자의 Index 위치 :: " &amp; Result_Match.FirstIndex &amp; "<br>;"
.Write "문자열의 길이 :: " &amp; Result_Match.Length &amp; "<br>;"
.Write "문자열의 내용 :: " &amp; Result_Match.Value &amp; "<br>;"
.Write "</font>;<br>;"
.Flush
End With

Next  

%>

이 코드를 직접 실행시켜 보면 다음과 같이 문장 내부에 존재하는 '저장' 이라는 문자열 네 개에 관한 정보가 모두 출력되는 것을 확인할 수 있을 것이다.

문자열의 첫 글자의 Index 위치 :: 39
문자열의 길이 :: 2
문자열의 내용 :: 저장

문자열의 첫 글자의 Index 위치 :: 91
문자열의 길이 :: 2
문자열의 내용 :: 저장

문자열의 첫 글자의 Index 위치 :: 171
문자열의 길이 :: 2
문자열의 내용 :: 저장

문자열의 첫 글자의 Index 위치 :: 187
문자열의 길이 :: 2
문자열의 내용 :: 저장

간단하나마 이 정도의 정보를 가지고 있다면 실제 프로그래밍 작업시에도 충분히 이 정보에 기반하여 대부분의 후속 작업을 무난히 진행할 수 있을 것이다. 그렇다면, 이번엔 위의 코드를 한 줄씩 차분히 살펴보도록 하자.

우선 눈에 띄는 점은 RegExpExec() 함수의 실행 결과를 받을 때 또다시 Set 문이 사용된다는 것이다. 이것은 지극히 당연한 사항이므로 더이상 설명하지 않도록 하겠다. 그 다음으로 눈에 띄는 점은 이 코드에서 사용되고 있는 정규 표현식 패턴인데 '저장' 이라는 매우 간단한 형태를 가지고 있다. 잠시 후에 이 패턴을 약간씩 수정하여 그 때 마다의 검색 결과의 변화를 살펴보기로 한다.

가장 흥미로운 부분은 For Each ... Next 문을 사용하여 검색 결과에 대한 정보들을 표시하는 부분인데, 위에서 얘기했던 것처럼 Execute() 메소드로 검색된 각각의 문자열들에 대한 정보는 검색된 문자열 한 개마다 각각 그에 대응하는 Match 객체 한 개씩에 그 정보가 저장되어 Matches Collection 으로 리턴된다. 이처럼 Match 객체 그 자체는 Execute() 메소드의 실행의 결과로서만 생성이 되며 직접 생성하는 것은 불가능하다. 따라서 만약 위의 코드에서 검색된 문자열이 한 개도 없다면 Matches Collection 형인 Result_Matches_Collection 객체 변수의 프로퍼티, Count 에는 값 0 이 들어 있을 것이다. 그러나 위의 코드의 경우 검색된 문자열은 모두 네 개 이므로 Count 프로퍼티에는 값 4 가 들어있는데 바로 이 값을 참고하여 루프문의 순환 횟수를 결정할 수 있는 것이다.

다만 위의 코드에서는 그보다 좀 더 간편한 방법인 For Each ... Next 문을 사용하고 있다는 것을 알 수 있는데, 이것은 각자의 취향이나 그때 그때의 상황에 맞추어 두 가지 방법 중 아무 것이나 선택하여 사용하면 된다.

Match 객체에는 모두 세 개의 프로퍼티가 존재한다. 그 중 FirstIndex 프로퍼티는 검색된 문자열의 가장 첫 글자가 위치하는 검색 대상 문자 내에서의 인덱스 (Index) 값으로서 그 값은 0 부터 시작한다. Length 프로퍼티는 검색된 문자열의 길이값을 가지고 있으며, Value 프로퍼티는 검색된 문자열 그 자체를 값으로 가지고 있다. 이 Match 객체의 세 가지 프로퍼티는 모두 읽기 전용이라는 점에 주의하기 바란다.

그렇다면, 이번에는 이 코드에서 사용된 정규 표현식의 패턴을 다음의 코드와 같이 약간 수정하여 그 결과를 살펴보도록 하자.

Set Result_Matches_Collection = RegExpExec("저장\S+", Test_String) 

메타 문자 '\S' 는 공백이 아닌 문자를 의미한다. 그리고, 메타 문자 '+' 는 '+' 앞의 문자가 적어도 한 번 이상 반복된다는 것을 의미한다. 따라서, 그 실행 결과는 다음과 같다. '저장' 이라는 문자열 다음에 바로 공백 문자가 들어간 네 번째 '저장' 은 출력되지 않았다는 것을 알 수 있다.

문자열의 첫 글자의 Index 위치 :: 39
문자열의 길이 :: 3
문자열의 내용 :: 저장할

문자열의 첫 글자의 Index 위치 :: 91
문자열의 길이 :: 3
문자열의 내용 :: 저장이

문자열의 첫 글자의 Index 위치 :: 171
문자열의 길이 :: 5
문자열의 내용 :: 저장장치도

이번엔 아예 정규 표현식 검색 패턴 자체를 바꿔보도록 하자. 검색 대상 문장 내에는 '~~장치' 라는 문자열이 다음과 같이 모두 세 종류, 네 개가 존재하고 있다.

컴퓨터가 자료를 처리하려면 그 자료와 이를 
처리할 수 있는 프로그램을 저장할 수 있는 곳이 있어야 한다. 처리장치로 프로그램을 불러내어 처리하는 과정에서 
일시적인 저장이 필요하기도 하고, 또한 지금 바로 처리장치가 필요로 하지 않는 자료와 프로그램을 얼마 동안 보존했다가 
필요할 때 꺼내서 사용할 수 있는 저장장치도 필요하다. 이러한 저장 기능을 담당하는 장치를 통털어 기억장치라 일컫는다. 

이 문장 중 '저장장치' 와 '기억장치' 라는 문자열만을 검색하고 싶다면, 다음과 같은 정규 표현식 패턴을 사용하면 된다. 이와 같이 하면 '처리장치' 라는 문자열은 검색되지 않는다.

Set Result_Matches_Collection = RegExpExec("(?:저장|기억)장치",  Test_String) 

다음은 위의 코드의 실행 결과이다. 우리가 원하는 대로 '처리장치' 라는 문자열은 검색되지 않았다는 것을 확인할 수 있다.

문자열의 첫 글자의 Index 위치 :: 171
문자열의 길이 :: 4
문자열의 내용 :: 저장장치

문자열의 첫 글자의 Index 위치 :: 207
문자열의 길이 :: 4
문자열의 내용 :: 기억장치

이번에는 약간 특별한 경우를 살펴보기로 한다. 정규 표현식 패턴에 사용되는 메타 문자 중에는, '(' 와 ')' 의 쌍, 즉 다시 말해서 소괄호가 있는데 이 메타 문자는 약간 특별한 기능을 가지고 있다. 예를 들어서 다음과 같은 정규 표현식 패턴을 사용하여 검색을 한다고 가정해보자.

(저장|기억|처리)장치

이제 이 정도 수준의 정규 표현식 패턴에는 익숙할 것이므로 그 의미도 쉽게 파악할 수 있을 것이다. 이 패턴은 '저장장치', '기억장치', '처리장치' 라는 문자열을 의미한다.
그런데, 지난번 글에서도 한 번 언급했지만 대상 문자열이 정규 표현식 패턴과 일치할 경우 소괄호 내부에 감싸인 문자열 부분은 메모리상에 미리 지정된 이름의 변수로 차례차례 저장이 된다 즉, 일반적인 논리식에서와 같은 논리 연산의 연산 그룹을 정해주는 기능 외에도 해당 요소를 별도로 저장해 두는 기능을 가지고 있는 것이다.
이 기능은 사실 Execute() 메소드에서 보다는 Replace() 메소드에서 더욱 유용하게 사용되는데, 이 부분에 관해서는 Replace() 메소드에 관해 설명할 때 더 상세하게 얘기하도록 하겠다.
아무튼, 이렇게 별도로 저장된 부분 일치 문자열은 추후에 재사용을 위해서 접근이 가능한데 다음의 코드에서 그 사례를 살펴볼 수 있다. 이 코드는 바로 위에서 예로 들었던 코드를 일부 수정한 것이다. 일단 정규 표현식 패턴을 '(저장|기억|처리)장치' 로 바꾸고, SubMatches 라는 Collection 을 이용하여 부분 일치 문자열 요소의 정보를 출력하는 코드를 한 줄 추가했다. 파란색으로 강조한 부분을 살펴보기 바란다.

<%

Dim Test_String                   '** 검색 대상 문장을 담을 변수
Dim Result_Match                  '** Match 를 참조할 변수
Dim Result_Matches_Collection     '** Matches Collection 을 참조할 변수


'** 검색 대상 문장
Test_String = "컴퓨터가 자료를 처리하려면 그 자료와 이를 처리할 수 있는 프로그" &amp; _
        "램을 저장할 수 있는 곳이 있어야 한다. 처리장치로 프로그램을 불러" &amp; _
        "내어 처리하는 과정에서 일시적인 저장이 필요하기도 하고, 또한 지" &amp; _
        "금 바로 처리장치가 필요로 하지 않는 자료와 프로그램을 얼마 동안 " &amp; _
        "보존했다가 필요할 때 꺼내서 사용할 수 있는 저장장치도 필요하다. " &amp; _
        "이러한 저장 기능을 담당하는 장치를 통털어 기억장치라 일컫는다."

        
'** RegExpExec() 함수 실행  
Set Result_Matches_Collection = RegExpExec("(저장|기억|처리)장치", Test_String)


'** 루프를 돌면서 정보를 출력한다.  
For Each Result_Match In Result_Matches_Collection

With Response
.Write "<font size="2">;"
.Write "문자열의 첫 글자의 Index 위치 :: " &amp; Result_Match.FirstIndex &amp; "<br>;"
.Write "문자열의 길이 :: " &amp; Result_Match.Length &amp; "<br>;"
.Write "문자열의 내용 :: " &amp; Result_Match.Value &amp; "<br>;"
.Write "부분 일치 문자열의 내용 :: " &amp; Result_Match.SubMatches(0) &amp; "<br>;"
.Write "</font>;<br>;"
.Flush
End With

Next  

%>

다음은, 이 코드를 실행한 결과이다. 각 항목의 네 번째 줄을 자세히 살펴보기 바란다.

문자열의 첫 글자의 Index 위치 :: 59
문자열의 길이 :: 4
문자열의 내용 :: 처리장치
부분 일치 문자열의 내용 :: 처리

문자열의 첫 글자의 Index 위치 :: 114
문자열의 길이 :: 4
문자열의 내용 :: 처리장치
부분 일치 문자열의 내용 :: 처리

문자열의 첫 글자의 Index 위치 :: 171
문자열의 길이 :: 4
문자열의 내용 :: 저장장치
부분 일치 문자열의 내용 :: 저장

문자열의 첫 글자의 Index 위치 :: 207
문자열의 길이 :: 4
문자열의 내용 :: 기억장치
부분 일치 문자열의 내용 :: 기억

위의 코드에서 처음으로 사용된 SubMatches Collection 의 각 Item 에는 각각의 부분 일치 문자열들이 들어있는데, Collection 이라는 점에서도 눈치챌 수 있겠지만 부분 일치 문자열의 갯수에는 상식적인 조건하에서 제한이 없고 그 갯수는 당연히 정규 표현식 패턴에 사용된 소괄호의 갯수와 일치한다. 또한 SubMatches Collection 도 For Each ... Next 문이나 Count 프로퍼티의 사용이 가능한 일반적인 Collection 인데, 다만 읽기 전용이므로 수정이 불가능하다는 특징을 가지고 있다.

그리고, 또 한가지 중요한 점은 SubMatches Collection 은 VBScript 5.5 에서부터 지원된다는 것이다. 따라서 경우에 따라 사용이 불가능할 수도 있다. 그러나, 이 제약은 Microsoft Windows Script 5.6 을 설치하기만 하면 간단하게 해결되므로 그다지 걱정할 만한 일은 아닌 것이다.

그렇다면 이와 같은 기능이 실제로 우리에게 어떤 도움을 줄 수 있는 것일까? 예를 들면 이런 경우도 있을 수 있다. 우리가 필요한 임의의 패턴을 지닌 문자열들을 검색하고 그 결과 문자열들의 부분 요소를 해체하여 분석하거나 그 중 일부 요소만 재사용할 필요가 있을 때가 있다. 이를테면 URL 을 해체하는 것과 같은 경우다. 이 사례는 'Windows Script V5.6 온라인 설명서' 에서 발췌한 것이다.

http://msdn.microsoft.com:80/scripting/default.htm 

이 URL 은 임의로 선택된 것이다. 이 URL 문장을 해체하여 각각의 요소를 얻어낸다고 생각해보자. 이 때, 다음과 같은 정규 표현식 패턴이 생성 가능하다.

(\w+):\/\/([^/:]+)(:\d*)?([^# ]*) 

이 정규 표현식 패턴에는 소괄호가 전부 네 번 사용되었다. 따라서 이 패턴을 사용하여 대상 URL 을 해체한 결과는 다음과 같다. 별반 큰 노력없이도 만족할 만한 결과를 얻을 수 있다는 것을 깨달을 수 있을 것이다. 하지만 그보다 더 중요한 사실은 이와 같이 잘 구성된 정규 표현식 패턴은 비슷한 대부분의 경우를 포괄할 수 있으므로 재사용이 매우 용이하다는 점이다.

SubMatches(0) 이 가지고 있는 값 → http
SubMatches(1) 이 가지고 있는 값 → msdn.microsoft.com
SubMatches(2) 이 가지고 있는 값 → :80
SubMatches(3) 이 가지고 있는 값 → /scripting/default.htm

그러나 이렇게 부분 일치 문자열의 정보를 항상 별도의 변수에 저장해 두는 것이 언제나 좋은 것만은 아니다. 사용하지도 않는 정보를 위해 메모리를 낭비할 필요는 없다는 점에는 누구나 동의할 것이다. 그래서, 정규 표현식에서 제공해 주는 것이 '(?:)' 이다. 위에서 사용된 정규 표현식 패턴, '(저장|기억|처리)장치' 의 경우 좀더 효율적인 패턴 '(?:저장|기억|처리)장치' 으로 변경이 가능하다. 이렇게 하면 OR 논리 연산자의 기능을 가진 메타 문자 파이프 (|) 에 대한 연산 그룹을 설정하는 기능만을 가질뿐, 별도의 변수에 부분 일치 문자열을 저장하는 기능은 실행되지 않는다. 물론 당연한 얘기지만 이 경우에는 SubMatches Collection 을 사용할 수 없다. 그 밖에도 소괄호와 관련된 메타 문자로는 '(?=)' 와 '(?!)' 가 있는데, 이에 관해서는 각자 알아보기 바란다.

필자의 예상과는 달리 이번 글에서도 그 분량이 매우 길어졌다. 그래서, 어쩔수 없이 글을 이 정도에서 마무리 짓고, 정규 표현식에 관한 글을 한 회정도 더 작성해야 할 것 같다. 다음글에서는 정규 표현식의 핵심이라고도 말할 수 있는 Replace() 메소드에 관해서 얘기하도록 하겠다. 그리고 간단하게나마 JavaScript 를 사용하여 Client Side 에서 정규 표현식을 사용하는 방법에 대해서도 얘기하려고 한다. 비록 그 사용이 제한적이긴 하지만 인트라넷 (Intranet) 과 같은 특수한 환경에서는 가끔씩 발생하는 독특한 상황에서 돌파구가 되어줄 수도 있을 것이다. 宋

Table of contents 목차

평점을 남겨주세요
평점 : 2.5
총 투표수 : 1