मैं C++ में स्ट्रिंग को टोकन कैसे करूं?




string split (20)

आप इसे सीधे करने के लिए स्ट्रीम, इटरेटर्स और कॉपी एल्गोरिदम का उपयोग कर सकते हैं।

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

जावा में एक सुविधाजनक विभाजन विधि है:

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

क्या सी ++ में ऐसा करने का कोई आसान तरीका है?


आप बस नियमित अभिव्यक्ति लाइब्रेरी का उपयोग कर सकते हैं और नियमित अभिव्यक्तियों का उपयोग करके हल कर सकते हैं।

अभिव्यक्ति (\ w +) और वैरिएबल का उपयोग \ 1 (या $ 1 नियमित अभिव्यक्तियों के पुस्तकालय कार्यान्वयन के आधार पर) में करें।


इस उदाहरण की जांच करें। यह आपकी मदद कर सकता है ..

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

एमएफसी / एटीएल का एक बहुत अच्छा टोकननाइज़र है। एमएसडीएन से:

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

मुझे पता है कि आपने सी ++ समाधान के लिए कहा है, लेकिन आप इस सहायक पर विचार कर सकते हैं:

क्यूटी

#include <QString>

...

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

इस उदाहरण में बूस्ट पर लाभ यह है कि यह आपके पोस्ट के कोड में एक मैपिंग के लिए प्रत्यक्ष है।

क्यूटी दस्तावेज पर और देखें


मेरे लिए अजीब लगता है कि हम सभी के साथ यहां पर जागरूक नरक की गति है, इसलिए किसी ने भी एक संस्करण प्रस्तुत नहीं किया है जो एक संकलन समय का उपयोग करता है जो डिलीमीटर के लिए तालिका को देखता है (उदाहरण कार्यान्वयन आगे नीचे)। 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


मैंने सोचा था कि स्ट्रिंग स्ट्रीम पर >> ऑपरेटर क्या था:

string word; sin >> word;

यदि आप सी का उपयोग करने के इच्छुक हैं, तो आप 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;
}

यहां एक नमूना टोकननाइज़र क्लास है जो आप जो चाहते हैं वह कर सकती है

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

यहां बहुत अधिक जटिल सुझाव दिए गए हैं। इस सरल std :: स्ट्रिंग समाधान का प्रयास करें:

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

सरल सामान के लिए मैं बस निम्नलिखित का उपयोग करता हूं:

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

कायरली अस्वीकरण: मैं रीयल-टाइम डेटा प्रोसेसिंग सॉफ़्टवेयर लिखता हूं जहां डेटा बाइनरी फाइलों, सॉकेट, या कुछ एपीआई कॉल (आई / ओ कार्ड, कैमरा) के माध्यम से आता है। स्टार्टअप पर बाहरी कॉन्फ़िगरेशन फ़ाइलों को पढ़ने से मैं कभी भी अधिक जटिल या समय-महत्वपूर्ण के लिए इस फ़ंक्शन का उपयोग नहीं करता हूं।


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

Boost.Tokenizer क्लास इस तरह की चीज को काफी सरल बना सकता है:

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

सी ++ 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;
    }
}

pystring एक छोटी लाइब्रेरी है जो स्प्लिट विधि सहित पाइथन के स्ट्रिंग फ़ंक्शंस का एक गुच्छा लागू करती है:

#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, "-");

getline का उपयोग करने का एक और त्वरित तरीका है। कुछ इस तरह:

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

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

यदि आप चाहते हैं, तो आप एक vector<string> लौटने वाली एक सरल split() विधि बना सकते हैं, जो वास्तव में उपयोगी है।


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.

Header file:

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

Implementattion file:

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

A scenario of usage would be:

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

Simple C++ code (standard C++98), accepts multiple delimiters (specified in a std::string), uses only vectors, strings and iterators.

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

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

एडम पिएर्स का जवाब एक 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;
}

लाइव उदाहरण

यदि आप मानक कार्यक्षमता का उपयोग करके अमूर्त जटिलता की तलाश में हैं, क्योंकि ऑन strtok बताता है कि 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 पर उपयोग नहीं किया जा सकता है: या तो वर्तमान string को nullptr करने के लिए एक nullptr पारित किया जाना चाहिए या nullptr लिए एक नया char* पारित किया जाना चाहिए (कुछ गैर-मानक कार्यान्वयन हैं जो इस पर समर्थन करते हैं, जैसे कि : strtok_s )
  2. इसी कारण से strtok को कई धागे पर एक साथ उपयोग नहीं किया जा सकता है (हालांकि यह कार्यान्वयन परिभाषित किया जा सकता है, उदाहरण के लिए: विजुअल स्टूडियो का कार्यान्वयन थ्रेड सुरक्षित है )
  3. कॉलिंग strtok उस string को संशोधित करता है जिस पर यह चल रहा है, इसलिए इसे strtok साथ इनमें से किसी को strtok करने के लिए या string पर काम करने के लिए const string एस, const char* s, या शाब्दिक तारों पर उपयोग नहीं किया जा सकता है, जिनके सामग्रियों को संरक्षित करने की आवश्यकता है, str प्रतिलिपि बनाना होगा, फिर प्रतिलिपि संचालित की जा सकती है

पिछले दोनों विधियों में एक टोकनयुक्त vector उत्पन्न नहीं हो सकता है, जिसका मतलब है कि उन्हें एक सहायक कार्य में सारणित किए बिना वे const vector<string> tokens प्रारंभ नहीं कर सकते हैं। उस कार्यक्षमता और किसी भी व्हाइट-स्पेस डिलीमीटर को स्वीकार करने की क्षमता को istream_iterator का उपयोग करके उपयोग किया जा सकता है। उदाहरण के लिए दिया गया: const string str{ "The quick \tbrown \nfox" } हम यह कर सकते हैं:

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

लाइव उदाहरण

इस विकल्प के लिए एक istringstream आवश्यक निर्माण पिछले 2 विकल्पों की तुलना में कहीं अधिक लागत है, हालांकि यह लागत आम तौर पर 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() };

लाइव उदाहरण





tokenize