[C++] الطريقة الأكثر أناقة لتكرار كلمات سلسلة


Answers

أنا استخدم هذا لتقسيم السلسلة من قبل محدد. يضع الأول النتائج في ناقل متجه مسبقًا ، ويعود الثاني إلى متجه جديد.

#include <string>
#include <sstream>
#include <vector>
#include <iterator>

template<typename Out>
void split(const std::string &s, char delim, Out result) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        *(result++) = item;
    }
}

std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    split(s, delim, std::back_inserter(elems));
    return elems;
}

لاحظ أن هذا الحل لا يتخطى الرموز الفارغة ، لذلك سيجد ما يلي 4 عناصر ، واحد منها فارغ:

std::vector<std::string> x = split("one:two::three", ':');
Question

ما هي الطريقة الأكثر أناقة لتكرار الكلمات من سلسلة؟ يمكن افتراض أن السلسلة تتكون من كلمات مفصولة بمسافة بيضاء.

لاحظ أنني لست مهتمًا بوظائف السلسلة C أو ذلك النوع من التلاعب بالوصول / الوصول. أيضا ، يرجى إعطاء الأسبقية للأناقة على الكفاءة في إجابتك.

أفضل حل لدي الآن هو:

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main()
{
    string s = "Somewhere down the road";
    istringstream iss(s);

    do
    {
        string subs;
        iss >> subs;
        cout << "Substring: " << subs << endl;
    } while (iss);
}



#include <vector>
#include <string>
#include <sstream>

using namespace std;

int main()
{
    string str("Split me by whitespaces");
    string buf; // Have a buffer string
    stringstream ss(str); // Insert the string into a stream

    vector<string> tokens; // Create vector to hold our words

    while (ss >> buf)
        tokens.push_back(buf);
}



لدي حل خطوط 2 لهذه المشكلة:

char sep = ' ';
std::string s="1 This is an example";

for(size_t p=0, q=0; p!=s.npos; p=q)
  std::cout << s.substr(p+(p!=0), (q=s.find(sep, p+1))-p-(p!=0)) << std::endl;

ثم بدلا من الطباعة يمكنك وضعها في ناقلات.




حتى الآن ، استخدمت واحدة في Boost ، لكني احتجت إلى شيء لا يعتمد عليه ، لذلك جئت إلى هذا:

static void Split(std::vector<std::string>& lst, const std::string& input, const std::string& separators, bool remove_empty = true)
{
    std::ostringstream word;
    for (size_t n = 0; n < input.size(); ++n)
    {
        if (std::string::npos == separators.find(input[n]))
            word << input[n];
        else
        {
            if (!word.str().empty() || !remove_empty)
                lst.push_back(word.str());
            word.str("");
        }
    }
    if (!word.str().empty() || !remove_empty)
        lst.push_back(word.str());
}

والنقطة الجيدة هي أنه في separators يمكنك تمرير أكثر من حرف واحد.




إليك حل بسيط يستخدم فقط مكتبة regex القياسية

#include <regex>
#include <string>
#include <vector>

std::vector<string> Tokenize( const string str, const std::regex regex )
{
    using namespace std;

    std::vector<string> result;

    sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
    sregex_token_iterator reg_end;

    for ( ; it != reg_end; ++it ) {
        if ( !it->str().empty() ) //token could be empty:check
            result.emplace_back( it->str() );
    }

    return result;
}

تسمح وسيطة regex بالتحقق من الوسيطات المتعددة (المسافات ، الفواصل ، إلخ.)

عادةً ما أتحقق فقط من التقسيم على المساحات والفواصل ، لذلك لدي أيضًا هذه الوظيفة الافتراضية:

std::vector<string> TokenizeDefault( const string str )
{
    using namespace std;

    regex re( "[\\s,]+" );

    return Tokenize( str, re );
}

يتحقق "[\\s,]+" وجود مسافات ( \\s ) وفواصل ( , ).

