与std ::string一起使用strtok

我有一个string,我想标记。 但是C strtok()函数需要我的string是char* 。 我怎样才能做到这一点?

我试过了:

 token = strtok(str.c_str(), " "); 

失败,因为它把它变成一个const char* ,而不是一个char*

 #include <iostream> #include <string> #include <sstream> int main(){ std::string myText("some-text-to-tokenize"); std::istringstream iss(myText); std::string token; while (std::getline(iss, token, '-')) { std::cout << token << std::endl; } return 0; } 

或者,如前所述,使用提高更多的灵活性。

  1. 如果你的系统上提供了boost (我认为这是目前大多数Linux发行版的标准),它有一个你可以使用的Tokenizer类。

  2. 如果没有,那么一个快速的谷歌为std :: string打开一个手动标记器,你可能只是复制和粘贴。 这很短。

  3. 而且,如果你不喜欢其中的任何一个,那么这里是我写的一个split()函数,使我的生活更轻松。 它会使用“delim”中的任何字符作为分隔符将一个string分解成几部分。 部件被追加到“部件”向量中:

     void split(const string& str, const string& delim, vector<string>& parts) { size_t start, end = 0; while (end < str.size()) { start = end; while (start < str.size() && (delim.find(str[start]) != string::npos)) { start++; // skip initial whitespace } end = start; while (end < str.size() && (delim.find(str[end]) == string::npos)) { end++; // skip to end of word } if (end-start != 0) { // just ignore zero-length strings. parts.push_back(string(str, start, end-start)); } } } 

复制string,标记它,然后释放它。

 char *dup = strdup(str.c_str()); token = strtok(dup, " "); free(dup); 

有一个更优雅的解决scheme。

用std :: string你可以使用resize()来分配一个合适的缓冲区,而&s [0]来获得一个指向内部缓冲区的指针。

在这一点上,许多好的人会跳起来,在屏幕上大喊大叫。 但这是事实。 大约2年前

图书馆工作组决定(在利勒哈默尔会议),就像std :: vector,std :: string也应该正式,不仅在实践中,有一个保证连续的缓冲区。

另一个问题是strtok()是否会增加string的大小。 MSDN文档说:

每次调用strtok都会通过在该调用返回的令牌之后插入一个空字符来修改strToken。

但是这是不正确的。 实际上这个函数用\ 0代替了第一个出现的分隔符。 没有改变的string的大小。 如果我们有这个string:

一二三四

我们将结束

一个\ 0two \ 0 – 三\ 0四

所以我的解决scheme非常简单:

 std::string str("some-text-to-split"); char seps[] = "-"; char *token; token = strtok( &str[0], seps ); while( token != NULL ) { /* Do your thing */ token = strtok( NULL, seps ); } 

阅读http://www.archivum.info/comp.lang.c++/2008-05/02889/does_std::string_have_something_like_CString::GetBuffer上的讨论

编辑:使用const转换用于演示应用于由string :: c_str()返回的指针时strtok()的效果。

不应该使用strtok()因为它修改了可能导致不希望的(如果不是未定义的)行为的标记string,因为Cstring“属于”string实例。

 #include <string> #include <iostream> int main(int ac, char **av) { std::string theString("hello world"); std::cout << theString << " - " << theString.size() << std::endl; //--- this cast *only* to illustrate the effect of strtok() on std::string char *token = strtok(const_cast<char *>(theString.c_str()), " "); std::cout << theString << " - " << theString.size() << std::endl; return 0; } 

在调用strtok() ,空间被从string中“移除”,或者被closures到一个不可打印的字符,但是长度保持不变。

 >./a.out hello world - 11 helloworld - 11 

因此,如前所述,您必须诉诸本地机制,重复string或第三方库。

我想这个语言是C或者C ++ …

strtok,IIRC,用\ 0replace分隔符。 这就是它不能使用一个常量string。 要快速解决这个问题,如果string不是很大,你可以直接strdup()。 如果你需要保持string不变(聪明的build议是什么)。

另一方面,您可能想要使用另一个标记器,可能是手动滚动的,在给定的参数上不太暴力。

假设通过“string”你正在讨论C ++中的std :: string,你可能会看看Boost中的Tokenizer包。

它失败了,因为str.c_str()返回常量string,但char * strtok (char * str, const char * delimiters )需要volatilestring。 所以你需要使用* const_cast <char >来使其变得更加有趣。 我给你一个完整的小程序来使用C strtok()函数来标记string。

 #include <iostream> #include <string> #include <string.h> using namespace std; int main() { string s="20#6 5, 3"; char *str=const_cast< char *>(s.c_str()); char *tok; tok=strtok(str, "#, " ); int arr[4], i=0; while(tok!=NULL){ arr[i++]=stoi(tok); tok=strtok(NULL, "#, " ); } for(int i=0; i<4; i++) cout<<arr[i]<<endl; return 0; } 

首先我会说使用提升标记。
或者,如果您的数据是空间分隔的,那么stringstream库非常有用。

但是以上都已经被覆盖了。
因此,作为第三个类似于C的替代方法,我build议将std :: string复制到缓冲区中进行修改。

 std::string data("The data I want to tokenize"); // Create a buffer of the correct length: std::vector<char> buffer(data.size()+1); // copy the string into the buffer strcpy(&buffer[0],data.c_str()); // Tokenize strtok(&buffer[0]," "); 

如果您不介意开放源码,则可以使用https://github.com/EdgeCast/json_parser中的子缓冲区和子分析器类。; 原始string保持不变,没有分配和不复制数据。 我没有编译以下所以可能会有错误。

 std::string input_string("hello world"); subbuffer input(input_string); subparser flds(input, ' ', subparser::SKIP_EMPTY); while (!flds.empty()) { subbuffer fld = flds.next(); // do something with fld } // or if you know it is only two fields subbuffer fld1 = input.before(' '); subbuffer fld2 = input.sub(fld1.length() + 1).ltrim(' ');