정규 표현 라이브러리 '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'>
와 같은 서식에서 나타내집니다.
- "<a"
- 1개 이상의 공백
- 인용부호
- 임의의 문자열(여기가 URL)
- 인용부호
- 0개 이상의 공백
- ">"
그렇다고 하는 순서로 나타나는 문자열 패턴을 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
山が好き
川も好き