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


414

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

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

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


172
मैं विश्वास नहीं कर सकता कि यह नियमित कार्य सी ++ में ऐसा सिरदर्द है
wfbarksdale 5

6
सी ++ में इसका सिरदर्द नहीं है - इसे प्राप्त करने के विभिन्न तरीके हैं। प्रोग्रामर को c ++ की तुलना में c # के बारे में कम जानकारी है - इसकी मार्केटिंग और निवेश के बारे में ... इसे प्राप्त करने के लिए विभिन्न c ++ विकल्पों के लिए इसे देखें: cplusplus.com/faq/fterences/strings/split
hB0

9
@ hB0 बहुत से सवालों के जवाब से गुजर रहा है और अभी भी इसका मतलब नहीं तय करना सिरदर्द है। एक को उस पुस्तकालय की जरूरत है, दूसरा सिर्फ रिक्त स्थान के लिए है, दूसरा रिक्त स्थान को नहीं संभालता है ..
पासचलिस


2
C ++ में सब कुछ संघर्ष क्यों करना पड़ता है?
वेल असफ

जवाबों:


145

C ++ मानक पुस्तकालय एल्गोरिदम कंक्रीट कंटेनर के बजाय पुनरावृत्तियों के आसपास बहुत सार्वभौमिक रूप से आधारित हैं। दुर्भाग्य से यह splitC ++ मानक पुस्तकालय में जावा जैसा फ़ंक्शन प्रदान करना कठिन बनाता है , हालांकि कोई भी तर्क नहीं देता है कि यह सुविधाजनक होगा। लेकिन इसका रिटर्न टाइप क्या होगा? std::vector<std::basic_string<…>>? हो सकता है, लेकिन तब हम प्रदर्शन करने के लिए मजबूर होते हैं (संभवतः अनावश्यक और महंगा) आवंटन।

इसके बजाय, C ++ मनमाने ढंग से जटिल सीमांकक के आधार पर तारों को विभाजित करने के तरीकों की एक बहुतायत प्रदान करता है, लेकिन उनमें से कोई भी अन्य भाषाओं की तरह अच्छी तरह से समझाया नहीं गया है। कई तरीके पूरे ब्लॉग पोस्ट को भरते हैं

इसके सरलतम पर, आप std::string::findहिट होने तक इसका उपयोग कर सकते हैं std::string::npos, और उपयोग की गई सामग्री को निकाल सकते हैं std::string::substr

व्हॉट्सएप पर बंटवारे के लिए एक अधिक द्रव (और मुहावरेदार, लेकिन मूल) संस्करण का उपयोग करेगा std::istringstream:

auto iss = std::istringstream{"The quick brown fox"};
auto str = std::string{};

while (iss >> str) {
    process(str);
}

std::istream_iteratorएस का उपयोग करते हुए , स्ट्रिंग स्ट्रीम की सामग्री को इसकी पुनरावृत्ति श्रेणी के निर्माणकर्ता का उपयोग करके वेक्टर में भी कॉपी किया जा सकता है।

कई पुस्तकालय (जैसे Boost.Tokenizer ) विशिष्ट टोकन प्रदान करते हैं।

अधिक उन्नत विभाजन के लिए नियमित अभिव्यक्ति की आवश्यकता होती है। C ++ std::regex_token_iteratorइस उद्देश्य के लिए विशेष रूप से प्रदान करता है :

auto const str = "The quick brown fox"s;
auto const re = std::regex{R"(\s+)"};
auto const vec = std::vector<std::string>(
    std::sregex_token_iterator{begin(str), end(str), re, -1},
    std::sregex_token_iterator{}
);

53
अफसोस की बात है, सभी परियोजनाओं के लिए बढ़ावा हमेशा उपलब्ध नहीं होता है। मुझे गैर-बूस्ट उत्तर की तलाश करनी होगी।
फजी बन्नीस्लीप

36
प्रत्येक प्रोजेक्ट "ओपन सोर्स" के लिए खुला नहीं है। मैं भारी विनियमित उद्योगों में काम करता हूं। यह वास्तव में एक समस्या नहीं है। यह जीवन का एक तथ्य मात्र है। हर जगह बूस्ट उपलब्ध नहीं है।
FuzzyBunnySlippers

