Turbo-C
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
터보-C 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
Lua 게시판
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C/C++ 팁&트릭
[35] [Boost]정규 표현식(regular expression) 라이브러리 Regex++의 소개
김백일 [cedar] 22154 읽음    2002-08-13 15:27
정규 표현 라이브러리 'Boost Regex++'의 사용

http://www.s34.co.jp/cpptechdoc/article/regexpp/에서 퍼온 글을 번역기에서 돌린 후, 약간 수정해서 올립니다.

근데, 일본어 번역체는 왜이리 어색한 것일까요? -_-;;


정규 표현 라이브러리 'Boost Regex++'의 사용법

'web를 개입시킨 프로그램의 실행'라고 하는 어플리케이션의 새로운 형태가 나타나, perl, python으로 대표되는 스크립트 언어가 자주 이용되게 되었습니다. 스크립트 언어의 상당수는 텍스트(문자열)을 취급하는 것을 자랑으로 여기고 있습니다.

한편 C++는 문자열을 취급하는 것이 별로 편리하지 않습니다. 최근이 되어 간신히 문자열을 표현하는 클래스 std::basic_string 가 표준 라이브러리에 집어 넣을 수 있었습니다.

그렇지만 표준의 문자열을 손에 넣은 C++에서도 perl등의 스크립트 언어에 이길 수 없는 것의 하나가, '정규 표현(regular expression)'입니다.

예를 들어 Web의 자동 순회를 실시하는 어플리케이션을 생각해 봅시다. 하나의 HTML로부터  링크 정보(URL)를 추출해, URL가 가리키는 링크를 더듬어내는 것 같은 처리를 실시합니다. 이 때 우선 필요한 것은 'HTML로부터 링크 정보(URL)를 추출하는'일입니다.

HTML내의 링크 정보는: <a href='http://www.s34.co.jp/cpptechdoc/index.html'> 와 같은 서식에서 나타내집니다.

  1. "<a"
  2. 1개 이상의 공백
  3. 인용부호
  4. 임의의 문자열(여기가 URL)
  5. 인용부호
  6. 0개 이상의 공백
  7. ">"

그렇다고 하는 순서로 나타나는 문자열 패턴을 HTML 즉 문자열 속으로부터 찾아내지 않으면 안됩니다. 이것을 표준 C함수나 std::basic_string의 메소드만으로 실현하려고 하면, 그 코드는 상당한 크기가 되겠지요.

