使用strtok和std::string

60

我有一个字符串需要分割成标记。 但是 C strtok() 函数要求我的字符串是char*类型的。 如何简单地实现这个需求?

我已经尝试过:

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

它失败的原因是它将其转换为 const char* 而不是 char*


The reason for its failure is that it converts it into a const char* instead of a char*.

请查看此问题:https://dev59.com/cHVD5IYBdhLWcg3wNIvc#55680 - Ferruccio
14个回答

0
首先,我建议使用boost分词器。
另外,如果您的数据是以空格分隔的,则字符串流库非常有用。
但是上述两种方法已经被涵盖了。
因此,作为第三个类C的替代方案,我建议将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]," ");

-1
它失败是因为str.c_str()返回的是常量字符串,但char * strtok (char * str, const char * delimiters )需要的是易变字符串。所以你需要使用*const_cast< char >来使其成为易变字符串。 我给你一个完整但很小的程序,用C的strtok()函数对字符串进行分词。
   #include <iostream>
   #include <string>
   #include <string.h> 
   using namespace std;
   int main() {
       string s="20#6 5, 3";
       // strtok requires volatile string as it modifies the supplied string in order to tokenize it 
       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;
   }

注意:strtok函数并不适用于所有情况,因为传递给函数的字符串会被分成更小的字符串而被修改。请ref以更好地理解strtok功能。

strtok的工作原理

添加了一些打印语句,以更好地理解每次调用strtok时字符串发生的变化及其如何返回标记。

#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;
    cout<<"string: "<<s<<endl;
    tok=strtok(str, "#, " );     
    cout<<"String: "<<s<<"\tToken: "<<tok<<endl;   
    while(tok!=NULL){
        tok=strtok(NULL, "#, " );
        cout<<"String: "<<s<<"\t\tToken: "<<tok<<endl;
    }
    return 0;
}

输出:

string: 20#6 5, 3

String: 206 5, 3    Token: 20
String: 2065, 3     Token: 6
String: 2065 3      Token: 5
String: 2065 3      Token: 3
String: 2065 3      Token: 

strtok 迭代字符串,首先找到非分隔符字符(在本例中为 2),将其标记为令牌 start,然后继续扫描分隔符并用空字符替换它(# 在实际字符串中被替换),并返回指向令牌起始字符的 start(即返回以 null 终止的令牌 20)。在后续调用中,它从下一个字符开始扫描,并在找到令牌时返回该令牌,否则返回 null。随后它返回令牌 6、5、3。


1
FYI: strtok 会改变 s 的值。你不应该使用 const_cast,因为这只是隐藏了一个问题。 - orbitcowboy
使用 c_str() 的结果来修改字符串会导致未定义的行为。 - M.M
@M.M 添加了更多有关 strtok 函数的解释和使用方法。希望这能帮助人们理解何时使用它。 - maximus

-1

将类型转换为(char*)对我有用!

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

1
这样做行不通。strtok将修改str的内部。我想这是用户不想要的副作用。解决方案是创建一个char缓冲区,并首先将str字符串复制到缓冲区中。 - Vivian De Smedt
"got it working"并不正确。它只是让编译器静默了,现在你有了一段(无效的)代码,每个编译器都会将其视为有效代码。 - undefined

-1
使用std::wstring.find_first_of()和std::wstring.substr()。
std::wstring可以被std::string替代,const wchar_t可以被const char替代。
#include <iostream>
using namespace std;

size_t __wstok(wstring * ws_mystring , wstring * ws_word ,  const wchar_t c)
{//size_t __wstok
   wstring mywstr = * ws_mystring;
   size_t found = mywstr.find_first_of(c) ;

      if (found != wstring::npos)
      {//if (found != wstring::npos)
      *ws_word =  mywstr.substr(0,found) ;
      *ws_mystring = mywstr.substr(found+1 , mywstr.size() );
      }//if (found != wstring::npos)

       if (found == wstring::npos)
       *ws_word = mywstr;

return(found);
}//size_t __wstok

// main
int main()
{
wstring a_wstring = L"every good boy deserves fudge"; 
wstring a_word; // the string where the result is stored every time.

    while (__wstok(&a_wstring, &a_word, L' ' ) != wstring::npos)
    {//while
    wcout <<  a_word.c_str() << L"\n\n";
    }//while
   wcout <<  a_word.c_str() << L"\n\n"; // last string

return(0);
}

每个好孩子都应该得到巧克力。

2
欢迎来到这个网站!对于像字符串分词这样简单的任务来说,这里有太多的原始指针算术操作了。如果真的需要 C 语言的 strtok 函数的行为,其他答案已经涵盖了它(基本上使用非const重载的.data())。如果目标只是分词,那么很多指针的技巧可以避免使用。总的来说,我尽量推荐现代 C++ 的最佳实践,而在 2023 年的 C++ 中,我无法真正推荐以这种风格编程。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接