5
@NonlinearIdeas अन्य प्रश्न / उत्तर ओपन सोर्स परियोजनाओं के बारे में बिल्कुल नहीं थे। किसी भी परियोजना के लिए भी यही सच है । उस ने कहा, मैं निश्चित रूप से प्रतिबंधित मानकों जैसे कि MISRA C के बारे में समझता हूं, लेकिन फिर यह समझा जाता है कि आप सब कुछ वैसे भी खरोंच से बनाते हैं (जब तक कि आप एक अनुरूप पुस्तकालय खोजने के लिए नहीं होते हैं - एक दुर्लभता)। वैसे भी, यह बात शायद ही है कि "बूस्ट उपलब्ध नहीं है" - यह है कि आपके पास विशेष आवश्यकताएं हैं जिनके लिए लगभग किसी भी सामान्य प्रयोजन के उत्तर अनुपयुक्त होंगे।
कोनराड रुडोल्फ

1
@NonlinearIdeas के मामले में, अन्य, गैर-बूस्ट उत्तर भी MISRA के अनुरूप नहीं हैं।
कोनराड रुडोल्फ

3
@ डमित्री "एसटीएल बारफ" क्या है ?! और पूरा समुदाय सी प्रीप्रोसेसर की जगह लेने के पक्ष में है - वास्तव में, ऐसा करने के प्रस्ताव हैं। लेकिन इसके बजाय PHP या किसी अन्य भाषा का उपयोग करने का आपका सुझाव एक बड़ा कदम होगा।
कोनराड रुडोल्फ

188

बूस्ट 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;
    }
}

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

1
अच्छा सामान, मैंने हाल ही में इसका उपयोग किया है। मेरे विजुअल स्टूडियो कंपाइलर में अजीब व्हिंज है, जब तक मैं टोकन (टेक्स्ट, सेप) बिट से पहले दो ">" वर्णों को अलग करने के लिए एक व्हाट्सएप का उपयोग करता हूं: (त्रुटि C2947: अपेक्षा '>' खाका-तर्क-सूची समाप्त करने के लिए, पाया गया>> > ')
एंडीयूके

@AndyUK हाँ, अंतरिक्ष के बिना संकलक इसे दो समापन टेम्पलेट्स के बजाय निष्कर्षण ऑपरेटर के रूप में पार्स करता है।
एनब्रेनटेन

सैद्धांतिक रूप से यह C ++ 0x में तय किया गया है
डेविड सूटर

3
char_separatorकंस्ट्रक्टर के तीसरे मापदंडों से सावधान रहें ( drop_empty_tokensडिफ़ॉल्ट, वैकल्पिक है keep_empty_tokens)।
बेनोइट

5
@ पुक - यह C ++ हेडर फ़ाइलों के लिए आमतौर पर इस्तेमाल किया जाने वाला प्रत्यय है। ( .hसी हेडर के लिए)
फेर्रुकियो

167

यहाँ एक वास्तविक सरल है:

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

क्या मुझे इस विधि के लिए .h फ़ाइल में एक प्रोटोटाइप जोड़ने की आवश्यकता है?
सुह्रोब समैव

5
यह बिल्कुल "सबसे अच्छा" उत्तर नहीं है क्योंकि यह अभी भी एक स्ट्रिंग शाब्दिक का उपयोग करता है जो सादे सी निरंतर चरित्र सरणी है। मेरा मानना ​​है कि प्रश्नकर्ता पूछ रहा था कि क्या वह सी ++ स्ट्रिंग को टोकन दे सकता है जो कि उत्तरार्द्ध द्वारा पेश किए गए "स्ट्रिंग" प्रकार का है।
विजय कुमार कांता

इसे एक नए उत्तर की आवश्यकता है क्योंकि मुझे C ++ 11 में नियमित अभिव्यक्तियों को शामिल करने पर दृढ़ता से संदेह है कि सबसे अच्छा उत्तर क्या होगा।
सर्वव्यापी

113

स्ट्रटोक का उपयोग करें। मेरी राय में, टोकन के आसपास एक वर्ग बनाने की आवश्यकता नहीं है जब तक कि स्ट्रटोक आपको वह प्रदान नहीं करता है जिसकी आपको आवश्यकता है। यह नहीं हो सकता है, लेकिन सी और सी ++ में विभिन्न पार्सिंग कोड लिखने के 15+ वर्षों में, मैंने हमेशा स्ट्रैटोक का उपयोग किया है। यहाँ एक उदाहरण है

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

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

मेरी अपनी राय में, उपरोक्त कोड इसके लिए एक अलग वर्ग लिखने की तुलना में कहीं अधिक सरल और आसान है। मेरे लिए, यह उन कार्यों में से एक है जो भाषा प्रदान करती है और इसे अच्छी तरह से और सफाई से करती है। यह बस एक "सी आधारित" समाधान है। यह उपयुक्त है, यह आसान है, और आपको बहुत सारे अतिरिक्त कोड लिखने की आवश्यकता नहीं है :-)


