如何在C ++中对字符串进行标记?

c++ string split tokenize

509935 观看

30回复

294694 作者的声誉

Java有一个方便的拆分方法:

String str = "The quick brown fox";
String[] results = str.split(" ");

有没有一种简单的方法在C ++中执行此操作?

作者: Bill the Lizard 的来源 发布者: 2008 年 9 月 10 日

回应 30


1

29115 作者的声誉

There is no direct way to do this. Refer this code project source code to find out how to build a class for this.

作者: Niyaz 发布者: 2008 年 9 月 10 日

22

7012 作者的声誉

这是一个示例tokenizer类,可以执行您想要的操作

//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());
}
作者: vzczc 发布者: 2008 年 9 月 10 日

134

400424 作者的声誉

决定

您可以使用该std::string::find方法轻松构建简单的案例。但是,看看Boost.Tokenizer。这很棒。Boost通常有一些非常酷的字符串工具。

作者: Konrad Rudolph 发布者: 2008 年 9 月 10 日

4

4004 作者的声誉

如果您愿意使用C,则可以使用strtok函数。使用它时应注意多线程问题。

作者: On Freund 发布者: 2008 年 9 月 10 日

166

22350 作者的声誉

这是一个非常简单的:

#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;
}
作者: Adam Pierce 发布者: 2008 年 9 月 10 日

4

42959 作者的声誉

我认为这就是>>字符串流上的运算符用于:

string word; sin >> word;
作者: Daren Thomas 发布者: 2008 年 9 月 10 日

80

14134 作者的声誉

您可以使用流,迭代器和复制算法直接执行此操作。

#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);
}
作者: KeithB 发布者: 2008 年 9 月 10 日

107

8774 作者的声誉

使用strtok。在我看来,没有必要建立一个围绕标记化的类,除非strtok没有为你提供你需要的东西。它可能没有,但是在用C和C ++编写各种解析代码的15年多来,我总是使用strtok。这是一个例子

char myString[] = "The quick brown fox";
char *p = strtok(myString, " ");
while (p) {
    printf ("Token: %s\n", p);
    p = strtok(NULL, " ");
}

一些警告(可能不适合您的需要)。字符串在此过程中被“销毁”,这意味着EOS字符内嵌在分隔符中。正确使用可能需要您创建字符串的非const版本。您还可以在解析时更改分隔符列表。

在我看来,上面的代码比为它编写一个单独的类要简单得多,也更容易使用。对我来说,这是该语言提供的那些功能之一,并且它干净利落地完成了它。它只是一个“基于C”的解决方案。这很合适,很简单,而且你不必编写很多额外的代码:-)

作者: Mark 发布者: 2008 年 9 月 10 日

186

80527 作者的声誉

升压标记生成器类可以使这种相当简单的事情:

#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;
    }
}
作者: Ferruccio 发布者: 2008 年 9 月 11 日

34

1717 作者的声誉

Boost具有强大的分割功能:boost :: algorithm :: split

示例程序:

#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"
""
作者: Raz 发布者: 2008 年 9 月 12 日

4

6312 作者的声誉

对于简单的东西,我只使用以下内容:

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

Cowardly免责声明:我编写实时数据处理软件,其中数据通过二进制文件,套接字或某些API调用(I / O卡,相机)进入。我从不将此函数用于比在启动时读取外部配置文件更复杂或时间更重要的事情。

作者: jilles de wit 发布者: 2008 年 9 月 15 日

46

7336 作者的声誉

没有进攻的乡亲,但对于这样一个简单的问题,你在做事情的方式太复杂了。使用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());
    }
}

例如(对于Doug的案例),

#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/

(我最初写的回答Doug的问题:基于分隔符的C ++字符串修改和提取(已关闭)。但是由于Martin York用指针关闭了这个问题......我只是概括了我的代码。)

作者: Mr.Ree 发布者: 2008 年 11 月 28 日

92

1629 作者的声誉

另一种快速方法是使用getline。就像是:

stringstream ss("bla bla");
string s;

while (getline(ss, s, ' ')) {
 cout << s << endl;
}

如果你愿意,你可以做一个简单的split()方法返回一个vector<string>,这是非常有用的。

作者: user35978 发布者: 2008 年 11 月 28 日

