상세 컨텐츠

본문 제목

닷넷(.NET)에서의 정규표현식 사용하기 Part 1

C#

by 탑~! 2014. 1. 3. 08:40

본문

응용프로그램을 개발할 때 사용자가 입력한 값이 응용프로그램이 원하는 형식과 구조인지를 파악하기 위해 유효성 검사를 수행합니다. 예를 들면, 사용자가 올바른 전화번호, 주민등록번호, 이메일 주소를 입력했는지 확인할 필요가 있습니다. 닷넷 뿐만 아니라 대부분의 프로그래밍 언어에서는 유효성 검사를 수행하기 위해 정규표현식(Regular Expression)을 사용할 수 있습니다.

 

정규표현식은 특정 패턴이 있는 문자열을 검사할 수 있는 아주 훌륭한 메커니즘을 제공합니다. 그렇기 때문에 정규표현식은 유효성 검사를 하기 위한 가장 좋은 방법 중 하나입니다.

 

우선 입력된 데이터의 구조와 내용에 대한 유효성 검사를 수행하는데 사용될 정규표현식의 문법을 이해해야 합니다.주변을 둘러보면 정규표현식을 잘 활용하는 개발자들은 그다지 많지 않은 것 같습니다. 정규표현식은 프로그래밍 언어에서뿐만 아니라 각종 에디터 및 워드, 엑셀 등에서 특정 패턴을 검색하거나 교체할 때 상당히 유용하게 사용될 수 있습니다. 정규식에 대한 모든 것을 이 글에서 설명할 수 없기에 http://regxlib.com 이나 MSDN의 정규표현식 언어요소(http://msdn.microsoft.com/ko-kr/library/az24scfc.aspx)를 참고하면 쉽게 정규식을 익힐 수 있을 것입니다.

 

정규표현식 요소

정규표현식은 크게 리터럴(literal)과 메타문자(meta character)로 구분할 수 있습니다. 리터럴은 일치되길 원하는 특정 문자를 말하며, 메타문자는 와일드카드, 그룹, 반복, 범위, 조건 및 또 다른 제어 메커니즘을 제공하는 문자를 말합니다. 다음은 자주 사용되는 메타문자입니다.

요소

설명

.

\n(new line) 문자를 제외한 모든 문자

\d

10진수

\D

10진수가 아닌 문자

\s

공백문자

\S

공백문자가 아닌 문자

\w

알파벳이나 한글 및 숫자 같은 문자

\W

문자가 아닌 것

^

문자열이나 줄의 시작

\A

일치하는 부분이 문자열의 시작에 있어야 함

$

문자열이나 줄의 끝

\z

일치하는 부분이 문자열의 끝에 있어야 함

|

파이프로 구분된 값들 중 일치하는 하나를 찾음

This|That|What 는 This나 That, What중 하나가 있을 경우 일치하게 됨

[abc]

대괄호 안의 문자 중 일치하는 하나를 찾음

[QwEr]은 Q, w, E, r 중 하나가 있을 경우 일치하게 됨

[^abc]

^기호 뒤에 오는 문자가 아닌 문자를 찾음

[^QwEr]은 a나b는 찾지만 Q나 w는 찾지 못함

[a-z]

지정된 문자 범위 내에 있는 문자를 찾음

[A-C]는 A, B, C를 찾음

( )

정규표현식 내부의 또 다른 부분 정규표현식을 나타냄

?

?앞의 식이 있거나 없을 수 있음

*

*앞의 식이 0번 이상 올 수 있음

+

*앞의 식이 1번 이상 올 수 있음

{n}

{n}앞의 식이 n개인 경우를 찾음

{n,}

{n}앞의 식이 최소한 n개인 경우를 찾음

{n, m}

{n, m}앞의 식이 최소 n개에서 최대 m개인 경우를 찾음

 