ملاحظة ، إذا كنت تريد تقسيم wstring بدلاً من string ،

  • تغيير جميع std::regex إلى std::wregex
  • تغيير كل sregex_token_iterator إلى wsregex_token_iterator

ملاحظة ، قد تحتاج أيضًا إلى استخدام الوسيطة string بالرجوع ، استنادًا إلى المترجم الخاص بك.




#include<iostream>
#include<string>
#include<sstream>
#include<vector>
using namespace std;

    vector<string> split(const string &s, char delim) {
        vector<string> elems;
        stringstream ss(s);
        string item;
        while (getline(ss, item, delim)) {
            elems.push_back(item);
        }
        return elems;
    }

int main() {

        vector<string> x = split("thi is an sample test",' ');
        unsigned int i;
        for(i=0;i<x.size();i++)
            cout<<i<<":"<<x[i]<<endl;
        return 0;
}



Short and elegant

#include <vector>
#include <string>
using namespace std;

vector<string> split(string data, string token)
{
    vector<string> output;
    size_t pos = string::npos; // size_t to avoid improbable overflow
    do
    {
        pos = data.find(token);
        output.push_back(data.substr(0, pos));
        if (string::npos != pos)
            data = data.substr(pos + token.size());
    } while (string::npos != pos);
    return output;
}

can use any string as delimiter, also can be used with binary data (std::string supports binary data, including nulls)

using:

auto a = split("this!!is!!!example!string", "!!");

output:

this
is
!example!string



I like to use the boost/regex methods for this task since they provide maximum flexibility for specifying the splitting criteria.

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

int main() {
    std::string line("A:::line::to:split");
    const boost::regex re(":+"); // one or more colons

    // -1 means find inverse matches aka split
    boost::sregex_token_iterator tokens(line.begin(),line.end(),re,-1);
    boost::sregex_token_iterator end;

    for (; tokens != end; ++tokens)
        std::cout << *tokens << std::endl;
}



هيريس حل regex يستخدم فقط مكتبة regex القياسية. (أنا صدئ قليلاً ، لذلك قد يكون هناك بعض الأخطاء النحوية ، ولكن هذه هي الفكرة العامة على الأقل)

#include <regex.h>
#include <string.h>
#include <vector.h>

using namespace std;

vector<string> split(string s){
    regex r ("\\w+"); //regex matches whole words, (greedy, so no fragment words)
    regex_iterator<string::iterator> rit ( s.begin(), s.end(), r );
    regex_iterator<string::iterator> rend; //iterators to iterate thru words
    vector<string> result<regex_iterator>(rit, rend);
    return result;  //iterates through the matches to fill the vector
}



باستخدام std::stringstream كما لديك تعمل بشكل جيد تماما ، وتفعل ما تريده بالضبط. إذا كنت تبحث فقط عن طريقة مختلفة لتنفيذ الأشياء ، يمكنك استخدام std::find() / std::find_first_of() و std::string::substr() .

إليك مثال على ذلك:

#include <iostream>
#include <string>

int main()
{
    std::string s("Somewhere down the road");
    std::string::size_type prev_pos = 0, pos = 0;

    while( (pos = s.find(' ', pos)) != std::string::npos )
    {
        std::string substring( s.substr(prev_pos, pos-prev_pos) );

        std::cout << substring << '\n';

        prev_pos = ++pos;
    }

    std::string substring( s.substr(prev_pos, pos-prev_pos) ); // Last word
    std::cout << substring << '\n';

    return 0;
}



هذا مشابه لسؤال كيف أقوم بتكوين سلسلة في C ++؟ .

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

using namespace std;
using namespace boost;

int main(int argc, char** argv)
{
    string text = "token  test\tstring";

    char_separator<char> sep(" \t");
    tokenizer<char_separator<char>> tokens(text, sep);
    for (const string& t : tokens)
    {
        cout << t << "." << endl;
    }
}



Get Boost ! : -)

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <vector>

using namespace std;
using namespace boost;