5

986 作者的声誉

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
作者: Jim In Texas 发布者: 2009 年 3 月 22 日

25

16246 作者的声誉

我知道您要求提供C ++解决方案,但您可能会认为这有用:

Qt的

#include <QString>

...

QString str = "The quick brown fox"; 
QStringList results = str.split(" "); 

在这个例子中,优于Boost的优势在于它是一个一对一的映射到你的帖子的代码。

Qt文档中查看更多内容

作者: sivabudh 发布者: 2010 年 8 月 4 日

8

81 作者的声誉

检查此示例。它可能会帮助你..

#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;
}
作者: sohesado 发布者: 2010 年 12 月 20 日

5

391 作者的声誉

您只需使用正则表达式库并使用正则表达式解决该问题。

使用表达式(\ w +)和\ 1中的变量(或$ 1,取决于正则表达式的库实现)。

作者: Fawix 发布者: 2011 年 4 月 22 日

0

412 作者的声誉

If the maximum length of the input string to be tokenized is known, one can exploit this and implement a very fast version. I am sketching the basic idea below, which was inspired by both strtok() and the "suffix array"-data structure described Jon Bentley's "Programming Perls" 2nd edition, chapter 15. The C++ class in this case only gives some organization and convenience of use. The implementation shown can be easily extended for removing leading and trailing whitespace characters in the tokens.

Basically one can replace the separator characters with string-terminating '\0'-characters and set pointers to the tokens withing the modified string. In the extreme case when the string consists only of separators, one gets string-length plus 1 resulting empty tokens. It is practical to duplicate the string to be modified.

头文件:

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
作者: Angel Sinigersky 发布者: 2011 年 5 月 15 日

1

34 作者的声誉

you can take advantage of boost::make_find_iterator. Something similar to this:

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;
}
作者: Arash 发布者: 2011 年 8 月 3 日

16

119622 作者的声誉

pystring是一个小型库,它实现了一堆Python的字符串函数,包括split方法:

#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, "-");
作者: dbr 发布者: 2011 年 12 月 29 日

0

53 作者的声誉

boost::tokenizer是您的朋友,但考虑通过使用wstring/ wchar_t而不是遗留string/ char类型参考国际化(i18n)问题来使您的代码可移植。

#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>

using namespace std;
using namespace boost;

typedef tokenizer<char_separator<wchar_t>,
                  wstring::const_iterator, wstring> Tok;

int main()
{
  wstring s;
  while (getline(wcin, s)) {
    char_separator<wchar_t> sep(L" "); // list of separator characters
    Tok tok(s, sep);
    for (Tok::iterator beg = tok.begin(); beg != tok.end(); ++beg) {
      wcout << *beg << L"\t"; // output (or store in vector)
    }
    wcout << L"\n";
  }
  return 0;
}
作者: jochenleidner 发布者: 2012 年 7 月 16 日

4

41 作者的声誉

这里有许多过于复杂的建议。试试这个简单的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;
}
作者: David919 发布者: 2012 年 8 月 1 日

2

449 作者的声誉

这是一种方法,允许您控制是否包含空标记(如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;
}
作者: Darren Smith 发布者: 2012 年 10 月 26 日

0

42915 作者的声誉

简单C ++代码(标准C ++ 98),接受多个分隔符(在std :: string中指定),仅使用向量,字符串和迭代器。

#include <iostream>
#include <vector>
#include <string>
#include <stdexcept> 

std::vector<std::string> 
split(const std::string& str, const std::string& delim){
    std::vector<std::string> result;
    if (str.empty())
        throw std::runtime_error("Can not tokenize an empty string!");
    std::string::const_iterator begin, str_it;
    begin = str_it = str.begin(); 
    do {
        while (delim.find(*str_it) == std::string::npos && str_it != str.end())
            str_it++; // find the position of the first delimiter in str
        std::string token = std::string(begin, str_it); // grab the token
        if (!token.empty()) // empty token only when str starts with a delimiter
            result.push_back(token); // push the token into a vector<string>
        while (delim.find(*str_it) != std::string::npos && str_it != str.end())
            str_it++; // ignore the additional consecutive delimiters
        begin = str_it; // process the remaining tokens
        } while (str_it != str.end());
    return result;
}