더 많은 요소들에 대한 설명은 MSDN 정규표현식 언어요소(http://msdn.microsoft.com/ko-kr/library/az24scfc.aspx)에 자세히 설명되어 있음

 

보다 복잡한 데이터에 대한 유효성 검사를 할 때는 보다 복잡한 정규표현식 문법이 필요합니다. 예를 들어, 최소값을 갖는 숫자 데이터를 검사하는 것은 쉽지만 URL이나 주민등록번호를 검사하는 것은 상당히 복잡합니다. 다음 표는 응용프로그램을 개발할 때 자주 사용하는 정규표현식을 만들어 본 것입니다. 최소한의 문법을 사용하여 구현하였으며 카드번호 및 주민등록번호의 각 자리가 의미하는 복잡한 패턴은 정의되어있지 않습니다.

 

입력 값

정규표현식

설명

숫자

^\d+$

문자가 아닌 숫자

단순비밀번호

^\w{6,8}$

6~8자리까지의 문자(숫자 포함) 비밀 번호를 검사함

신용카드번호

^\d{4}-?\d{4}-?\d{4}-?\d{4}$

4자리 숫자가 4번 반복됨 하이픈(-)은 없을 수도 있음

주민등록번호

^\d{6}-?\d{7}$

6자리 숫자와 7자리 숫자의 가운데에 하이픈(-)이 들어감

이메일

^[\w-]+@([\w-]+\.)+[\w-]+$

이메일 주소를 검사함

URL

^https?://([\w-]+\.)+[\w-]+(/[\w-./?&%=]*)?$

URL을 검사함

 

위의 정규표현식 중 그나마 가장 복잡해 보이는 URL을 분석해 보고 정규표현식에 조금 적응을 해 보도록 하겠습니다.

 

URL 정규표현식 패턴(^https?://([\w-]+\.)+[\w-]+(/[\w-./?&%=]*)?$) 분석

1.      ^는 문자열이나 줄의 시작을 나타냅니다.

2.      http는 리터럴로 순수 http라는 문자에 대응합니다. 1과 2를 함께 보면 입력된 문자는 http로 시작해야 한다는 것을 의미합니다.

3.      s?는 리터럴 s가 있거나 없을 수도 있음을 나타냅니다. 즉 http 또는 https로 문자열이 시작된다는 것을 의미합니다.

4.      ://는 단순 리터럴입니다.

5.      ([\w-]+\.)+는 \w(문자)나 하이픈(-)이 한번 이상 반복된 다음 마침표(.)가 나옵니다. 즉, www. 또는 w. 또는 mobile-. 등이 될 수 있습니다. 그리고 이것이 괄호로 묶여 있고 +가 있으므로 이 전체가 한번 이상 반복될 수 있습니다. ex) www.dayofdays. 또는 www.taeyo.

6.      [\w-]+는 문자 및 하이픈(-)이 한번 이상 반복될 수 있음을 의미합니다.

ex)www.dayofdays.net 또는 www.taeyo.net

7.      마지막으로 (/[\w-./?&%]*)?$는 문자(\w)나 하이픈(-) 또는 마침표(.), 슬래시(/). 물음표(?), 앰퍼센드(&), 퍼센트(%)중 한가지가 0번 이상(*)올 수 있으며 이 문자열의 앞에 리터럴 /가 오며 이 전체((/[\w-./?&%]*))가 있어도 없어도(?)되며 문자열이나 줄의 끝($)이어야 합니다. 즉 /talk/board.aspx?no=1&page=2와 같은 문자열을 검사합니다.

대괄호[] 안에 문자를 지정하는 경우 해당 문자들을 중 하나와 일치하는 값을 찾아내므로 .이나 ?가 역슬래쉬(\)없이 사용되었지만 메타문자가 아닌 일반 문자로 취급됩니다.

 

하나하나씩 분석해 보면 그다지 어렵지 않으며 순차적으로 해석하되 []를 먼저 해석하고 ()를 해석한 다음 전체를 보면 좀 더 쉽게 이해할 수 있습니다.

 

Regex 클래스를 사용하여 유효성 검사 수행

앞에서 정규표현식을 생성하였으므로 이제 Regex 클래스를 사용하여 실제 유효성 검사를 수행해보도록 하겠습니다. Regex 개체는 Regex 생성자에 정규표현식을 매개변수로 전달하여 만들 수 있습니다. 개체를 생성했으면 Regex 개체의 IsMatch 메서드를 사용하여 검사를 수행할 수 있습니다. IsMatch 메서드는 검사할 문자열을 매개변수로 취합니다. IsMatch 메서드는 전달한 문자열이 정규표현식과 일치한다면 true를 반환하고 그렇지 않다면 false를 반환하게 됩니다.

코드는 다음과 같다.

 

public static bool Validate(string regexString, string inputValue)

{

         Regex regex = new Regex(regexString);

         return r.IsMatch(inputValue);

}

 

위 메서드의 실행 결과는 다음과 같습니다.

Validate(“^\d+$”, ”12345”); //true를 반환

Validate(“^\d+$”, ”12345A”); //false 반환

 