int main(int argc, char**argv) {
    typedef vector < string > list_type;

    list_type list;
    string line;

    line = "Somewhere down the road";
    split(list, line, is_any_of(" "));

    for(int i = 0; i < list.size(); i++)
    {
        cout << list[i] << endl;
    }

    return 0;
}

This example gives the output -

Somewhere
down
the
road



لا تملك المحكمة الخاصة بلبنان هذه الطريقة متاحة بالفعل.

ومع ذلك ، يمكنك استخدام الدالة strtok() C باستخدام العضو std::string::c_str() أو يمكنك كتابة الخاصة بك. في ما يلي نموذج تعليمة برمجية عثرت عليه بعد بحث Google سريع ( "تقسيم سلسلة STL" ):

void Tokenize(const string& str,
              vector<string>& tokens,
              const string& delimiters = " ")
{
    // Skip delimiters at beginning.
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    string::size_type pos     = str.find_first_of(delimiters, lastPos);

    while (string::npos != pos || string::npos != lastPos)
    {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

مأخوذة من: http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html

إذا كانت لديك أسئلة حول نموذج التعليمات البرمجية ، فاترك تعليقًا وسأشرح لك.

ولأنه لا يقوم بتطبيق typedef يسمى iterator أو overload ، فإن المشغل << لا يعني أنه شفرة سيئة. أنا استخدم C وظائف في كثير من الأحيان. على سبيل المثال ، تكون كل من printf و scanf أسرع من std::cin و std::cout (بشكل ملحوظ) ، وبناء الجملة fopen هو أكثر ملاءمة للأنواع الثنائية ، كما أنها تميل إلى إنتاج EXE أصغر.

لا تباع في صفقة "أناقة على الأداء" هذه .




وهنا حل آخر. انها مدمجة وفعالة بشكل معقول:

std::vector<std::string> split(const std::string &text, char sep) {
  std::vector<std::string> tokens;
  std::size_t start = 0, end = 0;
  while ((end = text.find(sep, start)) != std::string::npos) {
    tokens.push_back(text.substr(start, end - start));
    start = end + 1;
  }
  tokens.push_back(text.substr(start));
  return tokens;
}

يمكن بسهولة تخطيطه للتعامل مع فواصل السلسلة ، سلاسل واسعة ، إلخ.

لاحظ أن تقسيم "" النتائج في سلسلة فارغة واحدة وتقسيم "," (أي. sep) ينتج عن سلسلتين فارغتين.

يمكن أيضًا توسيعه بسهولة لتخطي الرموز الفارغة:

std::vector<std::string> split(const std::string &text, char sep) {
    std::vector<std::string> tokens;
    std::size_t start = 0, end = 0;
    while ((end = text.find(sep, start)) != std::string::npos) {
        if (end != start) {
          tokens.push_back(text.substr(start, end - start));
        }
        start = end + 1;
    }
    if (end != start) {
       tokens.push_back(text.substr(start));
    }
    return tokens;
}

إذا كان من المطلوب تقسيم سلسلة في محددات متعددة أثناء تخطي الرموز الفارغة ، فيمكن استخدام هذا الإصدار:

std::vector<std::string> split(const std::string& text, const std::string& delims)
{
    std::vector<std::string> tokens;
    std::size_t start = text.find_first_not_of(delims), end = 0;

    while((end = text.find_first_of(delims, start)) != std::string::npos)
    {
        tokens.push_back(text.substr(start, end - start));
        start = text.find_first_not_of(delims, end);
    }
    if(start != std::string::npos)
        tokens.push_back(text.substr(start));

    return tokens;
}



What about this:

#include <string>
#include <vector>

using namespace std;

vector<string> split(string str, const char delim) {
    vector<string> v;
    string tmp;

    for(string::const_iterator i; i = str.begin(); i <= str.end(); ++i) {
        if(*i != delim && i != str.end()) {
            tmp += *i; 
        } else {
            v.push_back(tmp);
            tmp = ""; 
        }   
    }   

    return v;
}