42
ऐसा नहीं है कि मैं सी को नापसंद करता हूं, हालांकि स्ट्रेटोक थ्रेड-सुरक्षित नहीं है, और आपको यह सुनिश्चित करने की आवश्यकता है कि आपके द्वारा भेजे जाने वाले स्ट्रिंग में एक संभावित बफर अतिप्रवाह से बचने के लिए एक अशक्त चरित्र है।
tloach

11
इसमें strtok_r है, लेकिन यह C ++ प्रश्न था।
प्रो। फॉकन कॉन्ट्रैक्ट ने

3
@tloach: MS C ++ कंपाइलर स्ट्रटोक में धागा सुरक्षित होता है क्योंकि TLS (थ्रेड लोकल स्टोरेज) पर आंतरिक स्टैटिक वैरिएबल बनाया जाता है (वास्तव में यह कंपाइलर डिपेंड करता है)
अहमद ने

3
@ahmed: थ्रेड सेफ का मतलब है कि फंक्शन को दो बार अलग-अलग थ्रेड्स में चलाना। इस स्थिति में यदि थ्रेड को संशोधित करते समय थ्रेड को संशोधित किया जाता है, तो यह संभव है कि स्ट्रिंग को स्ट्रेटोक के पूरे रन के दौरान मान्य किया जाए, लेकिन स्ट्रेटोक अभी भी गड़बड़ करेगा क्योंकि स्ट्रिंग बदल गया है, यह अब पहले से ही अशक्त वर्ण से अतीत है, और यह जा रहा है स्मृति को तब तक पढ़ते रहें जब तक कि उसे या तो सुरक्षा उल्लंघन न हो या वह अशक्त चरित्र न पा ले। यह मूल C स्ट्रिंग फ़ंक्शंस के साथ एक समस्या है, यदि आप कहीं एक लंबाई निर्दिष्ट नहीं करते हैं जो आप समस्याओं में चलाते हैं।
tloach

4
strtok को एक नॉन-कॉन्स्टेबल नल-टर्म चार्ड सरणी के लिए एक पॉइंटर की आवश्यकता होती है, जो कि c ++ कोड में खोजने के लिए एक सामान्य प्राणी नहीं है ... std :: string से इसे कन्वर्ट करने का आपका पसंदीदा तरीका क्या है?
फजाइटीव्यू

105

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

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

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

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


2
मुझे स्ट्रिंग में 0x0A वर्णों के साथ इस तकनीक का उपयोग करने में समस्या थी, जो समय से पहले लूप से बाहर निकल गई। अन्यथा, यह एक अच्छा सरल और त्वरित समाधान है।
रयान एच।

4
यह अच्छा है लेकिन बस ध्यान रखना है कि ऐसा करने से डिफ़ॉल्ट सीमांकक '\ n' नहीं माना जाता है। यह उदाहरण काम करेगा, लेकिन यदि आप कुछ ऐसा उपयोग कर रहे हैं: जबकि (getline (inFile, word, '')) जहां inFile ifstream ऑब्जेक्ट है जिसमें कई लाइनें हैं, तो आपको फ़नी परिणाम मिलेंगे ..
Hackrock

यह बहुत खराब गेटलाइन स्ट्रिंग के बजाय स्ट्रीम को लौटाता है, जिससे यह अस्थायी भंडारण के बिना आरंभीकरण सूचियों में अनुपयोगी हो जाता है
fuzzyTew

1
ठंडा! कोई बढ़ावा नहीं और C ++ 11, उन विरासत परियोजनाओं का अच्छा समाधान है!
डेविक

1
यह जवाब है, फ़ंक्शन का नाम थोड़ा अजीब है।
नेल्स

82

आप इसे सीधे रूप से करने के लिए धाराओं, पुनरावृत्तियों और प्रतिलिपि एल्गोरिथ्म का उपयोग कर सकते हैं।

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

17
मैं उन एसटीडी को खोजता हूं :: पढ़ने के लिए परेशान हो रहा हूं .. "उपयोग" क्यों नहीं कर रहा हूं?
user35978

80
@Vadi: क्योंकि किसी और के पोस्ट को संपादित करना काफी घुसपैठ है। @ सर्प: मैं stdइस तरह से यह बताना चाहता हूं कि मुझे पता है कि मेरी वस्तु कहां से आई है, यह केवल शैली की बात है।
Matthieu M.