다양한 문자열을 검사하기 위해 Regex 개체를 반복적으로 사용할 수 있습니다. 그러나 Regex 개체에 할당된 정규표현식은 생성자를 통해 할당하였고 별도의 속성을 제공하지 않기 때문에 교체할 수 없습니다. Regex 개체를 재사용할 수 없으므로 새로운 정규표현식을 사용하기 위해서는 매번 Regex 개체를 생성해야 합니다. 이는 그다지 훌륭한 방법이 아닐 수 있습니다. 이런 경우 보다 적절한 대안은 IsMatch 메서드의 오버로드로 제공되는 static 메서드를 사용하는 것입니다

.

public static bool Validate(string regexString, string inputValue)

{

         return Regex.IsMatch(regexString, inputValue);

}

 

앞에서 설명한 두 방법은 Regex개체가 사용될 때 마다 런타임이 정규표현식을 중간 형식(Intermediate form, opcode)으로 변경(interpret)한 다음 대상 문자열에 적용합니다. 이로 인한 문제점 및 해결을 위한 방법을 다음 글에서 알아보도록 하겠습니다.

 

감사합니다.

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by kyeongkyun(kobukii) kyeongkyun
C#2013/07/22 13:09

컴파일 된 정규표현식 사용하기

이전 강좌의 두 가지 방법으로 복잡한 정규표현식을 자주 적용하다 보면 응용프로그램의 성능이 저하될 수 있습니다. 성능 저하를 최소화 하기 위해서는 정규표현식을 MSIL(Microsoft Intermediate Language)로 컴파일 하여 성능을 향상시킬 수 있으며, 이는 Regex 개체를 생성할 때 생성자 매개변수로 RegexOptions.Compiled 옵션을 지정하여 컴파일 되도록 설정할 수 있습니다.

 

Regex 개체를 생성할 때, 생성자에 전달한 정규표현식 패턴은 중간 형식(Intermediate Form, opcode)으로 컴파일 됩니다. 런타임은 Regex 개체를 사용할 때 마다 정규표현식 패턴의 중간 형식을 해석(Interpret)하여 대상 문자열에 적용하게 됩니다. 복잡한 정규표현식이 빈번하게 사용되면 이러한 해석 작업이 반복되며, 그것으로 인해 응용프로그램의 성능에 나쁜 영향을 미칠 수 있습니다.

 

중간 형식(Intermediate Form, opcode)은 MSIL(Microsoft Intermediate Language)가 아님

 

컴파일 된 정규표현식

Regex 개체를 생성할 때 RegexOptions.Compiled 옵션을 설정하면 .NET 런타임이 정규표현식을 중간형식으로 해석하는 대신 MSIL로 컴파일 하도록 강제할 수 있습니다. 이 MSIL은 최초 수행 시 런타임에 의해 네이티브 기계어 코드로 JIT(just-in-time) 컴파일 됩니다. 마치 정규 어셈블리 코드처럼 동작하는 것입니다. 그런 다음 Part 1에서 설명한 것처럼 일반 Regex 개체를 사용하는 방식으로 사용하면 보다 빠른 성능을 기대할 수 있습니다.

 

정규표현식의 컴파일은 성능상의 장점이 있는 반면, 그로 인한 두 가지의 단점도 있습니다. 첫 번째는 JIT 컴파일러가 더 많은 작업을 해야 하므로 JIT 컴파일을 하는 동안 지연이 발생할 수 있다는 것입니다. 두 번째로, 런타임은 컴파일 된 정규표현식을 언로드 할 수 없다는 것입니다. 일반적인 정규표현식과 달리 런타임 가비지 컬렉터는 컴파일 된 정규표현식에 의해 사용된 메모리를 회수하지 않습니다. 그렇기 때문에 컴파일 된 정규표현식은 프로그램이 종료되거나 컴파일 된 정규표현식이 로드 된 응용프로그램 도메인을 언로드 할 때까지는 메모리에 그대로 남아있게 됩니다.

 

컴파일 된 정규표현식을 외부 어셈블리로 내보내기

Regex 클래스의 정적 메서드인 CompileToAssembly는 정규표현식을 컴파일하고 외부 어셈블리로 작성하는 기능을 제공합니다. 이를 사용하면 여러 개의 응용프로그램에서 공유할 수 있는 정규표현식들로 구성된 어셈블리를 생성할 수 있습니다.

 

정규표현식을 컴파일하고 어셈블리로 만들기 위해서는 컴파일 된 정규표현식을 할당하기 위한 regexCompilationInfo 개체들을 저장해 둘 RegexCompilationInfo의 배열을 생성합니다. 그 다음 컴파일 된 정규표현식을 담기 위한 RegexCompilationInfo 개체를 생성합니다. 개체를 생성할 때 다음 매개변수들을 생성자 매개변수로 전달합니다

