C++에서 문자열을 토큰화하려면 어떻게 해야 하나요?
Java에는 편리한 분할 방법이 있습니다.
String str = "The quick brown fox";
String[] results = str.split(" ");
C++에서 쉽게 할 수 있는 방법이 있나요?
Boost 토큰라이저 클래스는 다음과 같은 작업을 매우 단순하게 만들 수 있습니다.
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int, char**)
{
string text = "token, test string";
char_separator<char> sep(", ");
tokenizer< char_separator<char> > tokens(text, sep);
BOOST_FOREACH (const string& t, tokens) {
cout << t << "." << endl;
}
}
C++11용으로 업데이트됨:
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int, char**)
{
string text = "token, test string";
char_separator<char> sep(", ");
tokenizer<char_separator<char>> tokens(text, sep);
for (const auto& t : tokens) {
cout << t << "." << endl;
}
}
여기 정말 간단한 것이 있습니다.
#include <vector>
#include <string>
using namespace std;
vector<string> split(const char *str, char c = ' ')
{
vector<string> result;
do
{
const char *begin = str;
while(*str != c && *str)
str++;
result.push_back(string(begin, str));
} while (0 != *str++);
return result;
}
C++ 표준 라이브러리 알고리즘은 콘크리트 컨테이너가 아닌 반복기를 기반으로 합니다.이 때문에 와 같은 것을 하는 것은 .split
C++ 표 c c c c c c c c c c c c c c 。아무도 이것이 편리하다고 주장하지 않습니다.지만반 반입 ?입 입?? ??? std::vector<std::basic_string<…>>
그럴 수도 있지만, 그 후에는 (장황하고 비용이 많이 들 가능성이 있는) 할당을 수행해야 합니다.
대신 C++는 임의의 복잡한 딜리미터를 기반으로 문자열을 분할하는 수많은 방법을 제공하지만 다른 언어만큼 적절하게 캡슐화된 것은 없습니다.블로그 투고에는 다양한 방법이 가득합니다.
간단히 말해, 를 반복해 사용할 수 있습니다.std::string::npos
를 사용하여 내용을 추출합니다.
공백으로 분할하기 위한 보다 유연한 버전(및 관용적이지만 기본 버전)은 다음과 같이 사용됩니다.
auto iss = std::istringstream{"The quick brown fox"};
auto str = std::string{};
while (iss >> str) {
process(str);
}
를 사용하면 문자열 스트림의 내용을 반복 범위 생성자를 사용하여 벡터에 복사할 수도 있습니다.
여러 라이브러리(Boost 등)Tokenizer)는 특정 토큰을 제공합니다.
보다 고도의 분할에는 정규식이 필요합니다.C++ 는, 특히 다음의 목적을 위해서 를 제공합니다.
auto const str = "The quick brown fox"s;
auto const re = std::regex{R"(\s+)"};
auto const vec = std::vector<std::string>(
std::sregex_token_iterator{begin(str), end(str), re, -1},
std::sregex_token_iterator{}
);
하나의 은 '먹다'를 사용하는 입니다.getline
를 들면 를를: :
stringstream ss("bla bla");
string s;
while (getline(ss, s, ' ')) {
cout << s << endl;
}
원하시면 간단하게split()
vector<string>
정말 유용하게 쓰입니다.
strtok을 사용합니다.내 생각에, strtok이 당신에게 필요한 것을 제공하지 않는 한 토큰화에 관한 클래스를 만들 필요는 없다.그렇지 않을 수도 있지만, 15년 이상 C와 C++로 다양한 해석 코드를 작성하면서 저는 항상 strtok을 사용해 왔습니다.여기 예가 있습니다.
char myString[] = "The quick brown fox";
char *p = strtok(myString, " ");
while (p) {
printf ("Token: %s\n", p);
p = strtok(NULL, " ");
}
몇 가지 주의사항(필요에 맞지 않을 수 있음)이 프로세스에서는 문자열이 "파괴"됩니다.즉, EOS 문자가 딜리미터 스폿에 인라인으로 배치됩니다.올바른 사용법을 사용하려면 일정하지 않은 버전의 문자열을 만들어야 할 수 있습니다.해석 중간 구분자 목록을 변경할 수도 있습니다.
제 생각에, 위의 코드는 별도의 클래스를 작성하는 것보다 훨씬 간단하고 사용하기 쉽습니다.저에게 이것은 언어가 제공하는 기능 중 하나이며, 훌륭하고 깔끔하게 기능합니다.단순히 "C 기반" 솔루션일 뿐입니다.적절하고 간단하며 많은 추가 코드를 쓸 필요가 없습니다:-)
스트림, 반복기 및 복사 알고리즘을 사용하여 이 작업을 직접 수행할 수 있습니다.
#include <string>
#include <vector>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>
#include <sstream>
#include <algorithm>
int main()
{
std::string str = "The quick brown fox";
// construct a stream from the string
std::stringstream strstr(str);
// use stream iterators to copy the stream to the vector as whitespace separated strings
std::istream_iterator<std::string> it(strstr);
std::istream_iterator<std::string> end;
std::vector<std::string> results(it, end);
// send the vector to stdout.
std::ostream_iterator<std::string> oit(std::cout);
std::copy(results.begin(), results.end(), oit);
}
★★★★★★★★★를 사용한 regex_token_iterator
s:
#include <iostream>
#include <regex>
#include <string>
using namespace std;
int main()
{
string str("The quick brown fox");
regex reg("\\s+");
sregex_token_iterator iter(str.begin(), str.end(), reg, -1);
sregex_token_iterator end;
vector<string> vec(iter, end);
for (auto a : vec)
{
cout << a << endl;
}
}
악의는 없지만, 그런 간단한 문제 때문에 일을 너무 복잡하게 만들고 있어요.Boost를 사용하는 이유는 여러 가지가 있습니다.하지만 이렇게 간단한 것은 20# 썰매로 파리를 치는 것과 같습니다.
void
split( vector<string> & theStringVector, /* Altered/returned value */
const string & theString,
const string & theDelimiter)
{
UASSERT( theDelimiter.size(), >, 0); // My own ASSERT macro.
size_t start = 0, end = 0;
while ( end != string::npos)
{
end = theString.find( theDelimiter, start);
// If at end, use length=maxLength. Else use length=end-start.
theStringVector.push_back( theString.substr( start,
(end == string::npos) ? string::npos : end - start));
// If at end, use start=maxSize. Else use start=end+delimiter.
start = ( ( end > (string::npos - theDelimiter.size()) )
? string::npos : end + theDelimiter.size());
}
}
예를 들어 (더그의 경우)
#define SHOW(I,X) cout << "[" << (I) << "]\t " # X " = \"" << (X) << "\"" << endl
int
main()
{
vector<string> v;
split( v, "A:PEP:909:Inventory Item", ":" );
for (unsigned int i = 0; i < v.size(); i++)
SHOW( i, v[i] );
}
네, split()는 새로운 벡터를 전달하지 않고 반환할 수 있습니다.포장하고 과부하시키는 것은 사소한 일이다.그러나 내가 하고 있는 일에 따라서는 항상 새로운 오브젝트를 만드는 것보다 기존의 오브젝트를 재사용하는 것이 더 낫다는 것을 알게 되는 경우가 많습니다.(그 사이의 벡터를 비우는 것을 잊지 않는 한!)
참고 자료: http://www.cplusplus.com/reference/string/string/
(원래는 더그의 질문에 대한 답변을 쓰고 있었습니다.C++ 구분자를 기반으로 한 문자열 수정 및 추출(닫힘).하지만 마틴 요크가 포인터로 질문을 끝냈기 때문에...코드를 일반화합니다.)
부스트에는 강력한 분할 기능이 있습니다. 부스트::알고리즘::분할.
샘플 프로그램:
#include <vector>
#include <boost/algorithm/string.hpp>
int main() {
auto s = "a,b, c ,,e,f,";
std::vector<std::string> fields;
boost::split(fields, s, boost::is_any_of(","));
for (const auto& field : fields)
std::cout << "\"" << field << "\"\n";
return 0;
}
출력:
"a"
"b"
" c "
""
"e"
"f"
""
은 STL을 사용한 솔루션입니다.입std::find
★★★★★★★★★★★★★★★★★」std::find_first_not_of
구분 기호(예: 공백이나 마침표)의 반복과 선행 구분 기호 및 후행 구분 기호를 처리합니다.
#include <string>
#include <vector>
void tokenize(std::string str, std::vector<string> &token_v){
size_t start = str.find_first_not_of(DELIMITER), end=start;
while (start != std::string::npos){
// Find next occurence of delimiter
end = str.find(DELIMITER, start);
// Push back the token found into vector
token_v.push_back(str.substr(start, end-start));
// Skip all occurences of the delimiter to find new start
start = str.find_first_not_of(DELIMITER, end);
}
}
라이브로 시험해 보세요!
C++ 솔루션을 요구하고 있는 것은 알고 있습니다만, 이것이 도움이 된다고 생각할 수도 있습니다.
큐트
#include <QString>
...
QString str = "The quick brown fox";
QStringList results = str.split(" ");
이 예에서 Boost보다 좋은 점은 게시물 코드에 대한 직접적인 일대일 매핑이라는 것입니다.
다음은 원하는 작업을 수행할 수 있는 토큰라이저 클래스의 예입니다.
//Header file
class Tokenizer
{
public:
static const std::string DELIMITERS;
Tokenizer(const std::string& str);
Tokenizer(const std::string& str, const std::string& delimiters);
bool NextToken();
bool NextToken(const std::string& delimiters);
const std::string GetToken() const;
void Reset();
protected:
size_t m_offset;
const std::string m_string;
std::string m_token;
std::string m_delimiters;
};
//CPP file
const std::string Tokenizer::DELIMITERS(" \t\n\r");
Tokenizer::Tokenizer(const std::string& s) :
m_string(s),
m_offset(0),
m_delimiters(DELIMITERS) {}
Tokenizer::Tokenizer(const std::string& s, const std::string& delimiters) :
m_string(s),
m_offset(0),
m_delimiters(delimiters) {}
bool Tokenizer::NextToken()
{
return NextToken(m_delimiters);
}
bool Tokenizer::NextToken(const std::string& delimiters)
{
size_t i = m_string.find_first_not_of(delimiters, m_offset);
if (std::string::npos == i)
{
m_offset = m_string.length();
return false;
}
size_t j = m_string.find_first_of(delimiters, i);
if (std::string::npos == j)
{
m_token = m_string.substr(i);
m_offset = m_string.length();
return true;
}
m_token = m_string.substr(i, j - i);
m_offset = j;
return true;
}
예:
std::vector <std::string> v;
Tokenizer s("split this string", " ");
while (s.NextToken())
{
v.push_back(s.GetToken());
}
pystring은 분할 메서드를 포함한 Python의 문자열 함수를 구현하는 작은 라이브러리입니다.
#include <string>
#include <vector>
#include "pystring.h"
std::vector<std::string> chunks;
pystring::split("this string", chunks);
// also can specify a separator
pystring::split("this-string", chunks, "-");
저는 비슷한 질문 때문에 이 답변을 올렸습니다.
수레바퀴를 재발명하지 마세요.저는 많은 라이브러리를 사용해 왔습니다만, 지금까지 본 것 중 가장 빠르고 유연한 것은 C++ String Toolkit Library입니다.
다음으로 스택오버플로우 이외의 장소에 투고하고 있는 사용 방법의 예를 나타냅니다.
#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp>
const char *whitespace = " \t\r\n\f";
const char *whitespace_and_punctuation = " \t\r\n\f;,=";
int main()
{
{ // normal parsing of a string into a vector of strings
std::string s("Somewhere down the road");
std::vector<std::string> result;
if( strtk::parse( s, whitespace, result ) )
{
for(size_t i = 0; i < result.size(); ++i )
std::cout << result[i] << std::endl;
}
}
{ // parsing a string into a vector of floats with other separators
// besides spaces
std::string s("3.0, 3.14; 4.0");
std::vector<float> values;
if( strtk::parse( s, whitespace_and_punctuation, values ) )
{
for(size_t i = 0; i < values.size(); ++i )
std::cout << values[i] << std::endl;
}
}
{ // parsing a string into specific variables
std::string s("angle = 45; radius = 9.9");
std::string w1, w2;
float v1, v2;
if( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2) )
{
std::cout << "word " << w1 << ", value " << v1 << std::endl;
std::cout << "word " << w2 << ", value " << v2 << std::endl;
}
}
return 0;
}
Adam Pierce의 답변은 Hand-Spun 토큰라이저를 사용하여const char*
의 엔드 리터레이터를 증가시키는 것은 정의되어 있지 않기 때문에 리터레이터를 사용하는 것은 조금 더 문제가 있습니다.그렇긴 하지만string str{ "The quick brown fox" }
우리는 이것을 확실히 달성할 수 있습니다.
auto start = find(cbegin(str), cend(str), ' ');
vector<string> tokens{ string(cbegin(str), start) };
while (start != cend(str)) {
const auto finish = find(++start, cend(str), ' ');
tokens.push_back(string(start, finish));
start = finish;
}
On Freund가 제안하는 바와 같이 표준 기능을 사용하여 복잡성을 추상화하려는 경우 다음과 같은 간단한 옵션이 있습니다.
vector<string> tokens;
for (auto i = strtok(data(str), " "); i != nullptr; i = strtok(nullptr, " ")) tokens.push_back(i);
없는 에는 C++17로 data(str)
다음 예시와 같이 http://ideone.com/8kAGoa
예에서는 ,strtok
각 토큰에 동일한 구분 기호를 사용할 필요가 없습니다.그러나 이 장점과 함께 다음과 같은 몇 가지 단점이 있습니다.
strtok
할 수 .strings
"A" : "A" 중nullptr
하려면 , 가 있습니다.string
★★★★★★★★★★★★★★」char*
토큰화에는 패스할 필요가 있습니다(다만, 다음과 같은 비표준 실장도 있습니다).- 이유로 ★★★★★★ 。
strtok
여러 스레드에서 동시에 사용할 수 없습니다(단, Visual Studio의 구현은 스레드 세이프입니다). - " "
strtok
는 을 합니다.string
하고 있기 에는 할 수 .const string
s,const char*
s 문자열를 사용하여 중 합니다.strtok
'동작'으로 합니다.string
해야 할 는 누구입니까str
할 것 할 수 있을 것 같아요.을 사용법
c++20은 다음과 같은 기능을 제공합니다.split_view
문자열을 토큰화하려면 , 비파괴적인 방법으로https://topanswers.xyz/cplus?q=749#a874
된 '토큰화'를 할 수 .vector
않으면 in-place로 할 수 을 의미합니다.const vector<string> tokens
이 기능과 공백 구분 기호를 사용할 수 있는 기능은 를 사용하여 사용할 수 있습니다. 예를 들어 다음과 같습니다.const string str{ "The quick \tbrown \nfox" }
이치노
istringstream is{ str };
const vector<string> tokens{ istream_iterator<string>(is), istream_iterator<string>() };
의 istringstream
2가지 으로 이 은 '2'의 있습니다.string
★★★★★★ 。
위의 옵션 중 어느 것도 토큰화 요구에 충분히 유연하지 않은 경우, 가장 유연한 옵션은 물론 이 유연성과 함께 를 사용하는 것입니다.그 결과 비용이 증가하지만, 이 경우에도 이 옵션이 숨겨져 있을 가능성이 있습니다.string
예를 , 않은 하고 공백을 먹도록 . 이렇게 입력해 .const string str{ "The ,qu\\,ick ,\tbrown, fox" }
이치노
const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" };
const vector<string> tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() };
C++ 범위(C++20에서 허용되는 제한된 기능이 아닌 전체 범위 v3 라이브러리)를 사용하는 경우 다음과 같이 수행할 수 있습니다.
auto results = str | ranges::views::tokenize(" ",1);
...그리고 이건 게으름뱅이로군.또는 벡터를 이 범위로 설정할 수 있습니다.
auto results = str | ranges::views::tokenize(" ",1) | ranges::to<std::vector>();
(m과 O이 걸립니다.str
에는 n개의 문자가 있으며 m개의 단어를 구성합니다.
라이브러리의 토큰화 예를 참조하십시오.
이 예를 확인해 주세요.도움이 될 거야
#include <iostream>
#include <sstream>
using namespace std;
int main ()
{
string tmps;
istringstream is ("the dellimiter is the space");
while (is.good ()) {
is >> tmps;
cout << tmps << "\n";
}
return 0;
}
MFC/ATL은 매우 훌륭한 토큰라이저를 갖추고 있습니다.MSDN에서:
CAtlString str( "%First Second#Third" );
CAtlString resToken;
int curPos= 0;
resToken= str.Tokenize("% #",curPos);
while (resToken != "")
{
printf("Resulting token: %s\n", resToken);
resToken= str.Tokenize("% #",curPos);
};
Output
Resulting Token: First
Resulting Token: Second
Resulting Token: Third
C를 사용할 의향이 있다면 strtok 기능을 사용할 수 있습니다.사용할 때는 멀티스레딩 문제에 주의해야 합니다.
간단한 용도는 다음과 같습니다.
unsigned TokenizeString(const std::string& i_source,
const std::string& i_seperators,
bool i_discard_empty_tokens,
std::vector<std::string>& o_tokens)
{
unsigned prev_pos = 0;
unsigned pos = 0;
unsigned number_of_tokens = 0;
o_tokens.clear();
pos = i_source.find_first_of(i_seperators, pos);
while (pos != std::string::npos)
{
std::string token = i_source.substr(prev_pos, pos - prev_pos);
if (!i_discard_empty_tokens || token != "")
{
o_tokens.push_back(i_source.substr(prev_pos, pos - prev_pos));
number_of_tokens++;
}
pos++;
prev_pos = pos;
pos = i_source.find_first_of(i_seperators, pos);
}
if (prev_pos < i_source.length())
{
o_tokens.push_back(i_source.substr(prev_pos));
number_of_tokens++;
}
return number_of_tokens;
}
비겁한 면책 조항:바이너리 파일, 소켓 또는 API 호출(I/O 카드, 카메라)을 통해 데이터가 들어오는 실시간 데이터 처리 소프트웨어를 작성합니다.기동시에 외부 컨피규레이션파일을 읽는 것보다 복잡하거나 시간이 걸리는 경우는, 이 기능을 사용하지 않습니다.
정규표현 라이브러리를 사용하여 정규표현을 사용하여 해결할 수 있습니다.
expression(\w+) 및 \1 변수(또는 정규 표현의 라이브러리 구현에 따라 $1)를 사용합니다.
너무 복잡한 제안들이 많네요.다음 간단한 std::string 솔루션을 사용해 보십시오.
using namespace std;
string someText = ...
string::size_type tokenOff = 0, sepOff = tokenOff;
while (sepOff != string::npos)
{
sepOff = someText.find(' ', sepOff);
string::size_type tokenLen = (sepOff == string::npos) ? sepOff : sepOff++ - tokenOff;
string token = someText.substr(tokenOff, tokenLen);
if (!token.empty())
/* do something with token */;
tokenOff = sepOff;
}
줄 요.>>
문자열 스트림의 연산자는 다음과 같습니다.
string word; sin >> word;
이 질문은 이미 답한 것으로 알고 있지만, 저는 기여하고 싶습니다.내 솔루션은 다소 단순할 수 있지만, 내가 생각해낸 것은 다음과 같다.
vector<string> get_words(string const& text, string const& separator)
{
vector<string> result;
string tmp = text;
size_t first_pos = 0;
size_t second_pos = tmp.find(separator);
while (second_pos != string::npos)
{
if (first_pos != second_pos)
{
string word = tmp.substr(first_pos, second_pos - first_pos);
result.push_back(word);
}
tmp = tmp.substr(second_pos + separator.length());
second_pos = tmp.find(separator);
}
result.push_back(tmp);
return result;
}
제 코드에 더 나은 방법이 있거나 잘못된 방법이 있다면 코멘트를 주세요.
업데이트: 일반 구분 기호 추가
다음은 빈 토큰을 포함할지(strsep 등) 제외할지(strtok 등) 제어할 수 있는 방법입니다.
#include <string.h> // for strchr and strlen
/*
* want_empty_tokens==true : include empty tokens, like strsep()
* want_empty_tokens==false : exclude empty tokens, like strtok()
*/
std::vector<std::string> tokenize(const char* src,
char delim,
bool want_empty_tokens)
{
std::vector<std::string> tokens;
if (src and *src != '\0') // defensive
while( true ) {
const char* d = strchr(src, delim);
size_t len = (d)? d-src : strlen(src);
if (len or want_empty_tokens)
tokens.push_back( std::string(src, len) ); // capture token
if (d) src += len+1; else break;
}
return tokens;
}
속도를 중시하는 너덜너덜한 사람이 여기 SO에 있는데 아무도 딜리미터에 대해 컴파일 시간 생성 룩업 테이블을 사용하는 버전을 제시하지 않은 것은 이상하다고 생각합니다(아래의 구현 예).룩업 테이블과 반복기를 사용하면 효율 면에서 std::regex를 능가할 수 있습니다.regex를 능가할 필요가 없다면 C++11 기준으로 표준이며 매우 유연합니다.
일부에서는 이미 regex를 제안하고 있습니다만, noobs의 경우 OP가 기대하는 대로 실행하는 패키지화된 예를 다음에 나타냅니다.
std::vector<std::string> split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){
std::smatch m{};
std::vector<std::string> ret{};
while (std::regex_search (it,end,m,e)) {
ret.emplace_back(m.str());
std::advance(it, m.position() + m.length()); //next start position = match position + match length
}
return ret;
}
std::vector<std::string> split(const std::string &s, std::regex e = std::regex{"\\w+"}){ //comfort version calls flexible version
return split(s.cbegin(), s.cend(), std::move(e));
}
int main ()
{
std::string str {"Some people, excluding those present, have been compile time constants - since puberty."};
auto v = split(str);
for(const auto&s:v){
std::cout << s << std::endl;
}
std::cout << "crazy version:" << std::endl;
v = split(str, std::regex{"[^e]+"}); //using e as delim shows flexibility
for(const auto&s:v){
std::cout << s << std::endl;
}
return 0;
}
모든 문자가 8비트여야 한다는 제약조건을 받아들여야 할 경우 메타프로그래밍을 사용하여 컴파일 시 룩업테이블을 만들 수 있습니다.
template<bool...> struct BoolSequence{}; //just here to hold bools
template<char...> struct CharSequence{}; //just here to hold chars
template<typename T, char C> struct Contains; //generic
template<char First, char... Cs, char Match> //not first specialization
struct Contains<CharSequence<First, Cs...>,Match> :
Contains<CharSequence<Cs...>, Match>{}; //strip first and increase index
template<char First, char... Cs> //is first specialization
struct Contains<CharSequence<First, Cs...>,First>: std::true_type {};
template<char Match> //not found specialization
struct Contains<CharSequence<>,Match>: std::false_type{};
template<int I, typename T, typename U>
struct MakeSequence; //generic
template<int I, bool... Bs, typename U>
struct MakeSequence<I,BoolSequence<Bs...>, U>: //not last
MakeSequence<I-1, BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};
template<bool... Bs, typename U>
struct MakeSequence<0,BoolSequence<Bs...>,U>{ //last
using Type = BoolSequence<Bs...>;
};
template<typename T> struct BoolASCIITable;
template<bool... Bs> struct BoolASCIITable<BoolSequence<Bs...>>{
/* could be made constexpr but not yet supported by MSVC */
static bool isDelim(const char c){
static const bool table[256] = {Bs...};
return table[static_cast<int>(c)];
}
};
using Delims = CharSequence<'.',',',' ',':','\n'>; //list your custom delimiters here
using Table = BoolASCIITable<typename MakeSequence<256,BoolSequence<>,Delims>::Type>;
a a a a a a a 를 만들다getNextToken
기능은 간단합니다.
template<typename T_It>
std::pair<T_It,T_It> getNextToken(T_It begin,T_It end){
begin = std::find_if(begin,end,std::not1(Table{})); //find first non delim or end
auto second = std::find_if(begin,end,Table{}); //find first delim or end
return std::make_pair(begin,second);
}
사용법도 간단합니다.
int main() {
std::string s{"Some people, excluding those present, have been compile time constants - since puberty."};
auto it = std::begin(s);
auto end = std::end(s);
while(it != std::end(s)){
auto token = getNextToken(it,end);
std::cout << std::string(token.first,token.second) << std::endl;
it = token.second;
}
return 0;
}
다음은 라이브 예시입니다.http://ideone.com/GKtkLQ
boost::make_find_iterator를 이용할 수 있습니다.다음과 같은 경우:
template<typename CH>
inline vector< basic_string<CH> > tokenize(
const basic_string<CH> &Input,
const basic_string<CH> &Delimiter,
bool remove_empty_token
) {
typedef typename basic_string<CH>::const_iterator string_iterator_t;
typedef boost::find_iterator< string_iterator_t > string_find_iterator_t;
vector< basic_string<CH> > Result;
string_iterator_t it = Input.begin();
string_iterator_t it_end = Input.end();
for(string_find_iterator_t i = boost::make_find_iterator(Input, boost::first_finder(Delimiter, boost::is_equal()));
i != string_find_iterator_t();
++i) {
if(remove_empty_token){
if(it != i->begin())
Result.push_back(basic_string<CH>(it,i->begin()));
}
else
Result.push_back(basic_string<CH>(it,i->begin()));
it = i->end();
}
if(it != it_end)
Result.push_back(basic_string<CH>(it,it_end));
return Result;
}
여기 Swiss® Army Knife가 있습니다.여백에 따라 줄을 나누고, 단일 및 이중으로 감긴 줄을 설명하며, 결과에서 해당 문자를 제거할 수 있습니다.대부분의 코드 스니펫을 생성하기 위해 RegexBuddy 4.x를 사용했지만, stripping quote 등의 커스텀 처리를 추가했습니다.
#include <string>
#include <locale>
#include <regex>
std::vector<std::wstring> tokenize_string(std::wstring string_to_tokenize) {
std::vector<std::wstring> tokens;
std::wregex re(LR"(("[^"]*"|'[^']*'|[^"' ]+))", std::regex_constants::collate);
std::wsregex_iterator next( string_to_tokenize.begin(),
string_to_tokenize.end(),
re,
std::regex_constants::match_not_null );
std::wsregex_iterator end;
const wchar_t single_quote = L'\'';
const wchar_t double_quote = L'\"';
while ( next != end ) {
std::wsmatch match = *next;
const std::wstring token = match.str( 0 );
next++;
if (token.length() > 2 && (token.front() == double_quote || token.front() == single_quote))
tokens.emplace_back( std::wstring(token.begin()+1, token.begin()+token.length()-1) );
else
tokens.emplace_back(token);
}
return tokens;
}
https://stackoverflow.com/a/50247503/3976739의 간결한 버전(그리고 조금 효율적일 수도 있음)을 작성했습니다.도움이 됐으면 좋겠어요.
void StrTokenizer(string& source, const char* delimiter, vector<string>& Tokens)
{
size_t new_index = 0;
size_t old_index = 0;
while (new_index != std::string::npos)
{
new_index = source.find(delimiter, old_index);
Tokens.emplace_back(source.substr(old_index, new_index-old_index));
if (new_index != std::string::npos)
old_index = ++new_index;
}
}
토큰화할 입력 문자열의 최대 길이를 알고 있으면 이를 이용하여 매우 빠른 버전을 구현할 수 있습니다.아래는 Strtok()와 Jon Bentley의 Programming Perls 제2판 15장에서 설명한 "suffix 배열" 데이터 구조에서 영감을 얻은 기본 아이디어를 스케치하고 있습니다.이 경우 C++ 클래스는 일부 구성 및 사용 편의성만 제공합니다.표시된 구현은 토큰의 선행 및 후행 공백 문자를 삭제하기 위해 쉽게 확장할 수 있습니다.
기본적으로 구분 문자를 문자열 끝의 '\0' 문자로 대체하고 수정된 문자열을 사용하여 토큰에 대한 포인터를 설정할 수 있습니다.문자열이 구분자로만 구성되어 있는 극단적인 경우 문자열 길이 더하기1개의 빈 토큰이 생성됩니다.변경할 문자열을 복제하는 것이 실용적입니다.
헤더 파일:
class TextLineSplitter
{
public:
TextLineSplitter( const size_t max_line_len );
~TextLineSplitter();
void SplitLine( const char *line,
const char sep_char = ',',
);
inline size_t NumTokens( void ) const
{
return mNumTokens;
}
const char * GetToken( const size_t token_idx ) const
{
assert( token_idx < mNumTokens );
return mTokens[ token_idx ];
}
private:
const size_t mStorageSize;
char *mBuff;
char **mTokens;
size_t mNumTokens;
inline void ResetContent( void )
{
memset( mBuff, 0, mStorageSize );
// mark all items as empty:
memset( mTokens, 0, mStorageSize * sizeof( char* ) );
// reset counter for found items:
mNumTokens = 0L;
}
};
구현 파일:
TextLineSplitter::TextLineSplitter( const size_t max_line_len ):
mStorageSize ( max_line_len + 1L )
{
// allocate memory
mBuff = new char [ mStorageSize ];
mTokens = new char* [ mStorageSize ];
ResetContent();
}
TextLineSplitter::~TextLineSplitter()
{
delete [] mBuff;
delete [] mTokens;
}
void TextLineSplitter::SplitLine( const char *line,
const char sep_char /* = ',' */,
)
{
assert( sep_char != '\0' );
ResetContent();
strncpy( mBuff, line, mMaxLineLen );
size_t idx = 0L; // running index for characters
do
{
assert( idx < mStorageSize );
const char chr = line[ idx ]; // retrieve current character
if( mTokens[ mNumTokens ] == NULL )
{
mTokens[ mNumTokens ] = &mBuff[ idx ];
} // if
if( chr == sep_char || chr == '\0' )
{ // item or line finished
// overwrite separator with a 0-terminating character:
mBuff[ idx ] = '\0';
// count-up items:
mNumTokens ++;
} // if
} while( line[ idx++ ] );
}
사용 시나리오는 다음과 같습니다.
// create an instance capable of splitting strings up to 1000 chars long:
TextLineSplitter spl( 1000 );
spl.SplitLine( "Item1,,Item2,Item3" );
for( size_t i = 0; i < spl.NumTokens(); i++ )
{
printf( "%s\n", spl.GetToken( i ) );
}
출력:
Item1
Item2
Item3
언급URL : https://stackoverflow.com/questions/53849/how-do-i-tokenize-a-string-in-c
'programing' 카테고리의 다른 글
WPF에 Design Mode 속성이 있습니까? (0) | 2023.04.22 |
---|---|
시스템 추가 중Web.클래스 라이브러리의 스크립트 참조 (0) | 2023.04.22 |
파일은 유니버설 (3 슬라이스)입니다만, iOS의 스태틱 라이브러리의 ARMv7-s 슬라이스 에러는 포함되어 있지 않습니다만, 생략해도 될까요? (0) | 2023.04.22 |
WPF ListView 비활성 선택 항목 색상 (0) | 2023.04.22 |
일련의 문서 템플릿에서 Word 문서 생성(Excel VBA) (0) | 2023.04.22 |