7
मैं आपके कारण को समझता हूं और मुझे लगता है कि यह वास्तव में एक अच्छा विकल्प है यदि यह आपके लिए काम करता है, लेकिन एक शैक्षणिक दृष्टिकोण से मैं वास्तव में फेज से सहमत हूं। शीर्ष पर एक "नेमस्पेस std का उपयोग" के साथ इस तरह के एक पूरी तरह से विदेशी उदाहरण को पढ़ना और समझना आसान है, क्योंकि इसके लिए निम्न पंक्तियों की व्याख्या करने के लिए कम प्रयास की आवश्यकता होती है ... विशेष रूप से इस मामले में क्योंकि सब कुछ मानक पुस्तकालय से है। आप इसे पढ़ना और स्पष्ट करना आसान बना सकते हैं जहाँ ऑब्जेक्ट "std :: string" का उपयोग करके श्रृंखला से आते हैं; आदि खासकर जब से फ़ंक्शन इतना छोटा है।
चेशायरकोव

61
"Std ::" उपसर्गों के बावजूद चिड़चिड़ाहट या बदसूरत होने के बावजूद, उन्हें उदाहरण कोड में शामिल करना सबसे अच्छा है ताकि यह पूरी तरह से स्पष्ट हो जाए कि ये फ़ंक्शन कहाँ से आ रहे हैं। यदि वे आपको परेशान करते हैं, तो उदाहरण चोरी करने के बाद उन्हें "उपयोग करना" के साथ प्रतिस्थापित करना तुच्छ है और इसे अपना दावा करें।
dlchambers

20
हां! उसने क्या क़हा! सबसे अच्छा अभ्यास एसटीडी उपसर्ग का उपयोग करना है। किसी भी बड़े कोड के आधार पर कोई संदेह नहीं है कि यह अपने स्वयं के पुस्तकालयों और नामस्थानों का उपयोग कर रहा है और "नामस्थान एसटीडी का उपयोग करके" आपको सिरदर्द देगा जब आप नेमस्पेस संघर्ष शुरू कर देंगे।
मीक

48

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

उदाहरण के लिए (डौग के मामले के लिए),

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

और हाँ, हम विभाजित हो सकते हैं () एक में गुजरने के बजाय एक नया वेक्टर लौटाते हैं। यह लपेटने और अधिभार के लिए तुच्छ है। लेकिन जो मैं कर रहा हूं, उसके आधार पर, मैं अक्सर पहले से मौजूद वस्तुओं को फिर से उपयोग करने के बजाय बेहतर बनाता हूं कि वे हमेशा नए बनाएं। (जब तक मैं बीच में वेक्टर खाली करने के लिए मत भूलना!)

संदर्भ: http://www.cplusplus.com/reference/string/string/

(मैं मूल रूप से डग के सवाल का जवाब लिख रहा था: सी ++ स्ट्रिंग्स मॉडिफ़ाइंग एंड एक्सट्रैक्टिंग पर आधारित सेपरेटर (बंद) । लेकिन चूंकि मार्टिन ने उस प्रश्न को यहां एक पॉइंटर के साथ बंद कर दिया ... मैं सिर्फ अपना कोड सामान्य करूंगा।)


12
एक मैक्रो को क्यों परिभाषित करें जो आप केवल एक ही स्थान पर उपयोग करते हैं। और कैसे आपके UASSERT मानक मुखर से बेहतर है। तुलना को 3 टोकन में विभाजित करना जैसे कि आपको अन्यथा आवश्यकता से अधिक अल्पविराम की आवश्यकता के अलावा और कुछ भी नहीं है।
क्रैबर'

1
हो सकता है कि यूएएसईआरटीईआर मैक्रो (त्रुटि संदेश में) दो तुलना मूल्यों के बीच (और मूल्यों के बीच) वास्तविक संबंध दिखाता है? यह वास्तव में एक बहुत अच्छा विचार है, IMHO।
घाससैनपाल

10
उह, std::stringक्लास में स्प्लिट () फ़ंक्शन क्यों शामिल नहीं है ?
श्री शिखाडांस

मुझे लगता है कि लूप में अंतिम पंक्ति होनी चाहिए start = ((end > (theString.size() - theDelimiter.size())) ? string::npos : end + theDelimiter.size());और जबकि लूप होनी चाहिए while (start != string::npos)। इसके अलावा, मैं यह सुनिश्चित करने के लिए कि यह सदिश में डालने से पहले खाली नहीं हूं, सबस्ट्रिंग की जांच करता हूं।
जॉन के।

