2019년 12월 18일 수요일

[C#] 정규식 (Regula-Expressions) 사용하기

공식메뉴얼



문자열에서 특정패턴에 맞는 문자열을 추출하고 싶을때 유용하다.

예를 들어 아래 공식 예제를 풀이해보자면

private static void DumpHRefs(string inputString) 
{
    Match m;
    string HRefPattern = @"href\s*=\s*(?:[""'](?<1>[^""']*)[""']|(?<1>\S+))";
  
    try
    {
        m = Regex.Match(inputString, HRefPattern, 
                        RegexOptions.IgnoreCase | RegexOptions.Compiled, 
                        TimeSpan.FromSeconds(1));
        while (m.Success)
        {
            Console.WriteLine("Found href " + m.Groups[1] + " at " 
               + m.Groups[1].Index);
            m = m.NextMatch();
        }
    }
    catch (RegexMatchTimeoutException)
    {
        Console.WriteLine("The matching operation timed out.");
    }
}

public static void Main()
{
    string inputString = "My favorite web sites include:</P>" +
                         "<A HREF=\"http://msdn2.microsoft.com\">" +
                         "MSDN Home Page</A></P>" +
                         "<A HREF=\"http://www.microsoft.com\">" +
                         "Microsoft Corporation Home Page</A></P>" +
                         "<A HREF=\"http://blogs.msdn.com/bclteam\">" +
                         ".NET Base Class Library blog</A></P>";
    DumpHRefs(inputString);

}
// The example displays the following output:
//       Found href http://msdn2.microsoft.com at 43
//       Found href http://www.microsoft.com at 102
//       Found href http://blogs.msdn.com/bclteam at 176

목표는 주어진 문자열에서 url 주소를 가져오는 것

주어진 문자열
My favorite web sites include:</P>
<A HREF="http://msdn2.microsoft.com">
MSDN Home Page</A></P>
<A HREF="http://www.microsoft.com">
Microsoft Corporation Home Page</A></P>
<A HREF="http://blogs.msdn.com/bclteam">
.NET Base Class Library blog</A></P>
여기서 각 <A HREF=" 를 찾아 "> 전까지의 문자열을 추출하면 된다.

이에 대한 정규식은
href\s*=\s*(?:[""'](?<1>[^""']*)[""']|(?<1>\S+))

분석하자면
href : 문자열"href"와 일치함을 의미한다. 원래는 대소문자를 구분하기에 위의 예제에 맞추려면 대문자 HREF가 되어야 하지만 Match() 함수에 IgnoreCase 옵션을 넣었기에 소문자도 ok.

\s : 's'는 space. 즉 공백문자와 일치함을 의미한다.

* : 앞에 위치한 문자의 0~n번 반복을 의미한다. 즉 \s* 는 공백문자가 0~n번 반복됨을 의미한다.

= : 문자'='와 일치함을 의미한다.

(?:[""'](?<1>[^""']*)[""']|(?<1>\S+)) : 여러 식이 조합되어 있다. (?:expression1|expression2)  형태로 이해하면 된다.

(expression) : 그룹화 캡쳐를 의미한다. 괄호 내부에 선언된 식에 의해 추출된 데이터를 따로 캡쳐해둔다. 캡쳐된 데이터는
Match m = Regex.Match(input, expression);
string capture1 = m.Groups[1].Value;
string capture2 = m.Groups[2].Value;
이런 식으로 사용할 수 있다.

(?:expression) : ?: 는 그룹화는 하지만 캡쳐는 안하겠다는 의미이다. 캡쳐안하는데 그룹화를 하는 이유는 | 를 사용하기 위함이다.
(?:expression1|expression2) : expression1 와 일치하거나 expression2 와 일치하는 문자열을 찾는다.

expression1 [""'](?<1>[^""']*)[""'] 를 분석하자면

[""'] : 본래는 ["'] 인데 코드문법상 " 를 "" 로 표기한 것. [ ] 는 괄호 내부 문자들 중에 하나라도 있다면 일치함을 의미한다. 예를 들어 [abc]는 'a','b','c' 중 하나라도 있다면 일치함을 의미한다. 즉 ["'] 는 " 또는 ' 와 일치함을 의미한다.

(?<1>[^""']*) : (?<1>expression)  는 캡쳐된 데이터를 배열 1번째에 할당하겠다는 의미이다. [^""'] 는 [""']  의 반대이다. ^ 는 뒤에 위치한 문자를 제외한 나머지를 의미하기에 [^""'] 는 " 와 ' 를 제외한 아무 문자와 일치함을 의미한다.

결국 expression1 [""'](?<1>[^""']*)[""'] 은 ""와'를제외한아무문자열" 또는 '"와'를제외한아무문자열' 를 의미한다. 그리고 배열1번째에 캡쳐되는 문자열은 "와'를제외한아무문자열  이 된다.

expression2 (?<1>\S+)를 분석하자면
\S : \s 의 반대다. 공백이 아닌 문자와 일치함을 의미한다.

+ : * 가 앞의 위치한 문자의 0~n번 반복을 의미한다면, + 는 앞의 위치한 문자의 1~n번 반복을 의미한다.

결국 expression2 (?<1>\S+)공백이아닌아무문자열 을 의미하며 이를 배열1번째에 캡쳐하겠다는 의미이다.

정리하자면
정규식 href\s*=\s*(?:[""'](?<1>[^""']*)[""']|(?<1>\S+)) 는 주어진 문자열에서

href(공백0개이상)=(공백0개이상)(("또는')("와'를제외한아무문자열)("또는')or(공백이아닌아무문자열)) 를 찾는다.

예1) <a href   = 'urlstring'> 에서 정규식과 일치하는 문자열은
href   = 'urlstring' 이고 캡쳐된 데이터는 urlstring 이다.

예2) hdsaghref=  "sg aijgsli 2357*2"gefa 에서 정규식과 일치하는 문자열은
href=  "sg aijgsli 2357*2" 이고 캡쳐된 데이터는 sg aijgsli 2357*2 이다.

사실 위의 ms 예제코드만 가지고 보자면
정규식은 href\s*=\s*[""'](?<1>[^""']*)[""'] 으로 충분하다.

예제 코드에서 href\s*=\s*(?:[""'](?<1>[^""']*)[""']|(?<1>\S+))를 사용한 이유는
url 이 "또는' 문자로 안 묶여있는 패턴 (예: href=http://www.microsoft.com  ) 을 찾기 위함인 것 같다.


c# 뿐 아니라 java난 python 등 대부분의 언어에서 정규식을 지원하고 있다.
사용법은 거의 비슷하나 언어마다 약간의 차이가 있으니 확인하도록 하자.


댓글 없음:

댓글 쓰기