이 문자열 패턴의 정규 표현은: <a +href=('|\"). *('|\") *> 가 됩니다. 정규 표현 라이브러리는 문자열로부터 정규 표현에 적합한 부분을 찾아내 줍니다.

C-인터페이스를 제공하는 정규 표현 라이브러리는 몇 가지 있습니다만, C++로 사용하기 편리하고, 한편 고기능/고성능의 것은 매우 적은 것 같습니다. 그 얼마 안 되는 C++판 정규 표현 라이브러리의 하나, Regex++의 사용법을 해설합니다.

※Regex++는 프리 C++라이브러리 'Boost'에 수록되고 있어 http://www.boost.org/ 로부터 입수할 수 있습니다.
덧붙여 이 아티클에서는 Regex++의 기본적인 사용법의 해설에 그칩니다. 자세하게는 라이브러리에 첨부의 문서를 참조해 주세요.

Regex++는 정규 표현 패턴을 격납하는 reg_expression, 매치 위치를 표현하는sub_match, 검색 결과(sub_match의 집합)인 match_results 의 3개의 클래스 템플릿, 그리고 그것들을 사용해 정규 표현 검색을 실현하는 몇 개의 함수 템플릿으로 구성되어 있습니다.

regex_search

const char* source = 문자열;
boost::reg_expression<char> regex = 정규 표현;
boost::match_results<const char*> results;
 
bool found = boost::regex_search(source, results, regex);

source로부터 regex에 일치하는 부분 문자열을 찾습니다. 일치하는 부분 문자열이 발견되면 true, 발견되지 않으면 false를 돌려줍니다.

일치하는 부분 문자열이 발견되었을 때, results에 일치하는 부분을 리턴합니다.

resutls. str(n)는, regex에 있는 n번째의 '('와 거기에 대응하는 ')'에 둘러싸인 부분에 일치하는 부분 문자열(std::string)을 돌려줍니다. 예를 들어:

const char* source = "abc1234def";
boost::reg_expression<char> regex = "([a-zA-Z])(. *)[a-zA-Z]";
boost::match_results<const char*> results;
boost::regex_search(source, results, regex);

그럼,

result. str(1) = "c"
result. str(2) = "1234"

됩니다. 덧붙여 str(0) regex전체와 일치하는 부분 문자열 즉"c1234d"입니다.

results. position(n), results. length(n) 는, 각각 n번째에 대응하는 부분 문자열의 위치와 길이를 돌려줍니다. 위의 예에서는:

results. position(1) = 2
results. length(1) = 1
 
results. position(2) = 3
results. length(2) = 4

한자(2바이트 문자)를 포함한 멀티 바이트 문자열에 대해서는 올바른 결과를 얻을 수 없습니다.

예를 들어 Shift_JIS 환경에서:

const char* soure = "アルファベットを含むShift_JIS文字列";
boost::reg_expression<char> regex = "([a-zA-Z])(. *)[a-zA-Z]";
boost::match_results<const char*> results;
boost::regex_search(source, results, regex);

이 실행 결과는,results.str(0) = "Aルファベットを含むShift_JIS" 가 되어 버립니다. Shift_JIS에서는 'ア'의 제2바이트가 'A'와 일치하기 때문입니다.

멀티 바이트 문자열은 일단 Unicode로 변환하지 않으면 안됩니다. Unicode이면 Regex++로 올바른 결과를 얻을 수 있는:

const wchar_t* soure = L"アルファベットを含むShift_JIS文字列";
boost::reg_expression<wchar_t> regex = L"([a-zA-Z])(. *)[a-zA-Z]";
boost::match_results<const wchar_t*> results;
boost::regex_search(source, results, regex);
std::wstring found =  results.str(0); // found = L"Shift_JIS"

regex_match

regex_search가 일치하는 부분 문자열을 검색하는데 대해, regex_match는 처리의 대상인 문자열 전체가 주어진 정규 표현과 완전하게 일치했을 때 true를 돌려줍니다.

// 입력 문자열이 시각표현(hh:mm:ss)인가?
std::string input;
std::cin >> input;
boost::reg_expression<char> regex = "([0-9][0-9]):([0-9][0-9]):([0-9][0-9])";
boost::match_results<std::string::iterator> results;
if ( boost::regex_match(source, results, regex) ) {
  std::cout << results.str(1) << "hr.  " << results.str(2) << "min.  " << results.str(3) << "sec. \n";
} else {
  std::cout << "invalid input\n";
}

regex_grep

regex_search는 주어진 문자열로부터, 정규 표현에 최초로 일치하는 부분 문자열을 찾습니다. 이에 비해,regex_grep는 주어진 문자열로부터 정규 표현에 일치하는 부분 문자열을 '모두' 열거합니다.

unsigned int n = boost::regex_grep(predicate, source, regex);

제2, 제3 인수는regex_search, regex_match와 같이, 각각 검색 대상 문자열, 정규 표현입니다.

제1 인수 predicate match_results의 const 참조를 인수에 취해, bool 형을 돌려주는 함수 오브젝트입니다. 정규 표현에 일치하는 부분 문자열이 발견될 때에 불려 갑니다. 검색을 속행한다면 true를, 정지한다면 false를 돌려주세요. regex_grep predicate를 호출한 회수를 돌려줍니다.

struct print_submatch {
  bool operator ()(const boost::match_results<const std::wstring::const_iterator>& m) {
  std::wcout << m.str(0) << std::endl;
  return true;
  }
};
 
const std::wstring source = L"私は山が好きで海が好きで川も好きです。";
boost::reg_expression<wchar_t> regex = L"(山|川).好き";
boost::regex_grep(print_submatch(), source, regex);

regex_search, regex_grep 의 샘플 코드를 이하에 나타냅니다. 문자열 조작에 도움이 되었으면 합니다.

 


regexpp.cpp

 


#include <iostream>
#include <locale>
 
#include <boost/regex.hpp>
 
using std::cout;
using std::wcout;
using std::endl;
 
void bad_search() {
  cout << "\nRegex++ can NOT support multibyte regex" << endl;
  std::string source = "アルファベットを含むShift_JIS文字列";
  boost::reg_expression<char> regex = "[a-zA-Z]. *[a-zA-Z]";
  boost::match_results<std::string::const_iterator> results;
  if ( boost::regex_search(source, results, regex) ) {
    cout << results. str(0) << endl;
  }
}
 
void good_search() {
  cout << "\nso let's try std::wstring & Regex++" << endl;
  std::wstring source = L"アルファベットを含むShift_JIS文字列";
  boost::reg_expression<wchar_t> regex = L"[a-zA-Z]. *[a-zA-Z]";
  boost::match_results<std::wstring::const_iterator> results;
  if ( boost::regex_search(source, results, regex) ) {
    wcout << results. str(0) << endl;
  }
}
 
void test_search() {
 
  cout << "\nregex_search for const char*" << endl;
  {
    const char* source = "<a href='http://www.roguewave.com:80/index.html'>here</a>";
    boost::reg_expression<char> regex  = "(([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)";
    boost::match_results<const char*> results;
    cout << "URL = " << source << endl
         << "regex = " << regex. str() << endl;
    if ( boost::regex_search(source, results, regex) ) {
      cout << " scheme :" << results. str(2) << endl
           << " host   :" << results. str(3) << endl
           << " port   :" << results. str(5) << endl
           << " path   :" << results. str(6) << endl;
    } else {
      cout << "no match. " << endl;
    }
  }
 
  cout << "\nregex_search for std::string" << endl;
  {
    std::string source = "<a href='http://www.roguewave.com:80/index.html'>here</a>";
    boost::reg_expression<std::string::value_type> regex  = "(([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)";
    boost::match_results<std::string::const_iterator> results;
    cout << "URL = " << source << endl
         << "regex = " << regex. str() << endl;
    if ( boost::regex_search(source, results, regex) ) {
      cout << " scheme :" << results. str(2) << endl
           << " host   :" << results. str(3) << endl
           << " port   :" << results. str(5) << endl
           << " path   :" << results. str(6) << endl;
    } else {
      cout << "no match. " << endl;
    }
  }
 
}
 
void test_wide_search() {
 
  cout << "\nregex_search for const wchar_t*" << endl;
  {
    const wchar_t* source = L"<a href='http://www.roguewave.com:80/index.html'>here</a>";
    boost::reg_expression<wchar_t>  regex  = L"(([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)";
    boost::match_results<const wchar_t*> results;
    wcout << L"URL = " << source << endl
          << L"regex = " << regex. str() << endl;
    if ( boost::regex_search(source, results, regex) ) {
      wcout << L" scheme :" << results. str(2) << endl
            << L" host   :" << results. str(3) << endl
            << L" port   :" << results. str(5) << endl
            << L" path   :" << results. str(6) << endl;
    } else {
      cout << "no match. " << endl;
    }
  }
 
  cout << "\nregex_search for std::wstring" << endl;
  {
    std::wstring source = L"<a href='http://www.roguewave.com:80/index.html'>here</a>";
    boost::reg_expression<std::wstring::value_type> regex  = L"(([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)";
    boost::match_results<std::wstring::const_iterator> results;
    wcout << L"URL = " << source << endl
          << L"regex = " << regex. str() << endl;
    if ( boost::regex_search(source, results, regex) ) {
      wcout << L" scheme :" << results. str(2) << endl
            << L" host   :" << results. str(3) << endl
            << L" port   :" << results. str(5) << endl
            << L" path   :" << results. str(6) << endl;
    } else {
      cout << "no match. " << endl;
    }
  }
}
 
struct grep_predicate {
  bool operator()(const boost::match_results<std::string::const_iterator>& m) {
    cout << m. str(0) << endl;
    return true;
  }
};
 
void test_grep() {
  cout << "\nregex_grep for std::string\n";
  const std::string             source   = "My name is same as a name of a month";
  boost::reg_expression<char> regex    = ". ame";
  boost::regex_grep(grep_predicate(), source. begin(), source. end(), regex);
}
 
struct grep_wide_predicate {
  bool operator()(const boost::match_results<std::wstring::const_iterator>& m) {
    wcout << m. str(0) << endl;
    return true;
  }
};
 
void test_wide_grep() {
  cout << "\nregex_grep for std::wstring\n";
  const std::wstring             source   = L"僕は海が好きで山が好きで川も好き";
  boost::reg_expression<wchar_t> regex    = L"(山|川)(.)好き";
  boost::regex_grep(grep_wide_predicate(), source. begin(), source. end(), regex);
}
 
int main() {
  std::locale::global(std::locale("japanese"));
 
  bad_search();
  good_search();
 
  test_search();
  test_wide_search();
  test_grep();
  test_wide_grep();
 
  return 0;
}

 


실행 결과


Regex++ can NOT support multibyte regex
Aルファベットを含むShift_JIS
 
so let's try std::wstring & Regex++
Shift_JIS
 
regex_search for const char*
URL = <a href='http://www.roguewave.com:80/index.html'>here</a>
regex = (([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)
 scheme :http
 host   :www.roguewave.com
 port   :80
 path   :index.html
 
regex_search for std::string
URL = <a href='http://www.roguewave.com:80/index.html'>here</a>
regex = (([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)
 scheme :http
 host   :www.roguewave.com
 port   :80
 path   :index.html
 
regex_search for const wchar_t*
URL = <a href='http://www.roguewave.com:80/index.html'>here</a>
regex = (([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)
 scheme :http
 host   :www.roguewave.com
 port   :80
 path   :index.html
 
regex_search for std::wstring
URL = <a href='http://www.roguewave.com:80/index.html'>here</a>
regex = (([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)
 scheme :http
 host   :www.roguewave.com
 port   :80
 path   :index.html
 
regex_grep for std::string
name
same
name
 
regex_grep for std::wstring
山が好き
川も好き
김백일.cedar [cedar]   2002-08-19 21:31 X
근데 이 코드가 제 C++Builder6 에서는 컴파일이 안되네요. -_-;;
김백일.cedar [cedar]   2002-08-19 21:32 X
boost에 들어 있는 예제들은 이상없이 컴파일이 되지만, 이건 안 되네요. 혹시 성공하신 분?
김백일.cedar [cedar]   2002-08-19 21:33 X
또한 빌더6에서 regex++을 사용하려면 다음 패치를 설치하셔야 합니다.
김백일.cedar [cedar]   2002-09-16 11:08 X
게시판 버그가 있군요... 제글의 내용이 날아가서 다시 올립니다. -_-;;
freeman [builder88]   2005-08-13 22:45 X





아래 코드가 컴파일이 안됩니다.
터보C 쪽에 있는 패치를 해도 에러가 나고,  부스트가 에럽네요......
초보자는 접근 어려버요....... 쩝.


//---------------------------------------


#include <iostream>
#include <locale>

#include <boost/regex.hpp>

using std::cout;
using std::wcout;
using std::endl;

void bad_search() {
  cout << "\nRegex++ can NOT support multibyte regex" << endl;
  std::string source = "アルファベットを含むShift_JIS文字列";
  boost::reg_expression<char> regex = "[a-zA-Z]. *[a-zA-Z]";
  boost::match_results<std::string::const_iterator> results;
  if ( boost::regex_search(source, results, regex) ) {
    cout << results. str(0) << endl;
  }
}

void good_search() {
  cout << "\nso let's try std::wstring & Regex++" << endl;
  std::wstring source = L"アルファベットを含むShift_JIS文字列";
  boost::reg_expression<wchar_t> regex = L"[a-zA-Z]. *[a-zA-Z]";
  boost::match_results<std::wstring::const_iterator> results;
  if ( boost::regex_search(source, results, regex) ) {
    wcout << results. str(0) << endl;
  }
}

void test_search() {

  cout << "\nregex_search for const char*" << endl;
  {
    const char* source = "here"";
    boost::reg_expression<char> regex  = "(([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)";
    boost::match_results<const char*> results;
    cout << "URL = " << source << endl
         << "regex = " << regex. str() << endl;
    if ( boost::regex_search(source, results, regex) ) {
      cout << " scheme :" << results. str(2) << endl
           << " host   :" << results. str(3) << endl
           << " port   :" << results. str(5) << endl
           << " path   :" << results. str(6) << endl;
    } else {
      cout << "no match. " << endl;
    }
  }

  cout << "\nregex_search for std::string" << endl;
  {
    std::string source = "here"";
    boost::reg_expression<std::string::value_type> regex  = "(([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)";
    boost::match_results<std::string::const_iterator> results;
    cout << "URL = " << source << endl
         << "regex = " << regex. str() << endl;
    if ( boost::regex_search(source, results, regex) ) {
      cout << " scheme :" << results. str(2) << endl
           << " host   :" << results. str(3) << endl
           << " port   :" << results. str(5) << endl
           << " path   :" << results. str(6) << endl;
    } else {
      cout << "no match. " << endl;
    }
  }

}

void test_wide_search() {

  cout << "\nregex_search for const wchar_t*" << endl;
  {
    const wchar_t* source = L"here"";
    boost::reg_expression<wchar_t>  regex  = L"(([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)";
    boost::match_results<const wchar_t*> results;
    wcout << L"URL = " << source << endl
          << L"regex = " << regex. str() << endl;
    if ( boost::regex_search(source, results, regex) ) {
      wcout << L" scheme :" << results. str(2) << endl
            << L" host   :" << results. str(3) << endl
            << L" port   :" << results. str(5) << endl
            << L" path   :" << results. str(6) << endl;
    } else {
      cout << "no match. " << endl;
    }
  }

  cout << "\nregex_search for std::wstring" << endl;
  {
    std::wstring source = L"here"";
    boost::reg_expression<std::wstring::value_type> regex  = L"(([a-z]+):)? //([^:/]+)(:([0-9]+))? /([a-zA-Z. 0-9]*)";
    boost::match_results<std::wstring::const_iterator> results;
    wcout << L"URL = " << source << endl
          << L"regex = " << regex. str() << endl;
    if ( boost::regex_search(source, results, regex) ) {
      wcout << L" scheme :" << results. str(2) << endl
            << L" host   :" << results. str(3) << endl
            << L" port   :" << results. str(5) << endl
            << L" path   :" << results. str(6) << endl;
    } else {
      cout << "no match. " << endl;
    }
  }
}

struct grep_predicate {
  bool operator()(const boost::match_results<std::string::const_iterator>& m) {
    cout << m. str(0) << endl;
    return true;
  }
};

void test_grep() {
  cout << "\nregex_grep for std::string\n";
  const std::string             source   = "My name is same as a name of a month";
  boost::reg_expression<char> regex    = ". ame";
  boost::regex_grep(grep_predicate(), source. begin(), source. end(), regex);
}

struct grep_wide_predicate {
  bool operator()(const boost::match_results<std::wstring::const_iterator>& m) {
    wcout << m. str(0) << endl;
    return true;
  }
};

void test_wide_grep() {
  cout << "\nregex_grep for std::wstring\n";
  const std::wstring             source   = L"僕は海が好きで山が好きで川も好き";
  boost::reg_expression<wchar_t> regex    = L"(山|川)(.)好き";
  boost::regex_grep(grep_wide_predicate(), source. begin(), source. end(), regex);
}

int main() {
  std::locale::global(std::locale("japanese"));

  bad_search();
  good_search();

  test_search();
  test_wide_search();
  test_grep();
  test_wide_grep();

  return 0;
}


+ -

관련 글 리스트
35 [Boost]정규 표현식(regular expression) 라이브러리 Regex++의 소개 김백일 22154 2002/08/13
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.