@ जॉन्क यदि इनपुट में दो लगातार सीमांकक हैं, तो स्पष्ट रूप से उनके बीच का तार खाली है, और इसे वेक्टर में डाला जाना चाहिए। यदि खाली मान किसी विशेष उद्देश्य के लिए स्वीकार्य नहीं हैं, तो यह दूसरी बात है, लेकिन IMHO को इस तरह की बाधाओं को इस तरह के बहुत ही सामान्य उद्देश्य कार्यों के बाहर लागू किया जाना चाहिए।
लॉरी नूरमी

46

regex_token_iteratorएस का उपयोग कर एक समाधान :

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

5
यह शीर्ष क्रम का उत्तर होना चाहिए। C ++> = 11. में ऐसा करने का यह सही तरीका है
Omnifarious

1
मुझे खुशी है कि मैंने इस उत्तर के लिए सभी तरह से नीचे स्क्रॉल किया है (वर्तमान में केवल 9 upvotes थे)। यह वही है जो इस कार्य के लिए C ++ 11 कोड जैसा दिखना चाहिए!
येफासीके

उत्कृष्ट उत्तर जो बाहरी पुस्तकालयों पर निर्भर नहीं करता है और पहले से ही उपलब्ध पुस्तकालयों का उपयोग करता है
एंड्रयू

1
महान जवाब, सीमांकक में सबसे अधिक लचीलापन दे। कुछ चेतावनी: पाठ के बीच में खाली टोकन का उपयोग करने से बचना चाहिए, लेकिन यदि पाठ व्हाट्सएप से शुरू होता है तो खाली टोकन देता है। इसके अलावा, रेगेक्स धीमा लगता है: मेरे लैपटॉप पर, 20 एमबी के रैंडम टेक्स्ट के लिए, स्ट्रैटोक, स्ट्रैसेप के लिए 0.014 सेकंड की तुलना में, 0.6 सेकंड लगते हैं, या परम के जवाब में str.find_first_of, या पर्ल के लिए 0.027 सेकंड, या पायथन के लिए 0.021 सेकंड का उपयोग होता है। । छोटे पाठ के लिए, गति चिंता का विषय नहीं हो सकती है।
मार्क गेट्स

2
ठीक है, यह अच्छा लग रहा है, लेकिन यह स्पष्ट रूप से नियमित अभिव्यक्तियों का अति प्रयोग है। उचित तभी यदि आप प्रदर्शन की परवाह नहीं करते हैं।
मारेक आर

35

बूस्ट का एक मजबूत विभाजन कार्य है: बढ़ावा :: एल्गोरिथ्म :: विभाजन

नमूना कार्यक्रम:

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

26

मुझे पता है कि आपने C ++ समाधान के लिए कहा था, लेकिन आप इसे उपयोगी मान सकते हैं:

क्यूटी

#include <QString>

...

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

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

Qt प्रलेखन में अधिक देखें


22

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

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

19

यह एक सरल एसटीएल-ओनली सॉल्यूशन (~ 5 लाइन्स!) का उपयोग करना है std::findऔर std::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);
    }
}

इसे लाइव करके देखें !


3
यह एक अच्छा है, लेकिन मुझे लगता है कि आपको इसके लिए कई सीमांकक के साथ ठीक से काम करने के लिए find_first_of () के बजाय find () का उपयोग करने की आवश्यकता है।

2
@ user755921 कई सीमांकक को छोड़ दिया जाता है, जब start_first_not_of के साथ आरंभिक स्थिति का पता लगाते हैं।
शुरुआती

16

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

3
वाह, आपने मेरे तात्कालिक प्रश्न और भविष्य के कई सवालों के जवाब दिए हैं। मुझे लगता है कि c ++ शक्तिशाली है। लेकिन जब उपरोक्त उत्तरों की तरह स्रोत कोड में एक स्ट्रिंग को विभाजित करते हैं, तो यह स्पष्ट रूप से निराशाजनक है। मैं इस तरह के अन्य पुस्तकालयों के बारे में जानना पसंद करूंगा, जो उच्च स्तर की लंबाग्रेगता को नीचे खींचते हैं।
रॉस

वाह, आपने गंभीरता से सिर्फ मेरा दिन बनाया है !! पता नहीं था के बारे में pystring। यह मुझे बहुत समय बचाने जा रहा है!
accraze

11

मैंने इसी प्रश्न के लिए यह उत्तर पोस्ट किया है।
पहिया को सुदृढ़ मत करो। मैंने कई लाइब्रेरियों का उपयोग किया है और सबसे तेज और सबसे लचीला मैं आया है: C ++ स्ट्रिंग टूलकिट लाइब्रेरी

