سؤال

جافا مريحة تقسيم الطريقة:

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

هل هناك طريقة سهلة للقيام بذلك في C++?

هل كانت مفيدة؟

المحلول

الخاص بك الحالة بسيطة ويمكن بسهولة أن تكون بنيت باستخدام std::string::find الأسلوب.بيد أن نلقي نظرة على دفعة.Tokenizer.انه لشيء رائع.دفعة عموما بعض باردة جدا سلسلة أدوات.

نصائح أخرى

على دفعة 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;
    }
}

هنا هو بسيط واحد:

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

استخدام strtok.في رأيي ليس هناك حاجة إلى بناء فئة حول tokenizing إلا strtok لا نقدم لك ما تحتاجه.ربما لا, ولكن في 15+ سنوات من الكتابة المختلفة تحليل التعليمات البرمجية في C و C++ ، لقد استعملت دائما strtok.هنا مثال

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

بعض المحاذير (التي قد لا تتناسب مع الاحتياجات الخاصة بك).السلسلة "دمرت" في هذه العملية ، بمعنى أن EOS الشخصيات توضع مضمنة في delimter البقع.الاستخدام الصحيح قد تتطلب إجراء غير const نسخة من السلسلة.يمكنك أيضا تغيير قائمة المحددات منتصف تحليل.

في رأيي الخاص, رمز أعلاه هو الآن أبسط وأسهل في الاستخدام من كتابة فئة منفصلة من أجل ذلك.بالنسبة لي, هذا هو واحد من تلك المهام أن اللغة تقدم وهو يفعل ذلك بشكل جيد ونظيف.أنها ببساطة "ج" القائمة على الحل.أنه من المناسب ، فإنه من السهل و لا تكتب الكثير من رمز إضافية :-)

سريع آخر طريقة هي استخدام getline.شيء من هذا القبيل:

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

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

إذا كنت تريد, يمكنك جعل بسيطة split() طريقة العودة vector<string>, الذي هو من المفيد حقا.

يمكنك استخدام تيارات ، التكرار ، خوارزمية نسخة للقيام بذلك مباشرة إلى حد ما.

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

لا إهانة الناس ، ولكن هذه مشكلة بسيطة, كنت جعل الأشياء طريقة معقدة جدا.هناك الكثير من الأسباب لاستخدام دفعة.ولكن هذا شيء بسيط ، هو مثل ضرب يطير مع 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/.

(أنا أصلا كتابة رد على دوغ السؤال: C++ سلاسل تعديل و استخراج بناء على فواصل (مغلقة).ولكن منذ مارتن نيويورك مغلقة على هذا السؤال مع مؤشر هنا...أنا مجرد تعميم قانون بلدي.)

دفعة قوية وظيفة الانقسام: دفعة::الخوارزمية::الانقسام.

نموذج البرنامج:

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

الحل باستخدام 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;
    }
}

أنا أعرف أنك طلبت C++ الحل في هذا المفيدة:

Qt

#include <QString>

...

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

ميزة على دفعة في هذا المثال هو أنه مباشر واحد إلى واحد التعيين على وظيفة الخاص بك التعليمات البرمجية.

انظر المزيد في Qt الوثائق

هنا عينة 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());
}

هذا هو بسيط STL-الحل الوحيد (~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);
    }
}

في محاولة منه يعيش!

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

نشرت لي هذه الإجابة عن مثل هذا السؤال.
لا إعادة اختراع العجلة.لقد استعملت عدد من المكتبات و الأسرع والأكثر مرونة لقد جئت عبر هو: 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 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;
}

تحقق من هذا المثال.قد تساعدك..

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

MFC/ATL لطيفة جدا tokenizer.من 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

يمكنك ببساطة استخدام التعبير العادي المكتبة وحل ذلك باستخدام التعبيرات العادية.

استخدام التعبير (\w+) و المتغير في \1 (أو 1 دولار اعتمادا على مكتبة تنفيذ التعابير العادية).

إذا كنت على استعداد لاستخدام C, يمكنك استخدام strtok وظيفة.يجب أن تدفع الانتباه إلى متعدد خيوط القضايا عند استخدامه.

من أجل أشياء بسيطة أنا فقط استخدام ما يلي:

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

الجبان تنويه:أكتب البيانات في الوقت الحقيقي تجهيز البرمجيات حيث تأتي البيانات من خلال ملفات ثنائية المقابس أو استدعاء API (I/O بطاقات الكاميرا).انا لم استخدم هذه الدالة شيء أكثر تعقيدا أو في الوقت الحرج من قراءة ملفات التكوين الخارجية على بدء التشغيل.

العديد من معقد الاقتراحات هنا.هذه محاولة بسيطة 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;
}

