닌자고양이
[C/C++] 문자열 분리 (strpbrk, strtok, find_first_of 사용) 본문
1. C 의 strpbrk 함수를 사용한 delimiter 문자 검색 (가장 빠름)
#include <stdio.h> #include <string.h> int main(void) { char str[] = "This_is,the,,C/C++ world"; char del[] = "/, _"; char *start = str, *stop = start; for (; stop != NULL; start = stop + 1) { if (stop = strpbrk(start, del)) *stop = NULL; printf("%s\n", start); } }
출력:
This is the C C++ world
2. C 의 strtok 함수를 사용한 delimiter 문자 검색
strtok 함수는 strpbrk 와 비슷한데, 발견된 구분자 위치에 자동으로 널문자를 넣어주며 연속된 delimiter에 의한 빈 문자열은 건너뛴다는 점이 다르다. 다만 중첩 수행 및 멀티 쓰레드 안정성이 고려되어 있지 않으므로 strtok_s 또는 strpbrk 를 사용하는 것이 좋다.
#include <stdio.h> #include <string.h> int main(void) { char str[] = "This_is,the,,C/C++ world"; char del[] = "/, _"; char* start = strtok(str, del); while (start) { printf("%s\n", start); start = strtok(NULL, del); } }
출력:
This is the C C++ world
3. C++ 의 find_first_of 함수를 사용한 delimiter 문자 검색
strpbrk 와 같이 빈 문자열을 건너뛰지 않는다.
#include <string> #include <iostream> #include <algorithm> using namespace std; int main() { string str = "This_is,the,,C/C++ world"; string del = "/, _"; for (auto start = str.begin(), stop = start; stop != str.end(); ) { stop = find_first_of(start, str.end(), del.begin(), del.end()); cout << string(start, stop) << endl; if (stop != str.end()) start = stop + 1; } }
출력:
This is the C C++ world
속도는 윈도우 기준으로 find_first_of 가 O(N*M), strpbrk 가 O(N+M) 의 실행 속도를 낸다.
(입력 문자열 수 N, 구분 문자 세트 수 M)
strpbrk 는 입력 요소가 char 타입이기 때문에 구분 문자 세트 M이 최대 256 이므로 M 에 대한 해시 테이블을 사용해 for 루프를 N 번만 수행하도록 최적화할 수 있지만, find_first_of 는 요소의 타입에 무관한 generic 함수이기 때문에 이러한 최적화가 구현되기 어려워 N과 M 의 이중 for 루프를 수행하기 때문으로 보인다.
실제로 확인해 보니 윈도우 C런타임의 구현은 strpbrk 의 경우 해시 테이블(32 X 8개의 비트 마스크로 256가지 문자를 O(1)로 검색)을 사용해 한 단계의 루프만 돌지만 wcspbrk 의 경우 해시 테이블을 사용하지 않는 이중 루프로 구현되어 있다.
번외로 C++ range-based iterator 를 구현한 split_string 클래스
iterator 의 구현 방식이 다소 복잡하지만 사용법은 매우 간단하다.
#pragma once #include <string> #include <iostream> using namespace std; struct split_string { string _str, _del; split_string(string str, string delimiters = " \r\n\t\v\f") { _str = str, _del = delimiters; } struct iterator { char *start, *stop, *del; // start = nullptr 이면 더 이상 검색할 수 없는 상태 (end iterator) iterator operator++() { if (stop) stop = strpbrk(start = stop + 1, del); else start = nullptr; return *this; } bool operator!=(const iterator& other) { return start != other.start || stop != other.stop; } const char* operator*() { if (stop) *stop = '\0'; return start; } }; iterator begin() { return iterator{ &_str[0], strpbrk(&_str[0], &_del[0]), &_del[0] }; } iterator end() { return iterator{ nullptr }; } }; int main() { char str[] = "This_is,the,,C/C++ world"; char del[] = "/, _"; for (auto a : split_string(str, del)) cout << a << endl; }
출력:
This is the C C++ world
'C C++' 카테고리의 다른 글
[C/C++] 문자열 이스케이프 시퀀스 (0) | 2021.03.12 |
---|---|
[C/C++] 반복문 없이 정수의 10진수 자릿수 구하기 (0) | 2020.10.04 |
[C++] ifstream 으로 텍스트 파일 읽는 방법들 (0) | 2020.09.20 |
[C/C++] UTF-8 텍스트 파일 읽는 방법들 (0) | 2020.09.18 |
[C/C++] 중간값 구하기 mid(a, b, c) (0) | 2020.09.09 |