यहां इसका उपयोग करने का एक उदाहरण दिया गया है कि मैंने स्टैकओवरफ्लो पर और कहां पोस्ट किया है।

#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 s("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 s("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;
}

8

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

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

1
मुझे क्या करना होगाwhile ( is >> tmps ) { std::cout << tmps << "\n"; }
jordix

6

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

1
यह टोकनाइज़ () फ़ंक्शन खाली टोकन को छोड़ देगा, उदाहरण के लिए, यदि मुख्य स्ट्रिंग में "%%" को प्रतिस्थापित किया जाता है, तो कोई खाली टोकन वापस नहीं आता है। इसे छोड़ दिया जाता है।
शीन

4

यदि आप C का उपयोग करने के इच्छुक हैं, तो आप strtok फ़ंक्शन का उपयोग कर सकते हैं । इसका उपयोग करते समय आपको बहु-थ्रेडिंग मुद्दों पर ध्यान देना चाहिए।


3
ध्यान दें कि strtok आपके द्वारा जाँच की जा रही स्ट्रिंग को संशोधित करता है, इसलिए आप इसे कॉपी बनाने के बिना const char * स्ट्रिंग्स पर उपयोग नहीं कर सकते।
ग्रीम पेरो सेप

9
मल्टीथ्रेडिंग मुद्दा यह है कि स्ट्रटोक एक वैश्विक वैरिएबल का उपयोग करता है कि वह कहाँ है, इसका ट्रैक रखने के लिए, इसलिए यदि आपके पास दो धागे हैं जो प्रत्येक स्ट्रेटोक का उपयोग करते हैं, तो आपको अपरिभाषित व्यवहार मिलेगा।
जॉनएमसीजीजी

@ जॉनमोक्ग या केवल वह उपयोग करें strtok_sजो मूल रूप strtokसे स्पष्ट स्थिति से गुजर रहा है।
मथायस

4

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

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

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


4

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

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


Regex के सुझाव के लिए +1, अगर आपको ताना गति की आवश्यकता नहीं है, तो यह सबसे लचीला समाधान है, जो अभी तक हर जगह समर्थित नहीं है, लेकिन जैसे-जैसे समय कम होगा, यह महत्वपूर्ण हो जाएगा।
1946 को ओडिन्थेनरड

मेरे से +1, बस c ++ 11 में <regex> की कोशिश की। इतना सरल और सुरुचिपूर्ण
StahlRat

4

बहुत से जटिल सुझाव यहां दिए गए हैं। इस सरल 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;
}

4

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

string word; sin >> word;

1
एक खराब (बहुत सरल) उदाहरण देने के लिए मेरी गलती है। जहाँ तक मुझे पता है, यह तभी काम करता है जब आपका सीमांकक व्हॉट्सएप हो।
छिपकली

4

एडम पियर्स का जवाब एक हाथ से घूमने वाला टोकन प्रदान करता है const char*। यह iterators के साथ करने के लिए थोड़ा और अधिक समस्याग्रस्त है क्योंकि एक string's अंत ittering वृद्धि अपरिभाषित है । यह कहा, 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


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

हमें split_viewएक गैर-विनाशकारी तरीके से टोकन स्ट्रिंग्स के साथ प्रदान करता है : https://topanswers.xyz/cplusplus?q=749#a874


पिछले तरीकों से एक टोकन 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>() };

Live Example

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

Live Example


strtok_sC11 मानक है, वैसे। strtok_rPOSIX2001 मानक है। उन दोनों के बीच, strtokअधिकांश प्लेटफार्मों के लिए एक मानक फिर से प्रवेश संस्करण है ।
एंडन एम। कोलमैन

@ AndonM.Coleman लेकिन यह एक c ++ प्रश्न है, और C ++ में #include <cstring>केवल c99 संस्करण शामिल है strtok। इसलिए मेरी धारणा यह है कि आप इस टिप्पणी को सहायक सामग्री के रूप में प्रदान कर रहे हैं, strtokएक्सटेंशन की कार्यान्वयन की विशिष्ट उपलब्धता का प्रदर्शन कर रहे हैं ?
जोनाथन मी

1
केवल इतना है कि यह उतना गैर-मानक नहीं है जितना लोग अन्यथा विश्वास कर सकते हैं। strtok_sC11 और Microsoft के C रनटाइम में स्टैंडअलोन एक्सटेंशन के रूप में प्रदान किया जाता है। यहाँ एक जिज्ञासु सा इतिहास है जहाँ Microsoft के _sकार्य C मानक बन गए हैं।
एंडन एम। कोलमैन

