사용자 도구

사이트 도구


kb:boostregex

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

kb:boostregex [2014/11/06 19:01] (현재)
줄 1: 줄 1:
 +====== Boost Regex ======
 +boost::​regex 는 정규표현식(RegularExpression)을 프로그램 내부에서 사용할 수 있도록 해주는 라이브러리다. 사실 regex 보다 사용법 쉽고, 컴팩트한 라이브러리는 상당히 많은데, 굳이 regex 를 쓰기로 한 것은 기능이 좀 많고, boost 라이브러리와 같이 따라오기 때문에 설치가 그나마 편하다고 생각했기 때문이다.
 +
 +
 +====== 간단한 소개 ======
 +:!: //​라이브러리와 함께 딸려오는 문서를 번역한 것이다.//​
 +
 +정규식은 패턴 매칭의 한 가지 형태로서,​ 텍스트 처리에 종종 사용된다. 많은 사용자들이 정규식을 사용하는 grep, sed, awk 같은 유틸리티나,​ perl 같은 언어에 익숙할 것이다. 전통적으로 C++ 사용자들은 POSIX C API(regXXX 시리즈)를 사용해서 정규식을 처리해 왔다. 비록 regex 라이브러리가 이 API들의 기능을 똑같이 제공하지만,​ 기존의 API처럼 사용하는 것은 라이브러리의 기능을 충분히 살리지 못하는 것이다. 예를 들어 regex 라이브러리는 유니코드 문자열을 다룰 수 있으며, 검색/​치환 기능도 제공한다. 이런 기능들은 기존의 전통적인 C 라이브러리에는 없는 것들이다.
 +
 +boost::​basic_regex 는 라이브러리의 핵심이 되는 클래스이다. 이 클래스는 "​기계가 읽을 수 있는"​ 정규식을 나타내며,​ std::string 과 상당히 비슷한 모양으로 디자인되어 있다. std::string 에다 정규식 처리 알고리즘을 위한 상태 정보를 더했다고 보면 된다. std::string 과 마찬가지로 두 가지의 typedef가 존재한다.
 +
 +<code cpp>
 +namespace boost{
 +
 +template <class charT, ​
 +          class traits = regex_traits<​charT>, ​
 +          class Allocator = std::​allocator<​charT>​ >
 +class basic_regex;​
 +
 +typedef basic_regex<​char>​ regex;
 +typedef basic_regex<​wchar_t>​ wregex;
 +
 +}
 +</​code>​
 +
 +이 라이브러리가 어떻게 사용되는지 알기 위해서, 신용 카드 처리와 관련된 애플리케이션을 제작한다고 생각해 보자. 신용 카드 번호는 10진수 4개 묶음을 4번 겹친 것, 즉 16개의 10진수로 이루어진다. 그리고 4개의 묶음들 사이에는 ' '나 '​-'​ 문자가 들어간다. 신용 카드 번호를 데이터베이스에 저장하기 전에, 카드 번호가 올바른 포맷인지를 검사하기를 원한다고 하자. 10진수인지를 검사하기 위해서는 전통적인 <​code>​[0-9]</​code>​ 와 같은 표현을 쓸 수 있다. 하지만 이런 표현방식은 실제로는 로케일에 독립적이지 못하다. 대신에 POSIX 표준인 <​code>​[[:​digit:​]]</​code>​ 표현이나,​ perl의 단축어인 '​\d'​를 사용하자. (오래된 라이브러리일 경우, \d 같은 표현은 로케일에 상관없이 하드코딩되어 있을 확률이 높으니 주의) 이를 이용하면,​ 다음과 같이 신용 카드 번호를 표현할 수 있다.
 +
 +<​code>​
 +(\d{4}[- ]){3}\d{4}
 +</​code>​
 +
 +괄호는 그룹을 나타내기 위한 것이다. {4}가 나타내는 것은 "​정확히 4번 반복"​한다는 말이다. 이것은 perl, awk, egrep에서 사용하는 확장 정규표현식의 예이다. regex 라이브러리는 sed, grep 에서 사용하는 좀 더 오래된 "​기본"​ 문법도 제공하지만,​ 보통 비실용적이다. 물론 이미 활용하고 있는 오래된 정규표현식들이 있다면 "​기본"​ 문법들을 사용할 수도 있다.
 +
 +위에서 만든 표현을 신용 카드 번호를 검증하는 C++ 코드로 만들면 다음과 같다.
 +
 +<code cpp>
 +bool validate_card_format(const std::string s)
 +{
 +   ​static const boost::​regex e("​(\\d{4}[- ]){3}\\d{4}"​);​
 +   ​return regex_match(s,​ e);
 +}
 +</​code>​
 +
 +C++ 코드이므로 '​\'​ 문자를 사용할 때 하나를 더 쓰는 것은 C++ 문법을 아는 사람이라면 익숙할 것이다. 그리고 위의 예제를 포함해서 모든 예제들은 사용하는 컴파일러가 Koenig lookup(네임스페이스와 관련된 모호성 해소 방법)을 지원한다고 가정한다. 만일 이를 지원하는 않는 컴파일러(예를 들어 VC6)라면, 몇개 함수 앞에다 <​code>​boost::</​code>​ 접두어를 붙여줘야할 것이다.
 +
 +신용 카드 처리에 익숙한 사람이라면,​ 위의 포맷('​ ' 또는 '​-'​로 구분되는...)이 사람이 읽기에는 좋을지는 몰라도, 다른 대부분의 온라인 카드 처리 시스템에서 사용하는 포맷과는 맞지 않는다는 것을 알 것이다. 즉 중간의 ' ' 또는 '​-'​ 문자를 없애야하는 말이다. 그러므로 중간에 구분자가 들어가는 포맷과 들어가지 않는 포맷 간의 전환이 가능해야 한다. 이는 검색/​치환 기능을 요구한다. sed, perl 등에 익숙한 사람은 이미 이 문제를 생각하고 있었을 것이다. regex 라이브러리에서는 regex_replace 알고리즘을 이용한다. 신용 카드 예제와 관련해서 다음과 같은 표현식을 만들어 두 포맷 간의 변환을 제공할 수 있다.
 +
 +<code cpp>
 +// match any format with the regular expression:
 +const boost::​regex e("​\\A(\\d{3,​4})[- ]?​(\\d{4})[- ]?​(\\d{4})[- ]?​(\\d{4})\\z"​);​
 +const std::string machine_format("​\\1\\2\\3\\4"​);​
 +const std::string human_format("​\\1-\\2-\\3-\\4"​);​
 +
 +std::string machine_readable_card_number(const std::string s)
 +{
 +   ​return regex_replace(s,​ e, machine_format,​ boost::​match_default | boost::​format_sed);​
 +}
 +
 +std::string human_readable_card_number(const std::string s)
 +{
 +   ​return regex_replace(s,​ e, human_format,​ boost::​match_default | boost::​format_sed);​
 +}
 +</​code>​
 +
 +하위 표현식(sub-expression)을 사용해서,​ 신용 카드 번호를 각각의 묶음으로 나누었다. 포맷 문자열은 sed 에서 사용하는 방식으로 만들어, 각각의 일치하는 부분을 재포맷된 문자열로 치환하도록 했다.
 +
 +위의 예제에서는 정규 표현식 매칭의 결과를 직접 다루지는 않았다. 하지만 일반적으로 이 결과값은 전체 매칭 뿐만 아니라, 하위 매칭 결과도 같이 포함한다. regex 라이브러리에서는 이런 결과값들을 리턴하기 위해서 다음과 같은 클래스들을 사용한다.
 +
 +<code cpp>
 +namespace boost{
 +typedef match_results<​const char*> cmatch;
 +typedef match_results<​const wchar_t*>​ wcmatch;
 +typedef match_results<​std::​string::​const_iterator>​ smatch;
 +typedef match_results<​std::​wstring::​const_iterator>​ wsmatch; ​
 +}
 +</​code>​
 +
 +regex_search 알고리즘과 regex_match 알고리즘은 match_results 클래스를 사용하여 결과를 반환한다. regex_match 는 주어진 문자열 전체가 표현식과 일치하는지의 여부를 반환하고,​ regex_search 는 주어진 문자열 내부에서 표현식에 일치하는 부분을 찾는다.
 +
 +이 알고리즘들은 일반적인 C 문자열 뿐만 아니라 양방향 반복자를 제공하는 어떤 데이터 타입에도 적용할 수 있다는 점을 참고하기 바란다.
 + 
 +검색/​치환 작업에 있어서, 이미 위에서 본 regex_replace 알고리즘 뿐만 아니라, match_results 클래스 또한, 포맷 결과를 받아들여 합친 후 반환하는 함수를 제공한다.
 +
 +For iterating through all occurences of an expression within a text, there are two iterator types: regex_iterator will enumerate over the match_results objects found, while regex_token_iterator will enumerate a series of strings (similar to perl style split operations).
 + 
 +텍스트 안에서 일치하는 부분을 횡단하기 위해서, 두 가지 반복자를 제공한다. regex_iterator 는 match_results 들을 횡단하기 위해서 사용하고,​ regex_token_iterator 는 문자열의 집합을 횡단하는 데 사용한다. ​
 +
 +호환성을 위해서 POSIX API 함수, regcomp, regexec, regfree, regerrr 들도 1바이트 및 2바이트 문자열 버전으로 존재한다.
 +
 +
 +====== 예제 ======
 +BoostRegex 라이브러리를 사용하기 위해서는 빌드 과정을 거쳐야 한다. 빌드 과정은 BoostBuild 페이지를 참고. 여러 가지 기능 많지만, 기본적인 검색과 치환 기능만 있으면 왠만한 작업은 처리 가능할 듯 한데...
 +
 +==== 문자열 내부에서 표현식에 일치하는 부분 모두 출력하기 ====
 +특정 문자열 내부에서 WikiName을 찾아서 출력하는 예제다.
 +
 +<code cpp>
 +#include <​boost/​regex.hpp>​
 +#include <​iostream>​
 +
 +#pragma comment(lib,​ "​libboost_regex-vc71-mt-sgd-1_31.lib"​)
 +
 +int main()
 +{
 +    try
 +    {
 +        const boost::​regex e("​([A-Z][a-z]+[A-Z][a-z]+|\\[.*\\])"​);​
 +        std::string text = 
 +            "​TestCase is WikiName. but TESTCase is not WikiName! Is [this] WikiName?";​
 +
 +        boost::​match_results<​std::​string::​const_iterator>​ m;
 +        std::​string::​const_iterator start = text.begin();​
 +        std::​string::​const_iterator end = text.end();
 +
 +        while (boost::​regex_search(start,​ end, m, e))
 +        {
 +            // m[0]는 일치하는 부분 문자열을 나타내고,​
 +            // m[0].first는 일치하는 부분의 시작 위치,
 +            // m[0].second는 일치하는 부분의 끝을 나타낸다.
 +            // m[n]은 하위 표현식(괄호로 둘러싸인 표현식)을 나타내는데, ​
 +            // 예제 정규 표현식에 하위 표현식이 존재하지 않기 때문에, ​
 +            // 여기서는 [0] 밖에는 쓸 일이 없다.
 +            cout << m[0] << " = "
 +                << m[0].first - text.begin() << "​~"​
 +                << m[0].second - text.begin() << endl;
 +
 +            start = m[0].second;​
 +        }
 +    }
 +    catch (std::​exception&​ e)
 +    {
 +        cerr << e.what() << endl;
 +    }
 +    ​
 +    return 0;
 +}
 +</​code>​
 +
 +==== 일치하는 문자열 치환하기 ====
 +모든 위키 이름을 괄호 2개로 감싸는 예제. (원래는 괄호 3개가 목적이었는데,​ 위키에서는 표현하기가 좀 곤란한 관계로.)
 +
 +<code cpp>
 +#include <​boost/​regex.hpp>​
 +#include <​iostream>​
 +#include <​sstream>​
 +
 +#pragma comment(lib,​ "​libboost_regex-vc71-mt-sgd-1_31.lib"​)
 +
 +int main()
 +{
 +    try
 +    {
 +        const boost::​regex e("​([A-Z][a-z]+[A-Z][a-z]+|\\[.*\\])"​);​
 +        std::string text = 
 +            "​TestCase is WikiName. but TESTCase is not WikiName! Is [this] WikiName?";​
 +
 +        std::​string::​const_iterator start = text.begin();​
 +        std::​string::​const_iterator end = text.end();
 +        std::​stringstream result;
 +        std::​ostream_iterator<​char,​ char> oi(result);
 +
 +        // $0는 정규 표현식에 일치하는 부분 전체를 의미한다. ​
 +        // 자세한 것은 펄(perl) 문법을 참고.
 +        boost::​regex_replace(oi,​ start, end,
 +            e, "​{{$0}}",​ boost::​match_default | boost::​format_all);​
 +
 +        cout << result.str() << endl;
 +
 +        // 이런 방법도 가능하다.
 +        // cout << boost::​regex_replace(text,​
 +        //     e, "​{{$0}}",​ boost::​match_default | boost::​format_all);​
 +    }
 +    catch (std::​exception&​ e)
 +    {
 +        cerr << "​Exception:​ " << e.what() << endl;
 +    }
 +    ​
 +    return 0;
 +}
 +</​code>​
 +
 +====== 링크 ======
 +  * [[http://​www.boost.org/​libs/​regex/​doc/​index.html | Boost Regex Reference]]
 +  * [[http://​network.hanbitbook.co.kr/​view.php?​bi_id=1218 | C++에서 Boost.Regex로 정규식 사용하기]]
 +  * [[http://​www.codeproject.com/​cpp/​notepadre.asp | Notepad RE (Regular Expressions)]] \\ boost::​regex를 사용한 간단한 노트패드
 +
 +----
 +  * see also [[BoostLibrary]],​ [[RegularExpression]]
  
kb/boostregex.txt · 마지막으로 수정됨: 2014/11/06 19:01 (바깥 편집)