안녕하세요? cedar 김백일입니다.
간만에 STL 팁을 또 올려봅니다.
제일 처음에 올렸던 글(string에서의 trim() 구현)에서 설명한 바와 같이
string(basic_string)에는 find_first_of(), find_last_of(), find_first_not_of(), find_last_not_of()와 같은 멤버 함수들이 있어서,
trim()이나 토크나이저(tokenizer)와 같은 기능들을 손쉽게 구현할 수 있습니다.
반면에 string 뿐만 아니라 모든 시퀀스 컨테이너에 적용가능한
일반(generic) 알고리듬에는 find_first_of 밖에는 없습니다.
template <class InputIterator, class ForwardIterator>
InputIterator find_first_of(InputIterator first1, InputIterator last1,
ForwardIterator first2, ForwardIterator last2);
template <class InputIterator, class ForwardIterator, class BinaryPredicate>
InputIterator find_first_of(InputIterator first1, InputIterator last1,
ForwardIterator first2, ForwardIterator last2,
BinaryPredicate comp);
(이 알고리듬의 의미를 다시 한번 설명하면,
매개 변수로서 "검색 범위"와 "포함 집합"를 지정해주면 그 포함 집합의 원소에 해당하는
요소가 나타나는 가장 첫 위치를 검색 범위에서 찾아준다는 의미입니다.)
반면에 "해당되지 않는" 요소가 나타나는 가장 첫 위치를 찾는 알고리듬(find_first_not_of)
는 애석하게도 없습니다.
보통 이런 기능은 문자열에 대해서 많이 쓰기 마련인데요,
문자열이 반드시 string이어야 하는 것은 아닙니다.
경우에 따라 기존 C API이나 VCL의 AnsiString과의 호환을 위해
'구닥다리'인 char 배열이나 vector<char> (char 배열과 메모리 구조가 동일합니다.)를
써야할 경우도 있기 마련입니다.
이런 경우를 위해서,
find_last_of, find_first_not_of, find_last_not_of 알고리듬을 구현해 보았습니다.
(빌더 6 STLport의 find_first_of의 소스 코드를 바탕으로 수정했습니다.)
//-----------------------------------------------------------------------------
template <typename BidirectionalIterator1, typename BidirectionalIterator2>
BidirectionalIterator1 find_last_of(BidirectionalIterator1 first1,
BidirectionalIterator1 last1,
BidirectionalIterator2 first2,
BidirectionalIterator2 last2)
{
do {
--last1;
for (BidirectionalIterator1 iter = first2; iter != last2; ++iter)
if (*last1 == *iter)
return last1;
} while (first1 != last1);
return first1;
}
template <typename BidirectionalIterator1, typename BidirectionalIterator2,
typename BinaryPredicate>
BidirectionalIterator1 find_last_of(BidirectionalIterator1 first1,
BidirectionalIterator1 last1,
BidirectionalIterator2 first2,
BidirectionalIterator2 last2,
BinaryPredicate pred)
{
do {
--last1;
for (BidirectionalIterator1 iter = first2; iter !=last2; ++iter)
if (pred(*last1, *iter))
return last1;
} while (first1 != last1);
return first1;
}
template <typename ForwardIterator1, typename ForwardIterator2>
ForwardIterator1 find_first_not_of(ForwardIterator1 first1,
ForwardIterator1 last1,
ForwardIterator2 first2,
ForwardIterator2 last2)
{
while (first1 != last1 && find(first2, last2, *first1) != last2)
++first1;
return first1;
}
template <typename ForwardIterator1, typename ForwardIterator2,
typename BinaryPredicate>
ForwardIterator1 find_first_not_of(ForwardIterator1 first1,
ForwardIterator1 last1,
ForwardIterator2 first2,
ForwardIterator2 last2,
BinaryPredicate pred)
{
while (first1 != last1 &&
find_if(first2, last2, bind1st(pred, *first1)) != last2)
++first1;
return first1;
}
template <typename BidirectionalIterator1, typename BidirectionalIterator2>
BidirectionalIterator1 find_last_not_of(BidirectionalIterator1 first1,
BidirectionalIterator1 last1,
BidirectionalIterator2 first2,
BidirectionalIterator2 last2)
{
do
--last1;
while (first1 != last1 && find(first2, last2, *last1) != last2);
return last1;
}
template <typename BidirectionalIterator1, typename BidirectionalIterator2,
typename BinaryPredicate>
BidirectionalIterator1 find_last_not_of(BidirectionalIterator1 first1,
BidirectionalIterator1 last1,
BidirectionalIterator2 first2,
BidirectionalIterator2 last2,
BinaryPredicate pred)
{
do
--last1;
while (first1 != last1 &&
find_if(first2, last2, bind1st(pred, *last1)) != last2);
return last1;
}
//-----------------------------------------------------------------------------
다음은 이들 알고리듬을 사용하는 예제 소스코드입니다.
char 배열과 vector<char>에 적용한 예제입니다.
특히 '구닥다리' C 배열과 STL의 알고리듬을 어떻게 조합하는가를 잘보시기 바랍니다.
STL의 반복자(iterator)는 포인터와 같다는 것을 생각하시면 쉽게 이해하실 수 있습니다.
또한 printf와 ostreambuf_iterator의 사용 방법을 대조해서 보시기 바랍니다.
//---------------------------------------------------------------------------
#include <cstring>
#include <iostream>
#pragma hdrstop
#include <vector>
#include <algorithm>
#include <functional>
//---------------------------------------------------------------------------
#pragma argsused
using namespace std;
template <typename Container>
inline Container make(const char s[])
{
return Container(&s[0], &s[strlen(s)]);
}
// (생략: 위의 알고리듬의 원형 선언)
int main(int argc, char* argv[])
{
char *WS = "\t\n ";
int n_WS = strlen(WS);
char *sentence = "This sentence contains five words.";
char *ltrim = " One Word";
char *rtrim = "One Word ";
printf("First word of sentence: \"%.*s\"\n",
find_first_of(sentence, sentence + strlen(sentence), WS, WS + n_WS)
- sentence,
sentence);
printf("Last word of sentence: \"%s\"\n",
find_last_of(sentence, sentence + strlen(sentence), WS, WS + n_WS) + 1);
printf("Left trim of \"%s\" : \"%s\"\n", ltrim,
find_first_not_of(ltrim, ltrim + strlen(ltrim), WS, WS + n_WS));
printf("Left trim of \"%s\" : \"%s\"\n", ltrim,
find_first_not_of(ltrim, ltrim + strlen(ltrim), WS, WS + n_WS,
equal_to<char>()));
printf("Right trim of \"%s\" : \"%.*s\"\n", rtrim,
find_last_not_of(rtrim, rtrim + strlen(rtrim), WS, WS + n_WS)
- rtrim + 1,
rtrim);
printf("Right trim of \"%s\" : \"%.*s\"\n", rtrim,
find_last_not_of(rtrim, rtrim + strlen(rtrim), WS, WS + n_WS,
equal_to<char>()) - rtrim + 1,
rtrim);
vector<char> v_WS = make< vector<char> >(WS);
vector<char> v_sentence = make< vector<char> >(sentence);
vector<char> v_ltrim = make< vector<char> >(ltrim);
vector<char> v_rtrim = make< vector<char> >(rtrim);
cout << "\nFirst word of sentence: \"";
copy(v_sentence.begin(),
find_first_of(v_sentence.begin(), v_sentence.end(), v_WS.begin(), v_WS.end()),
ostreambuf_iterator<char>(cout));
cout << "\"\nLast word of sentence: \"";
copy(find_last_of(v_sentence.begin(), v_sentence.end(), v_WS.begin(), v_WS.end()) + 1,
v_sentence.end(), ostreambuf_iterator<char>(cout));
cout << "\"\nLeft trim of \"" << ltrim << "\" : \"";
copy(find_first_not_of(v_ltrim.begin(), v_ltrim.end(), v_WS.begin(), v_WS.end()),
v_ltrim.end(), ostreambuf_iterator<char>(cout));
cout << "\"\nLeft trim of \"" << ltrim << "\" : \"";
copy(find_first_not_of(v_ltrim.begin(), v_ltrim.end(), v_WS.begin(), v_WS.end(),
equal_to<char>()), v_ltrim.end(), ostreambuf_iterator<char>(cout));
cout << "\"\nRight trim of \"" << rtrim << "\" : \"";
copy(v_rtrim.begin(),
find_last_not_of(v_rtrim.begin(), v_rtrim.end(), v_WS.begin(), v_WS.end()) + 1,
ostreambuf_iterator<char>(cout));
cout << "\"\nRight trim of \"" << rtrim << "\" : \"";
copy(v_rtrim.begin(),
find_last_not_of(v_rtrim.begin(), v_rtrim.end(), v_WS.begin(), v_WS.end(),
equal_to<char>()) + 1, ostreambuf_iterator<char>(cout));
cout << "\"\n";
return 0;
}
//---------------------------------------------------------------------------
출력 결과는 다음과 같습니다.
First word of sentence: "This"
Last word of sentence: "words."
Left trim of " One Word" : "One Word"
Left trim of " One Word" : "One Word"
Right trim of "One Word " : "One Word"
Right trim of "One Word " : "One Word"
First word of sentence: "This"
Last word of sentence: "words."
Left trim of " One Word" : "One Word"
Left trim of " One Word" : "One Word"
Right trim of "One Word " : "One Word"
Right trim of "One Word " : "One Word"
|