@ AndonM.Coleman सही है, मैं आपके साथ हूं। जाहिर है कि अगर यह C11 मानक में है तो इंटरफ़ेस और कार्यान्वयन में उन बाधाओं को रखा गया है जिनके लिए प्लेटफॉर्म से स्वतंत्र समान व्यवहार की आवश्यकता होती है। अब एकमात्र समस्या यह सुनिश्चित कर रही है कि C11 फ़ंक्शन हमें प्लेटफार्मों भर में उपलब्ध है। उम्मीद है कि C11 मानक कुछ ऐसा होगा जो C ++ 17 या C ++ 20 पिकअप को चुनता है।
जोनाथन मी

3

मुझे पता है कि यह सवाल पहले से ही उत्तर दिया गया है लेकिन मैं योगदान देना चाहता हूं। हो सकता है कि मेरा समाधान थोड़ा सरल हो, लेकिन यह वही है जो मैं लेकर आया हूं:

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

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

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

    result.push_back(tmp);

    return result;
}

कृपया टिप्पणी करें कि क्या मेरे कोड में किसी चीज़ के लिए बेहतर दृष्टिकोण है या यदि कुछ गलत है।

अद्यतन: जोड़ा सामान्य विभाजक


भीड़ से अपने समाधान का इस्तेमाल किया :) क्या मैं किसी भी विभाजक को जोड़ने के लिए आपके कोड को संशोधित कर सकता हूं?
Zac

1
@Zac आपको खुशी है कि आपको यह पसंद आया और toc आप इसे संशोधित कर सकते हैं ... बस मेरे उत्तर में एक बोल्ड अद्यतन अनुभाग जोड़ें ...
NutCracker

2

यहां एक दृष्टिकोण है जो आपको इस बात पर नियंत्रण करने की अनुमति देता है कि क्या खाली टोकन शामिल हैं (जैसे 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;
}

2

मेरे लिए यह अजीब लगता है कि हम सभी के साथ एसओ पर सचेत गति से गति करता है, किसी ने भी ऐसा संस्करण प्रस्तुत नहीं किया है जो परिसीमन के लिए संकलित समयबद्ध तालिका का उपयोग करता है (उदाहरण कार्यान्वयन आगे और नीचे)। एक लुक अप टेबल का उपयोग करते हुए और पुनरावृत्तियों को std :: regex को दक्षता में हरा देना चाहिए, यदि आपको regex को पीटने की आवश्यकता नहीं है, तो बस इसका उपयोग करें, इसका मानक C ++ 11 और सुपर लचीला है।

कुछ ने पहले से ही रेगेक्स का सुझाव दिया है, लेकिन यहां के नॉब्स के लिए एक पैकेज्ड उदाहरण है जो ओपी को उम्मीद है कि वास्तव में ऐसा करना चाहिए:

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

अगर हमें और तेज़ होने की ज़रूरत है और बाधा को स्वीकार करना है कि सभी चार्ट 8 बिट्स होने चाहिए, तो हम मेटाप्रोग्रामिंग का उपयोग करके संकलन समय पर एक नज़र रख सकते हैं:

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

इसके साथ एक getNextTokenसमारोह बनाने में आसान है:

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

इसका उपयोग करना भी आसान है:

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

यहाँ एक जीवंत उदाहरण है: http://ideone.com/GKtkLQ


1
क्या एक स्ट्रिंग सीमांकक के साथ टोकन लेना संभव है?
गैलीगेटर

यह संस्करण केवल एकल वर्ण परिसीमन के लिए अनुकूलित है, लुक अप टेबल का उपयोग करते हुए बहु चरित्र (स्ट्रिंग) सीमांकक के लिए अनुकूल नहीं है ताकि दक्षता में रेगेक्स को हरा सके।
ओडिन्थनेरड

1

आप बढ़ावा देने का लाभ ले सकते हैं :: make_find_iterator। इसके समान कुछ:

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

1

व्हॉट्सएप द्वारा स्ट्रिंग्स को विभाजित करने के लिए मेरे स्विस® आर्मी नाइफ के स्ट्रिंग-टोकनर्स, एकल और डबल-कोट लिपटे स्ट्रिंग्स के साथ-साथ उन पात्रों को परिणामों से अलग करना। मैंने अधिकांश कोड-स्निपेट जेनरेट करने के लिए RexxBuddy 4.x का उपयोग किया , लेकिन मैंने बोली और कुछ अन्य चीजों को छोड़ने के लिए कस्टम हैंडलिंग को जोड़ा।