.

속성명

설명

IsPublic

생성된 정규표현식 클래스가 public인지를 지정하는 bool값

Name

클래스 이름

Namespace

클래스의 네임스페이스

Pattern

정규표현식

Options

정규표현식의 옵션을 지정하기 위한 RegexOptions 열거형 값

 

이제 어셈블리를 생성하기 위해 System.Text.Reflection.AssemblyName 개체를 생성하고 Regex.CompileToAssembly 메서드를 통해 생성할 어셈블리의 이름을 나타내는 AssemblyName.Name에 값을 설정합니다. 마지막으로 RegexCompilationInfo 배열과 AssemblyName개체를 매개변수로 Regex.CompileToAssembly 메서드를 실행합니다.

위 과정대로 수행하면 컴파일 된 하나의 정규표현식이 하나의 클래스로 만들어지고 이 클래스들을 포함하는 어셈블리(dll)가 출력(Output) 폴더에 만들어집니다. 각 클래스는 Regex 클래스로부터 파생됩니다.

 

RegexCompilationInfo[] regexInfos = new RegexCompilationInfo[2];

//정규식 클래스 PinRegex를 생성하기 위한 정보 설정

regexInfos[0] = new RegexCompilationInfo(@"^\d{4}$", RegexOptions.Compiled, "PinRegex", "", true);

//정규식 클래스 URLRegex를 생성하기 위한 정보 설정

regexInfos[1] = new RegexCompilationInfo(@"^https?://([\w-]+\.)+[\w-]+(/[\w-./?&%=]*)?$", RegexOptions.Compiled, "URLRegex", "", true);

 

//어셈블리 이름을 설정

AssemblyName assemblyName = new AssemblyName();

assemblyName.Name = "MyRegex";

 

//정규식 클래스들을 어셈블리로 기록

Regex.CompileToAssembly(regexInfos, assemblyName);

 

위의 코드를 실행하면 출력(Output) 폴더에 MyRegex.dll 어셈블리가 생성이 되며 이를 사용하기 위해 어셈블리를 참조합니다.

 

그림 1. MyRegex 어셈블리 참조

 

어셈블리에 포함된 정규표현식을 사용하기 위해서는 매개변수(정규표현식) 없이 Regex 개체를 생성하는 것처럼 해당 클래스의 개체를 생성하고 생성된 개체의 IsMatch 메서드를 호출하여 검사를 수행할 수 있습니다. 각 클래스는 Regex 클래스로부터 파생되었기 때문에 정적 IsMatch 메서드도 제공하지만 이 메서드는 부모 클래스인 Regex의 정적 메서드로써 정규표현식을 매개변수로 지정해야 하므로 무시하면 됩니다. 이제 아래 코드와 같이 인스턴스를 생성하고 IsMatch 메서드를 호출하도록 합니다.

 

PinRegex rpin = new PinRegex();

bool b1 = rpin.IsMatch("1234");

URLRegex rurl = new URLRegex();

bool b2 = rurl.IsMatch("http://teayo.net/talk/board.aspx?no=1&page=2");

 

위 코드와 같이 호출하면 Regex 개체를 사용하는 것보다 그 과정이 복잡하지만 위 방식은 여러 번 클래스를 생성하지 않고도 각 개체의 인스턴스 메서드인 IsMatch를 사용할 수 있으므로 불필요한 메모리의 낭비와 성능 문제를 해결 할 수 있습니다. 하지만 정규표현식 패턴과 RegexOptions들이 이미 정해진 채로 어셈블리가 생성되고 이는 추후에 변경할 수 없으므로 유연성이 상당히 떨어진다는 단점도 존재합니다.

 

정규표현식을 사용하기 위한 방법은 Regex 클래스의 인스턴스 메서드를 사용하는 것과 정적 메서드를 사용하는 방법, 컴파일 통해 사용하는 방법, 별도의 어셈블리로 만들어 사용하는 방법이 있으며, 이 들은 모두 적절한 용도가 있기 때문에 .NET에서 제공 하겠지만 어떤 상황에서 어떤 방식을 사용해야 할지는 명확하게 정의되어 있지 않습니다. 이 문제를 해결하기 위해 다음 글에서는 정규표현식 성능 측정 및 최적화에 대해 다루어 보도록 하겠습니다.

 

감사합니다.


출처 : http://kyeongkyun.tistory.com/category/C%23


관련글 더보기