اعتقدت أن كان ما >> المشغل على سلسلة تيارات كانت:

string word; sin >> word;

آدم بيرس الجواب يوفر ناحية نسج tokenizer أخذ في const char*.انها قليلا أكثر تعقيدا القيام به مع التكرار لأن تزايد عدد string's نهاية التكرار غير معرف.وقال معين 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 هو خيار بسيط:

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 لا يمكن استخدامها على مواضيع متعددة في وقت واحد (هذا ومع ذلك قد يكون تنفيذ محددة ، على سبيل المثال: Visual Studio تنفيذ موضوع آمنة)
  3. الدعوة strtok يعدل string فمن التشغيل ، لذلك فإنه لا يمكن استخدامها على const strings ، const char*ق ، أو الحرفي السلاسل ، tokenize أي من هذه مع strtok أو أن تعمل على string من محتويات تحتاج إلى الحفاظ عليها ، str يجب أن يكون نسخ ثم نسخ يمكن تشغيلها على

كل الطرق السابقة لا يمكن أن تولد tokenized vector في المكان ، أي دون التلخيص لهم في وظيفة مساعد أنهم لا يمكن تهيئة const vector<string> tokens.هذه الوظيفة و القدرة على تقبل أي white-space محدد يمكن تسخيرها باستخدام 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 تخصيص.


إذا كان أي من الخيارات المذكورة أعلاه هي flexable ما يكفي لإجراء عملية تحويل البيانات إلى رموز الاحتياجات الخيار الأكثر مرونة هو استخدام regex_token_iterator بالطبع مع هذه المرونة يأتي أكبر حساب ، ولكن مرة أخرى هذا هو المرجح المخفية في string تخصيص التكاليف.نقول على سبيل المثال نريد أن tokenize على أساس غير هرب الفواصل أيضا تناول white-space نظرا الإدخال التالية: 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() };

مثال حي

هنا هو النهج الذي يسمح لك السيطرة على ما إذا كانت فارغة الرموز المضمنة (مثل 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;
}

يبدو لي غريبا أن مع كل سرعة واعية المهووسين هنا حتى لا أحد لديه عرض الإصدار الذي يستخدم وقت الترجمة ولدت ابحث جدول محدد (على سبيل المثال تنفيذ مزيد من أسفل).باستخدام نظرة الجدول و التكرار يجب أن تغلب std::regex في الكفاءة ، إذا كنت لا تحتاج للتغلب regex فقط استخدام هذا المعيار اعتبارا من C++11 ومرونة فائقة.

وقد اقترح بعض التعابير المنطقية بالفعل ولكن لودي هنا هو تعبئتها سبيل المثال يجب أن تفعل بالضبط ما OP تتوقع:

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 بت يمكننا أن نجعل نظرة الجدول في وقت التحويل البرمجي باستخدام 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>;

مع أنه في مكان صنع 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

أعرف أن هذا السؤال أجبت ولكن أريد أن تسهم.ربما الحل هو بسيط بعض الشيء ولكن هذا هو ما جئت به:

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

يرجى التعليق إذا كان هناك نهج أفضل شيء في قانون بلدي أو إذا كان هناك شيء خاطئ.

لا يوجد طريقة مباشرة لفعل هذا.الرجوع هذا رمز المشروع البرمجية المصدر لمعرفة كيفية بناء الصف من أجل هذا.

يمكنك الاستفادة من دفعة::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;
}

إذا كان الحد الأقصى طول سلسلة الإدخال إلى tokenized هو معروف ، يمكن استغلال هذا وتنفيذ سريع جدا الإصدار.أنا رسم الفكرة الأساسية أدناه ، والتي كانت مستوحاة من كل strtok() و "لاحقة مجموعة"-هيكل بيانات وصفها جون بنتلي "البرمجة بيرلز" 2nd edition, الفصل 15.فئة C++ في هذه الحالة يعطي بعض المنظمات وراحة الاستخدام.تنفيذ أظهرت يمكن تمديدها بسهولة إزالة البائعين الرائدة زائدة الأحرف بيضاء في الرموز.

أساسا واحدا يمكن أن تحل محل الشخصيات فاصل مع سلسلة إنهاء '\0'-الشخصيات و مجموعة مؤشرات الرموز withing تعديل السلسلة.في الحالة القصوى عندما السلسلة تتكون فقط من فصل واحد يحصل سلسلة طول زائد 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;
    }
};

Implementattion الملف:

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

boost::tokenizer هو صديقك ، ولكن النظر في اتخاذ الخاص بك رمز المحمولة مع الإشارة إلى التدويل (i18n) المسائل باستخدام wstring/wchar_t بدلا من تراث string/char أنواع.

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

بسيطة رمز 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;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top