#include <string>
#include <locale>
#include <regex>

std::vector<std::wstring> tokenize_string(std::wstring string_to_tokenize) {
    std::vector<std::wstring> tokens;

    std::wregex re(LR"(("[^"]*"|'[^']*'|[^"' ]+))", std::regex_constants::collate);

    std::wsregex_iterator next( string_to_tokenize.begin(),
                                string_to_tokenize.end(),
                                re,
                                std::regex_constants::match_not_null );

    std::wsregex_iterator end;
    const wchar_t single_quote = L'\'';
    const wchar_t double_quote = L'\"';
    while ( next != end ) {
        std::wsmatch match = *next;
        const std::wstring token = match.str( 0 );
        next++;

        if (token.length() > 2 && (token.front() == double_quote || token.front() == single_quote))
            tokens.emplace_back( std::wstring(token.begin()+1, token.begin()+token.length()-1) );
        else
            tokens.emplace_back(token);
    }
    return tokens;
}

1
(नीचे) वोट उतने ही रचनात्मक हो सकते हैं, लेकिन तब नहीं जब आप टिप्पणियों को क्यों नहीं छोड़ते ...
kayleeFrye_onDeck

1
मैंने आपको पता लगाया, लेकिन यह हो सकता है क्योंकि कोड प्रोग्रामर के लिए बहुत चुनौतीपूर्ण लग रहा है 'कैसे एक स्ट्रिंग को विभाजित करने के लिए' विशेष रूप से प्रलेखन के बिना '
Mattshu

धन्यवाद @ ममत्सु! क्या यह रेगेक्स सेगमेंट है जो इसे चुनौतीपूर्ण बनाता है या कुछ और?
kayleeFrye_onDeck

0

यदि टोके जाने वाले इनपुट स्ट्रिंग की अधिकतम लंबाई ज्ञात है, तो कोई भी इसका फायदा उठा सकता है और बहुत तेज संस्करण को लागू कर सकता है। मैं नीचे दिए गए मूल विचार को स्केच कर रहा हूं, जो कि स्ट्रेटोक () और "प्रत्यय सरणी" से प्रेरित था -डेटा संरचना ने जॉन बेंटले के "प्रोग्रामिंग पर्ल्स" 2 संस्करण, अध्याय 15. का वर्णन किया है। इस मामले में C ++ वर्ग केवल इस संगठन और सुविधा देता है काम का। दिखाए गए कार्यान्वयन को टोकन में अग्रणी और अनुगामी व्हाट्सएप पात्रों को हटाने के लिए आसानी से बढ़ाया जा सकता है।

मूल रूप से एक विभाजक पात्रों को स्ट्रिंग-टर्मिनेटिंग '\ 0'-वर्णों के साथ बदल सकता है और संशोधित स्ट्रिंग के साथ टोकन के लिए पॉइंटर्स सेट कर सकता है। चरम मामले में जब स्ट्रिंग में केवल विभाजक होते हैं, तो एक को स्ट्रिंग की लंबाई प्लस 1 प्राप्त होती है, जिसके परिणामस्वरूप खाली टोकन होते हैं। संशोधित होने के लिए स्ट्रिंग को डुप्लिकेट करना व्यावहारिक है।

शीर्ष लेख फ़ाइल:

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

0

boost::tokenizerआपका मित्र है, लेकिन विरासत / प्रकारों के बजाय wstring/ उपयोग करके अंतर्राष्ट्रीयकरण (i18n) के संदर्भ में अपने कोड को पोर्टेबल बनाने पर विचार करें ।wchar_tstringchar

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

"विरासत" निश्चित रूप से सही नहीं है और wchar_tएक भयानक कार्यान्वयन पर निर्भर प्रकार है जिसे किसी को भी उपयोग नहीं करना चाहिए जब तक कि बिल्कुल आवश्यक न हो।
कॉफ़ेइंडकोड

Wchar_t का उपयोग किसी भी तरह से i18n मुद्दों को स्वचालित रूप से हल नहीं करता है। आप उस समस्या को हल करने के लिए एन्कोडिंग का उपयोग करते हैं। यदि आप एक सीमांकक द्वारा एक स्ट्रिंग को विभाजित कर रहे हैं, तो यह निहित है कि स्ट्रिंग के अंदर किसी भी टोकन की एन्कोडेड सामग्री के साथ सीमांकक टकराता नहीं है। भागने की जरूरत हो सकती है, आदि wchar_t इस के लिए एक जादुई समाधान नहीं है।
योनिल

0

सरल सी ++ कोड (मानक सी ++ 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;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.