使用带有std :: string的strtok

时间:2008-11-14 06:14:47

标签: c++ strtok

我有一个我想要标记的字符串。 但是C strtok()函数要求我的字符串是char*。 我怎么能这么做呢?

我试过了:

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

失败,因为它将其变为const char*,而不是char*

12 个答案:

答案 0 :(得分:61)

#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;
}

或者,如上所述,使用boost可以获得更大的灵活性。

答案 1 :(得分:20)

  1. 如果您的系统上有boost(我认为这是目前大多数Linux发行版的标准配置),那么它可以使用Tokenizer课程。

  2. 如果没有,那么快速Google会为std :: string调出一个hand-rolled tokenizer,您可以复制并粘贴它。它很短。

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

    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));
        }
      }
    }
    

答案 2 :(得分:15)

复制字符串,对其进行标记,然后释放它。

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

答案 3 :(得分:6)

有一个更优雅的解决方案。

使用std :: string,您可以使用resize()来分配适当大的缓冲区,使用&amp; s [0]来获取指向内部缓冲区的指针。

此时许多优秀的人都会在屏幕上跳起来大喊大叫。但这是事实。大约2年前

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

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

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

但这不正确。实际上该函数用\ 0替换了分隔符的 first 次出现。字符串的大小没有变化。如果我们有这个字符串:

一二三四

我们最终会以

结束
一个\ 0two \ 0 - 三\ 0四

所以我的解决方案很简单:


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

上的讨论

答案 4 :(得分:1)

我认为语言是C或C ++ ......

strtok,IIRC,用\ 0替换分隔符。这就是它不能使用const字符串。 要解决“快速”,如果字符串不是很大,你可以strdup()它。如果你需要保持字符串不变(这是const建议......),这是明智的。

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

答案 5 :(得分:1)

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

答案 6 :(得分:1)

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

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

#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()后,空格从字符串中“删除”,或者调低为不可打印的字符,但长度保持不变。

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

因此,您必须使用本机机制,复制字符串或第三方库,如前所述。

答案 7 :(得分:0)

首先我会说使用boost tokenizer 或者,如果您的数据是空格分隔的,那么字符串流库非常有用。

但上述两点都已被涵盖 因此,作为第三个C-Like替代方案,我建议将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]," ");

答案 8 :(得分:0)

如果您不介意开源,可以使用https://github.com/EdgeCast/json_parser中的子缓冲区和子分类器类。原始字符串保持不变,没有分配和数据复制。我没有编译以下内容,因此可能存在错误。

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(' ');

答案 9 :(得分:0)

失败是因为 str.c_str() 返回常量字符串,但char * strtok (char * str, const char * delimiters )需要使用volatile字符串。所以你需要使用 * const_cast&lt; char &gt; 以使其具有竞争性。 我给你一个完整但很小的程序来使用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可能不适合所有情况,因为 传递给函数的字符串会被分解为更小的字符串 。请更好地了解strtok功能。{/ 3}

strtok如何运作

添加了一些print语句,以便更好地理解每次调用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 然后继续扫描一个分隔符并将其替换为null charater(#gets replace在实际字符串中)并返回 start ,它指向令牌开始字符(即,它返回由null终止的令牌20)。在后续调用中,它从下一个字符开始扫描,如果找到则返回令牌null。随后它返回令牌6,5,3。

答案 10 :(得分:0)

在C ++ 17中,str::string收到data()重载,该指针返回指向可修改缓冲区的指针,因此可以直接在strtok中使用字符串,而不会受到任何黑客攻击:

#include <string>
#include <iostream>
#include <cstring>
#include <cstdlib>

int main()
{
    ::std::string text{"pop dop rop"};
    char const * const psz_delimiter{" "};
    char * psz_token{::std::strtok(text.data(), psz_delimiter)};
    while(nullptr != psz_token)
    {
        ::std::cout << psz_token << ::std::endl;
        psz_token = std::strtok(nullptr, psz_delimiter);
    }
    return EXIT_SUCCESS;
}

输出

  

流行音乐
  dop
  rop

答案 11 :(得分:0)

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

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