int main() {
    std::string test_string = ".this is.a.../.simple;;test;;;END";
    std::string delim = "; ./"; // string containing the delimiters
    std::vector<std::string> tokens = split(test_string, delim);           
    for (std::vector<std::string>::const_iterator it = tokens.begin(); 
        it != tokens.end(); it++)
            std::cout << *it << std::endl;
}
作者: vsoftco 发布者: 2013 年 12 月 15 日

9

1168 作者的声誉

我发布了类似问题的答案。
不要重新发明轮子。我使用了许多库,我遇到的最快和最灵活的是: C ++ String Toolkit Library

这是一个如何使用它的例子,我已经发布了stackoverflow上的其他地方。

#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 t("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 u("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;
}
作者: DannyK 发布者: 2014 年 1 月 7 日

2

3221 作者的声誉

Seems odd to me that with all us speed conscious nerds here on SO no one has presented a version that uses a compile time generated look up table for the delimiter (example implementation further down). Using a look up table and iterators should beat std::regex in efficiency, if you don't need to beat regex, just use it, its standard as of C++11 and super flexible.

Some have suggested regex already but for the noobs here is a packaged example that should do exactly what the OP expects:

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

If we need to be faster and accept the constraint that all chars must be 8 bits we can make a look up table at compile time using metaprogramming:

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

With that in place making a getNextToken function is easy:

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

Using it is also easy:

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

Here is a live example: http://ideone.com/GKtkLQ

作者: odinthenerd 发布者: 2014 年 7 月 26 日

29

8927 作者的声誉

使用regex_token_iterators 的解决方案:

#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;
    }
}
作者: w.b 发布者: 2014 年 12 月 14 日

18

1061 作者的声誉

这是一个简单的仅限STL的解决方案(约5行!)std::findstd::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);
    }
}

试试吧直播

作者: Parham 发布者: 2015 年 2 月 28 日

4

21875 作者的声誉

Adam Pierce的回答提供了一个手动旋转的标记器const char*。使用迭代器有点问题,因为增加一个string结束迭代器是未定义的。那说,鉴于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;
}

Live Example


如果您希望通过使用标准功能来抽象复杂性,那么On Freund建议这 strtok是一个简单的选择:

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无需为每个标记使用相同的分隔符。除了这个优点,还有几个缺点:

  1. strtok不能在多个可使用strings在同一时间:无论是nullptr必须传递给继续标记化的当前string或新的char*来标记必须被传递(也有其做然而支持这一点,如一些非标准的实现:strtok_s
  2. 出于同样的原因strtok,不能同时在多个线程上使用(但这可能是实现定义的,例如:Visual Studio的实现是线程安全的
  3. 调用strtok修改string它正在进行操作,因此不能在const strings,const char*s或文字字符串上使用它来标记任何这些,strtok或者对string需要保存的内容进行操作str,必须复制,然后复制可以操作

以前的方法都不能生成vector就地标记化,这意味着不将它们抽象为无法初始化的辅助函数const vector<string> tokens。可以使用a来利用该功能接受任何空白分隔符的能力istream_iterator。例如:const string str{ "The quick \tbrown \nfox" }我们可以这样做:

istringstream is{ str };
const vector<string> tokens{ istream_iterator<string>(is), istream_iterator<string>() };

Live Example

istringstream对于此选项所需的构造比前两个选项具有更高的成本,但是这个成本通常隐藏在string分配费用中。


如果上述选项都不足以满足您的标记化需求,那么最灵活的选项regex_token_iterator当然是使用这种灵活性需要更高的费用,但这又可能隐藏在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() };

Live Example

作者: Jonathan Mee 发布者: 2016 年 7 月 26 日

2

1018 作者的声誉

I know this question is already answered but I want to contribute. Maybe my solution is a bit simple but this is what I came up with:

vector<string> get_words(string const& text)
{
    vector<string> result;
    string tmp = text;

    size_t first_pos = 0;
    size_t second_pos = tmp.find(" ");;

    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 + 1);
        second_pos = tmp.find(" ");
    }

    result.push_back(tmp);

    return result;
}

Please comment if there is a better approach to something in my code or if something is wrong.

作者: NutCracker 发布者: 2018 年 5 月 9 日
32x32