web-dev-qa-db-fra.com

Comment puis-je parcourir les mots d'une chaîne?

J'essaie de parcourir les mots d'une chaîne.

On peut supposer que la chaîne est composée de mots séparés par des espaces.

Notez que je ne suis pas intéressé par les fonctions de chaîne C ni par ce type de manipulation/d'accès des caractères Aussi, veuillez donner la priorité à l'élégance plutôt qu'à l'efficacité dans votre réponse.

La meilleure solution que j'ai actuellement est la suivante:

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

Y a-t-il une manière plus élégante de faire ceci?

2767
Ashwin Nanjappa

Pour ce que cela vaut, voici un autre moyen d'extraire des jetons d'une chaîne d'entrée en utilisant uniquement les fonctions de bibliothèque standard. C'est un exemple de la puissance et de l'élégance du design de la STL.

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>

int main() {
    using namespace std;
    string sentence = "And I feel fine...";
    istringstream iss(sentence);
    copy(istream_iterator<string>(iss),
         istream_iterator<string>(),
         ostream_iterator<string>(cout, "\n"));
}

Au lieu de copier les jetons extraits dans un flux de sortie, vous pouvez les insérer dans un conteneur en utilisant le même algorithme générique copy.

vector<string> tokens;
copy(istream_iterator<string>(iss),
     istream_iterator<string>(),
     back_inserter(tokens));

... ou créez directement la vector:

vector<string> tokens{istream_iterator<string>{iss},
                      istream_iterator<string>{}};
1253
Zunino

J'utilise ceci pour scinder une chaîne par un délimiteur. La première place les résultats dans un vecteur pré-construit, la seconde renvoie un nouveau vecteur.

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

Notez que cette solution n'ignore pas les jetons vides. Par conséquent, les éléments suivants trouveront 4 éléments, dont l'un est vide:

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

Une solution possible en utilisant Boost pourrait être:

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));

Cette approche peut être encore plus rapide que l’approche stringstream. Et puisqu'il s'agit d'une fonction de modèle générique, il peut être utilisé pour scinder d'autres types de chaînes (wchar, etc. ou UTF-8) en utilisant toutes sortes de délimiteurs.

Voir documentation pour plus de détails.

813
ididak
#include <vector>
#include <string>
#include <sstream>

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

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

    while (ss >> buf)
        tokens.Push_back(buf);

    return 0;
}
342
kev

Pour ceux avec qui il ne convient pas de sacrifier toute efficacité pour la taille du code et de voir le mot «efficace» comme un type d'élégance, les éléments suivants devraient trouver leur bonheur (et je pense que la classe conteneur de modèle est un ajout incroyablement élégant.):

template < class ContainerT >
void tokenize(const std::string& str, ContainerT& tokens,
              const std::string& delimiters = " ", bool trimEmpty = false)
{
   std::string::size_type pos, lastPos = 0, length = str.length();

   using value_type = typename ContainerT::value_type;
   using size_type  = typename ContainerT::size_type;

   while(lastPos < length + 1)
   {
      pos = str.find_first_of(delimiters, lastPos);
      if(pos == std::string::npos)
      {
         pos = length;
      }

      if(pos != lastPos || !trimEmpty)
         tokens.Push_back(value_type(str.data()+lastPos,
               (size_type)pos-lastPos ));

      lastPos = pos + 1;
   }
}

Je choisis généralement d'utiliser les types std::vector<std::string> comme deuxième paramètre (ContainerT) ... mais list<> est bien plus rapide que vector<> lorsque l'accès direct n'est pas nécessaire, et vous pouvez même créer votre propre classe de chaîne et utiliser quelque chose comme std::list<subString>subString ne le fait faire des copies pour des augmentations de vitesse incroyables.

Il est plus du double de la rapidité de la tokenize sur cette page et presque 5 fois plus rapide que d’autres. De plus, avec les types de paramètres parfaits, vous pouvez éliminer toutes les copies de chaînes et de listes pour des augmentations de vitesse supplémentaires.

De plus, il ne fait pas le retour du résultat (extrêmement inefficace), mais passe plutôt les jetons comme référence, vous permettant ainsi de créer des jetons en utilisant plusieurs appels si vous le souhaitez.

Enfin, il vous permet de spécifier si vous souhaitez supprimer les jetons vides des résultats via un dernier paramètre facultatif.

Il ne manque que std::string... le reste est optionnel. Il n'utilise pas de flux ni la bibliothèque boost, mais est suffisamment flexible pour accepter naturellement certains de ces types étrangers.

176
Marius

Voici une autre solution. C'est compact et raisonnablement efficace:

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

Il peut facilement être configuré pour gérer des séparateurs de chaînes, des chaînes larges, etc.

Notez que la division de "" donne une seule chaîne vide et que la division de "," (ie. Sep) donne deux chaînes vides.

Il peut également être facilement étendu pour ignorer les jetons vides:

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

Si vous souhaitez fractionner une chaîne sur plusieurs délimiteurs tout en sautant des jetons vides, cette version peut être utilisée:

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;
}
156
Alec Thomas

C'est mon moyen préféré pour parcourir une chaîne. Vous pouvez faire ce que vous voulez par mot.

string line = "a line of text to iterate through";
string Word;

istringstream iss(line, istringstream::in);

while( iss >> Word )     
{
    // Do something on `Word` here...
}
113
gnomed

Ceci est similaire à la question de débordement de pile Comment tokeniser une chaîne en 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;
    }
}
79
Ferruccio

J'aime ce qui suit, car il place les résultats dans un vecteur, prend en charge une chaîne en tant que délimiteur et permet de garder les valeurs vides. Mais, ça ne semble pas aussi bien alors.

#include <ostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

vector<string> split(const string& s, const string& delim, const bool keep_empty = true) {
    vector<string> result;
    if (delim.empty()) {
        result.Push_back(s);
        return result;
    }
    string::const_iterator substart = s.begin(), subend;
    while (true) {
        subend = search(substart, s.end(), delim.begin(), delim.end());
        string temp(substart, subend);
        if (keep_empty || !temp.empty()) {
            result.Push_back(temp);
        }
        if (subend == s.end()) {
            break;
        }
        substart = subend + delim.size();
    }
    return result;
}

int main() {
    const vector<string> words = split("So close no matter how far", " ");
    copy(words.begin(), words.end(), ostream_iterator<string>(cout, "\n"));
}

Bien sûr, Boost a un split() qui fonctionne partiellement comme ça. Et, par «espace blanc», vous entendez vraiment tout type d’espace blanc, l’utilisation de la division de Boost avec is_any_of() fonctionnant parfaitement.

66
Shadow2531

La STL ne dispose pas encore d'une telle méthode.

Toutefois, vous pouvez utiliser la fonction strtok() de C en utilisant la fonction std::string::c_str() member, ou vous pouvez écrire le vôtre. Voici un exemple de code que j'ai trouvé après une recherche rapide dans Google ("STL string split"):

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

Extrait de: http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html

Si vous avez des questions sur l'exemple de code, laissez un commentaire et je vais vous expliquer.

Et le fait qu’il n’implémente pas un typedef appelé itérateur ou ne surcharge pas l’opérateur << ne signifie pas qu’il s’agit d’un mauvais code. J'utilise les fonctions C assez fréquemment. Par exemple, printf et scanf sont plus rapides que std::cin et std::cout (significativement), la syntaxe fopen est beaucoup plus conviviale pour les types binaires, et ils ont aussi tendance à produire des EXE plus petits.

Ne vous vendez pas sur cet accord "Elegance over performance".

52
nlaq

Voici une fonction scindée qui:

  • est générique
  • utilise le standard C++ (pas de boost)
  • accepte plusieurs délimiteurs
  • ignore les jetons vides (peut facilement être changé)

    template<typename T>
    vector<T> 
    split(const T & str, const T & delimiters) {
        vector<T> v;
        typename T::size_type start = 0;
        auto pos = str.find_first_of(delimiters, start);
        while(pos != T::npos) {
            if(pos != start) // ignore empty tokens
                v.emplace_back(str, start, pos - start);
            start = pos + 1;
            pos = str.find_first_of(delimiters, start);
        }
        if(start < str.length()) // ignore trailing delimiter
            v.emplace_back(str, start, str.length() - start); // add what's left of the string
        return v;
    }
    

Exemple d'utilisation:

    vector<string> v = split<string>("Hello, there; World", ";,");
    vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");
40
Marco M.

J'ai une solution à 2 lignes à ce problème:

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;

Ensuite, au lieu d’imprimer, vous pouvez le mettre dans un vecteur.

35
rhomu

Encore un autre moyen flexible et rapide

template<typename Operator>
void tokenize(Operator& op, const char* input, const char* delimiters) {
  const char* s = input;
  const char* e = s;
  while (*e != 0) {
    e = s;
    while (*e != 0 && strchr(delimiters, *e) == 0) ++e;
    if (e - s > 0) {
      op(s, e - s);
    }
    s = e + 1;
  }
}

Pour l'utiliser avec un vecteur de chaînes (Edit: Depuis que quelqu'un a indiqué de ne pas hériter des classes STL ... hrmf;)):

template<class ContainerType>
class Appender {
public:
  Appender(ContainerType& container) : container_(container) {;}
  void operator() (const char* s, unsigned length) { 
    container_.Push_back(std::string(s,length));
  }
private:
  ContainerType& container_;
};

std::vector<std::string> strVector;
Appender v(strVector);
tokenize(v, "A number of words to be tokenized", " \t");

C'est tout! Et ce n’est qu’une façon d’utiliser le tokenizer, comme comment compter juste Compter les mots:

class WordCounter {
public:
  WordCounter() : noOfWords(0) {}
  void operator() (const char*, unsigned) {
    ++noOfWords;
  }
  unsigned noOfWords;
};

WordCounter wc;
tokenize(wc, "A number of words to be counted", " \t"); 
ASSERT( wc.noOfWords == 7 );

Limité par l'imagination;)

35
Robert

Voici une solution simple qui utilise uniquement la bibliothèque regex standard

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

L'argument regex permet de rechercher plusieurs arguments (espaces, virgules, etc.)

En général, je vérifie uniquement de séparer les espaces et les virgules, de sorte que j'ai aussi cette fonction par défaut:

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

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

    return Tokenize( str, re );
}

Le "[\\s,]+" vérifie les espaces (\\s) et les virgules (,).

Notez que si vous souhaitez scinder wstring au lieu de string,

  • change tout std::regex en std::wregex
  • change tout sregex_token_iterator en wsregex_token_iterator

Notez que vous pouvez également vouloir prendre l'argument chaîne par référence, en fonction de votre compilateur.

31
dk123

Si vous aimez utiliser boost, mais souhaitez utiliser une chaîne entière comme séparateur (au lieu de caractères uniques comme dans la plupart des solutions proposées précédemment), vous pouvez utiliser le boost_split_iterator.

Exemple de code comprenant un modèle pratique:

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

template<typename _OutputIterator>
inline void split(
    const std::string& str, 
    const std::string& delim, 
    _OutputIterator result)
{
    using namespace boost::algorithm;
    typedef split_iterator<std::string::const_iterator> It;

    for(It iter=make_split_iterator(str, first_Finder(delim, is_equal()));
            iter!=It();
            ++iter)
    {
        *(result++) = boost::copy_range<std::string>(*iter);
    }
}

int main(int argc, char* argv[])
{
    using namespace std;

    vector<string> splitted;
    split("HelloFOOworldFOO!", "FOO", back_inserter(splitted));

    // or directly to console, for example
    split("HelloFOOworldFOO!", "FOO", ostream_iterator<string>(cout, "\n"));
    return 0;
}
25
zerm

Utiliser std::stringstream en tant que vous fonctionne parfaitement et faites exactement ce que vous vouliez. Si vous cherchez simplement une autre façon de faire, vous pouvez utiliser std::find() / std::find_first_of() et std::string::substr() .

Voici un exemple:

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

Il existe une fonction nommée strtok .

#include<string>
using namespace std;

vector<string> split(char* str,const char* delim)
{
    char* saveptr;
    char* token = strtok_r(str,delim,&saveptr);

    vector<string> result;

    while(token != NULL)
    {
        result.Push_back(token);
        token = strtok_r(NULL,delim,&saveptr);
    }
    return result;
}
19
Pratik Deoghare

Voici une solution regex qui utilise uniquement la bibliothèque regex standard. (Je suis un peu rouillé, il peut donc y avoir quelques erreurs de syntaxe, mais c'est au moins l'idée générale)

#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
}
18
AJMansfield

Le stringstream peut être pratique si vous devez analyser la chaîne avec des symboles autres que des espaces:

string s = "Name:JAck; Spouse:Susan; ...";
string dummy, name, spouse;

istringstream iss(s);
getline(iss, dummy, ':');
getline(iss, name, ';');
getline(iss, dummy, ':');
getline(iss, spouse, ';')
16
lukmac

Jusqu'ici, j'ai utilisé celui de Boost , mais j'avais besoin de quelque chose qui n'en dépend pas, alors je suis arrivé à ceci:

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

Un bon point est que dans separators, vous pouvez passer plus d’un caractère.

14
Goran

Court et élégant

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

peut utiliser n'importe quelle chaîne comme séparateur, et peut également être utilisé avec des données binaires (std :: string supporte les données binaires, y compris les valeurs NULL)

en utilisant:

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

sortie:

this
is
!example!string
14
user1438233

J'ai moi-même roulé avec strtok et utilisé boost pour diviser une chaîne. La meilleure méthode que j'ai trouvée est la C++ String Toolkit Library . C'est incroyablement flexible et rapide.

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

La boîte à outils a beaucoup plus de souplesse que ce que montre cet exemple simple, mais son utilité pour analyser une chaîne en éléments utiles est incroyable.

13
DannyK

J'ai créé ceci parce que j'avais besoin d'un moyen facile de séparer des chaînes et des chaînes basées sur c ... J'espère que quelqu'un d'autre pourra le trouver utile également. En outre, il ne repose pas sur les jetons et vous pouvez utiliser les champs comme délimiteurs, ce qui est une autre clé dont j'avais besoin.

Je suis sûr qu'il y a des améliorations à apporter pour améliorer encore son élégance et s'il-vous-plaît le faire par tous les moyens.

StringSplitter.hpp:

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

using namespace std;

class StringSplit
{
private:
    void copy_fragment(char*, char*, char*);
    void copy_fragment(char*, char*, char);
    bool match_fragment(char*, char*, int);
    int untilnextdelim(char*, char);
    int untilnextdelim(char*, char*);
    void assimilate(char*, char);
    void assimilate(char*, char*);
    bool string_contains(char*, char*);
    long calc_string_size(char*);
    void copy_string(char*, char*);

public:
    vector<char*> split_cstr(char);
    vector<char*> split_cstr(char*);
    vector<string> split_string(char);
    vector<string> split_string(char*);
    char* String;
    bool do_string;
    bool keep_empty;
    vector<char*> Container;
    vector<string> ContainerS;

    StringSplit(char * in)
    {
        String = in;
    }

    StringSplit(string in)
    {
        size_t len = calc_string_size((char*)in.c_str());
        String = new char[len + 1];
        memset(String, 0, len + 1);
        copy_string(String, (char*)in.c_str());
        do_string = true;
    }

    ~StringSplit()
    {
        for (int i = 0; i < Container.size(); i++)
        {
            if (Container[i] != NULL)
            {
                delete[] Container[i];
            }
        }
        if (do_string)
        {
            delete[] String;
        }
    }
};

StringSplitter.cpp:

#include <string.h>
#include <iostream>
#include <vector>
#include "StringSplit.hpp"

using namespace std;

void StringSplit::assimilate(char*src, char delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.Push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.Push_back(x);
            }

        }
        else
        {
            delete[] temp;
        }
    }
}

void StringSplit::assimilate(char*src, char* delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.Push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.Push_back(x);
            }
        }
        else
        {
            delete[] temp;
        }
    }
}

long StringSplit::calc_string_size(char* _in)
{
    long i = 0;
    while (*_in++)
    {
        i++;
    }
    return i;
}

bool StringSplit::string_contains(char* haystack, char* needle)
{
    size_t len = calc_string_size(needle);
    size_t lenh = calc_string_size(haystack);
    while (lenh--)
    {
        if (match_fragment(haystack + lenh, needle, len))
        {
            return true;
        }
    }
    return false;
}

bool StringSplit::match_fragment(char* _src, char* cmp, int len)
{
    while (len--)
    {
        if (*(_src + len) != *(cmp + len))
        {
            return false;
        }
    }
    return true;
}

int StringSplit::untilnextdelim(char* _in, char delim)
{
    size_t len = calc_string_size(_in);
    if (*_in == delim)
    {
        _in += 1;
        return len - 1;
    }

    int c = 0;
    while (*(_in + c) != delim && c < len)
    {
        c++;
    }

    return c;
}

int StringSplit::untilnextdelim(char* _in, char* delim)
{
    int s = calc_string_size(delim);
    int c = 1 + s;

    if (!string_contains(_in, delim))
    {
        return calc_string_size(_in);
    }
    else if (match_fragment(_in, delim, s))
    {
        _in += s;
        return calc_string_size(_in);
    }

    while (!match_fragment(_in + c, delim, s))
    {
        c++;
    }

    return c;
}

void StringSplit::copy_fragment(char* dest, char* src, char delim)
{
    if (*src == delim)
    {
        src++;
    }

    int c = 0;
    while (*(src + c) != delim && *(src + c))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

void StringSplit::copy_string(char* dest, char* src)
{
    int i = 0;
    while (*(src + i))
    {
        *(dest + i) = *(src + i);
        i++;
    }
}

void StringSplit::copy_fragment(char* dest, char* src, char* delim)
{
    size_t len = calc_string_size(delim);
    size_t lens = calc_string_size(src);

    if (match_fragment(src, delim, len))
    {
        src += len;
        lens -= len;
    }

    int c = 0;
    while (!match_fragment(src + c, delim, len) && (c < lens))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

vector<char*> StringSplit::split_cstr(char Delimiter)
{
    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char Delimiter)
{
    do_string = true;

    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

vector<char*> StringSplit::split_cstr(char* Delimiter)
{
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while(*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String,Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char* Delimiter)
{
    do_string = true;
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while (*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

Exemples:

int main(int argc, char*argv[])
{
    StringSplit ss = "This:CUT:is:CUT:an:CUT:example:CUT:cstring";
    vector<char*> Split = ss.split_cstr(":CUT:");

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

    return 0;
}

Est-ce que la sortie:

Ce
est
un
Exemple
C chaîne

int main(int argc, char*argv[])
{
    StringSplit ss = "This:is:an:example:cstring";
    vector<char*> Split = ss.split_cstr(':');

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

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This[SPLIT]is[SPLIT]an[SPLIT]example[SPLIT]string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string("[SPLIT]");

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

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This|is|an|example|string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string('|');

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

    return 0;
}

Pour conserver les entrées vides (par défaut, les vides seront exclus):

StringSplit ss = mystring;
ss.keep_empty = true;
vector<string> Split = ss.split_string(":DELIM:");

Le but était de le rendre similaire à la méthode Split () de C #, dans laquelle fractionner une chaîne est aussi simple que:

String[] Split = 
    "Hey:cut:what's:cut:your:cut:name?".Split(new[]{":cut:"}, StringSplitOptions.None);

foreach(String X in Split)
{
    Console.Write(X);
}

J'espère que quelqu'un d'autre trouvera cela aussi utile que moi.

11
Steve Dell

Et ça:

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

Cette réponse prend la chaîne et la met dans un vecteur de chaînes. Il utilise la bibliothèque de boost.

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));
9
NL628

J'aime utiliser les méthodes boost/regex pour cette tâche car elles offrent une flexibilité maximale pour la spécification des critères de fractionnement.

#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;
}
9
Marty B

Voici une autre façon de le faire ..

void split_string(string text,vector<string>& words)
{
  int i=0;
  char ch;
  string Word;

  while(ch=text[i++])
  {
    if (isspace(ch))
    {
      if (!Word.empty())
      {
        words.Push_back(Word);
      }
      Word = "";
    }
    else
    {
      Word += ch;
    }
  }
  if (!Word.empty())
  {
    words.Push_back(Word);
  }
}
9
user246110
#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;
}
9
san45

Récemment, j'ai dû scinder un mot camelé en sous-mots. Il n'y a pas de délimiteurs, juste des caractères supérieurs. 

#include <string>
#include <list>
#include <locale> // std::isupper

template<class String>
const std::list<String> split_camel_case_string(const String &s)
{
    std::list<String> R;
    String w;

    for (String::const_iterator i = s.begin(); i < s.end(); ++i) {  {
        if (std::isupper(*i)) {
            if (w.length()) {
                R.Push_back(w);
                w.clear();
            }
        }
        w += *i;
    }

    if (w.length())
        R.Push_back(w);
    return R;
}

Par exemple, cela divise "AQueryTrades" en "A", "Query" et "Trades". La fonction fonctionne avec des chaînes étroites et larges. Parce qu'il respecte les paramètres régionaux actuels, il divise "RaumfahrtÜberwachungsVerordnung" en "Raumfahrt", "Überwachungs" et "Verordnung".

Note std::upper devrait être vraiment passé comme argument de template de fonction. Ensuite, le plus généralisé de cette fonction peut également se diviser en séparateurs tels que ",", ";" ou " ".

9
Andreas Spindler

Le code ci-dessous utilise strtok() pour scinder une chaîne en jetons et stocker les jetons dans un vecteur.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;


char one_line_string[] = "hello hi how are you Nice weather we are having ok then bye";
char seps[]   = " ,\t\n";
char *token;



int main()
{
   vector<string> vec_String_Lines;
   token = strtok( one_line_string, seps );

   cout << "Extracting and storing data in a vector..\n\n\n";

   while( token != NULL )
   {
      vec_String_Lines.Push_back(token);
      token = strtok( NULL, seps );
   }
     cout << "Displaying end result in vector line storage..\n\n";

    for ( int i = 0; i < vec_String_Lines.size(); ++i)
    cout << vec_String_Lines[i] << "\n";
    cout << "\n\n\n";


return 0;
}
8
Software_Designer

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

Cet exemple donne la sortie - 

Somewhere
down
the
road
8
Aleksey Bykov

J'utilise ce simple parce que notre classe String est "spéciale" (c'est-à-dire non standard):

void splitString(const String &s, const String &delim, std::vector<String> &result) {
    const int l = delim.length();
    int f = 0;
    int i = s.indexOf(delim,f);
    while (i>=0) {
        String token( i-f > 0 ? s.substring(f,i-f) : "");
        result.Push_back(token);
        f=i+l;
        i = s.indexOf(delim,f);
    }
    String token = s.substring(f);
    result.Push_back(token);
}
7
Abe
#include <iostream>
#include <regex>

using namespace std;

int main() {
   string s = "foo bar  baz";
   regex e("\\s+");
   regex_token_iterator<string::iterator> i(s.begin(), s.end(), e, -1);
   regex_token_iterator<string::iterator> end;
   while (i != end)
      cout << " [" << *i++ << "]";
}

OMI, c'est la chose la plus proche de re.split () de python. Voir cplusplus.com pour plus d’informations sur regex_token_iterator. Le -1 (4ème argument de regex_token_iterator ctor) est la section de la séquence qui ne correspond pas, en utilisant la correspondance comme séparateur.

6
solstice333

Ce qui suit est un bien meilleur moyen de le faire. Il peut prendre n'importe quel caractère et ne divise pas de lignes sauf si vous le souhaitez. Aucune bibliothèque spéciale n'est nécessaire (enfin, à part std, mais qui considère vraiment qu'il s'agit d'une bibliothèque supplémentaire), pas de pointeurs, pas de références et c'est statique. Tout simplement simple C++.

#pragma once
#include <vector>
#include <sstream>
using namespace std;
class Helpers
{
    public:
        static vector<string> split(string s, char delim)
        {
            stringstream temp (stringstream::in | stringstream::out);
            vector<string> elems(0);
            if (s.size() == 0 || delim == 0)
                return elems;
            for(char c : s)
            {
                if(c == delim)
                {
                    elems.Push_back(temp.str());
                    temp = stringstream(stringstream::in | stringstream::out);
                }
                else
                    temp << c;
            }
            if (temp.str().size() > 0)
                elems.Push_back(temp.str());
                return elems;
            }

        //Splits string s with a list of delimiters in delims (it's just a list, like if we wanted to
        //split at the following letters, a, b, c we would make delims="abc".
        static vector<string> split(string s, string delims)
        {
            stringstream temp (stringstream::in | stringstream::out);
            vector<string> elems(0);
            bool found;
            if(s.size() == 0 || delims.size() == 0)
                return elems;
            for(char c : s)
            {
                found = false;
                for(char d : delims)
                {
                    if (c == d)
                    {
                        elems.Push_back(temp.str());
                        temp = stringstream(stringstream::in | stringstream::out);
                        found = true;
                        break;
                    }
                }
                if(!found)
                    temp << c;
            }
            if(temp.str().size() > 0)
                elems.Push_back(temp.str());
            return elems;
        }
};
6
Kelly Elton

En tant qu'amateur, c'est la première solution qui m'est venue à l'esprit. Je suis un peu curieux de savoir pourquoi je n'ai pas encore vu de solution similaire, y a-t-il un problème fondamental dans la façon dont j'ai procédé?

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

std::vector<std::string> split(const std::string &s, const std::string &delims)
{
    std::vector<std::string> result;
    std::string::size_type pos = 0;
    while (std::string::npos != (pos = s.find_first_not_of(delims, pos))) {
        auto pos2 = s.find_first_of(delims, pos);
        result.emplace_back(s.substr(pos, std::string::npos == pos2 ? pos2 : pos2 - pos));
        pos = pos2;
    }
    return result;
}

int main()
{
    std::string text{"And then I said: \"I don't get it, why would you even do that!?\""};
    std::string delims{" :;\".,?!"};
    auto words = split(text, delims);
    std::cout << "\nSentence:\n  " << text << "\n\nWords:";
    for (const auto &w : words) {
        std::cout << "\n  " << w;
    }
    return 0;
}

http://cpp.sh/7wmzy

5
Jehjoa

J'ai écrit le morceau de code suivant. Vous pouvez spécifier un délimiteur, qui peut être une chaîne . Le résultat est similaire à String.split de Java avec une chaîne vide dans le résultat.

Par exemple, si nous appelons split ("ABCPICKABCANYABCTWO: ABC", "ABC"), le résultat est le suivant:

0  <len:0>
1 PICK <len:4>
2 ANY <len:3>
3 TWO: <len:4>
4  <len:0>

Code:

vector <string> split(const string& str, const string& delimiter = " ") {
    vector <string> tokens;

    string::size_type lastPos = 0;
    string::size_type pos = str.find(delimiter, lastPos);

    while (string::npos != pos) {
        // Found a token, add it to the vector.
        cout << str.substr(lastPos, pos - lastPos) << endl;
        tokens.Push_back(str.substr(lastPos, pos - lastPos));
        lastPos = pos + delimiter.size();
        pos = str.find(delimiter, lastPos);
    }

    tokens.Push_back(str.substr(lastPos, str.size() - lastPos));
    return tokens;
}
5
Jim Huang

En ce qui concerne les espaces blancs en tant que séparateur, la réponse évidente à utiliser std::istream_iterator<T> est déjà donnée et a reçu beaucoup de votes. Bien entendu, les éléments ne peuvent pas être séparés par des espaces, mais par un séparateur. Je n'ai repéré aucune réponse qui redéfinisse simplement la signification des espaces blancs en tant que séparateur et utilise ensuite l'approche conventionnelle.

Pour changer ce que les flux considèrent comme des espaces, vous devez simplement changer le std::locale du flux en utilisant (std::istream::imbue()) avec une facette std::ctype<char> avec sa propre définition de ce que signifie un espace (cela peut être fait pour std::ctype<wchar_t>, aussi, mais il est en fait légèrement différent std::ctype<char> est piloté par une table alors que std::ctype<wchar_t> est piloté par des fonctions virtuelles).

#include <iostream>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <locale>

struct whitespace_mask {
    std::ctype_base::mask mask_table[std::ctype<char>::table_size];
    whitespace_mask(std::string const& spaces) {
        std::ctype_base::mask* table = this->mask_table;
        std::ctype_base::mask const* tab
            = std::use_facet<std::ctype<char>>(std::locale()).table();
        for (std::size_t i(0); i != std::ctype<char>::table_size; ++i) {
            table[i] = tab[i] & ~std::ctype_base::space;
        }
        std::for_each(spaces.begin(), spaces.end(), [=](unsigned char c) {
            table[c] |= std::ctype_base::space;
        });
    }
};
class whitespace_facet
    : private whitespace_mask
    , public std::ctype<char> {
public:
    whitespace_facet(std::string const& spaces)
        : whitespace_mask(spaces)
        , std::ctype<char>(this->mask_table) {
    }
};

struct whitespace {
    std::string spaces;
    whitespace(std::string const& spaces): spaces(spaces) {}
};
std::istream& operator>>(std::istream& in, whitespace const& ws) {
    std::locale loc(in.getloc(), new whitespace_facet(ws.spaces));
    in.imbue(loc);
    return in;
}
// everything above would probably go into a utility library...

int main() {
    std::istringstream in("a, b, c, d, e");
    std::copy(std::istream_iterator<std::string>(in >> whitespace(", ")),
              std::istream_iterator<std::string>(),
              std::ostream_iterator<std::string>(std::cout, "\n"));

    std::istringstream pipes("a b c|  d |e     e");
    std::copy(std::istream_iterator<std::string>(pipes >> whitespace("|")),
              std::istream_iterator<std::string>(),
              std::ostream_iterator<std::string>(std::cout, "\n"));   
}

La plupart du code est destiné à empaqueter un outil à usage général fournissant des délimiteurs progressifs : plusieurs délimiteurs consécutifs sont fusionnés. Il n'y a aucun moyen de produire une séquence vide. Lorsque différents délimiteurs sont nécessaires dans un flux, vous utiliserez probablement des flux configurés différemment à l'aide d'un tampon de flux partagé:

void f(std::istream& in) {
    std::istream pipes(in.rdbuf());
    pipes >> whitespace("|");
    std::istream comma(in.rdbuf());
    comma >> whitespace(",");

    std::string s0, s1;
    if (pipes >> s0 >> std::ws   // read up to first pipe and ignore sequence of pipes
        && comma >> s1 >> std::ws) { // read up to first comma and ignore commas
        // ...
    }
}
5
Dietmar Kühl

Voici ma solution en utilisant C++ 11 et leSTL. Il devrait être raisonnablement efficace:

#include <vector>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>

std::vector<std::string> split(const std::string& s)
{
    std::vector<std::string> v;

    const auto end = s.end();
    auto to = s.begin();
    decltype(to) from;

    while((from = std::find_if(to, end,
        [](char c){ return !std::isspace(c); })) != end)
    {
        to = std::find_if(from, end, [](char c){ return std::isspace(c); });
        v.emplace_back(from, to);
    }

    return v;
}

int main()
{
    std::string s = "this is the string  to  split";

    auto v = split(s);

    for(auto&& s: v)
        std::cout << s << '\n';
}

Sortie:

this
is
the
string
to
split
4
Galik

Voici ma version prise à la source de Kev:

#include <string>
#include <vector>
void split(vector<string> &result, string str, char delim ) {
  string tmp;
  string::iterator i;
  result.clear();

  for(i = str.begin(); i <= str.end(); ++i) {
    if((const char)*i != delim  && i != str.end()) {
      tmp += *i;
    } else {
      result.Push_back(tmp);
      tmp = "";
    }
  }
}

Ensuite, appelez la fonction et faites quelque chose avec elle:

vector<string> hosts;
split(hosts, "192.168.1.2,192.168.1.3", ',');
for( size_t i = 0; i < hosts.size(); i++){
  cout <<  "Connecting Host : " << hosts.at(i) << "..." << endl;
}

J'utilise le code suivant:

namespace Core
{
    typedef std::wstring String;

    void SplitString(const Core::String& input, const Core::String& splitter, std::list<Core::String>& output)
    {
        if (splitter.empty())
        {
            throw std::invalid_argument(); // for example
        }

        std::list<Core::String> lines;

        Core::String::size_type offset = 0;

        for (;;)
        {
            Core::String::size_type splitterPos = input.find(splitter, offset);

            if (splitterPos != Core::String::npos)
            {
                lines.Push_back(input.substr(offset, splitterPos - offset));
                offset = splitterPos + splitter.size();
            }
            else
            {
                lines.Push_back(input.substr(offset));
                break;
            }
        }

        lines.swap(output);
    }
}

// gtest:

class SplitStringTest: public testing::Test
{
};

TEST_F(SplitStringTest, EmptyStringAndSplitter)
{
    std::list<Core::String> result;
    ASSERT_ANY_THROW(Core::SplitString(Core::String(), Core::String(), result));
}

TEST_F(SplitStringTest, NonEmptyStringAndEmptySplitter)
{
    std::list<Core::String> result;
    ASSERT_ANY_THROW(Core::SplitString(L"xy", Core::String(), result));
}

TEST_F(SplitStringTest, EmptyStringAndNonEmptySplitter)
{
    std::list<Core::String> result;
    Core::SplitString(Core::String(), Core::String(L","), result);
    ASSERT_EQ(1, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
}

TEST_F(SplitStringTest, OneCharSplitter)
{
    std::list<Core::String> result;

    Core::SplitString(L"x,y", L",", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y", *result.rbegin());

    Core::SplitString(L",xy", L",", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(L"xy", *result.rbegin());

    Core::SplitString(L"xy,", L",", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"xy", *result.begin());
    ASSERT_EQ(Core::String(), *result.rbegin());
}

TEST_F(SplitStringTest, TwoCharsSplitter)
{
    std::list<Core::String> result;

    Core::SplitString(L"x,.y,z", L",.", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y,z", *result.rbegin());

    Core::SplitString(L"x,,y,z", L",,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y,z", *result.rbegin());
}

TEST_F(SplitStringTest, RecursiveSplitter)
{
    std::list<Core::String> result;

    Core::SplitString(L",,,", L",,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(L",", *result.rbegin());

    Core::SplitString(L",.,.,", L",.,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(L".,", *result.rbegin());

    Core::SplitString(L"x,.,.,y", L",.,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L".,y", *result.rbegin());

    Core::SplitString(L",.,,.,", L",.,", result);
    ASSERT_EQ(3, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(Core::String(), *(++result.begin()));
    ASSERT_EQ(Core::String(), *result.rbegin());
}

TEST_F(SplitStringTest, NullTerminators)
{
    std::list<Core::String> result;

    Core::SplitString(L"xy", Core::String(L"\0", 1), result);
    ASSERT_EQ(1, result.size());
    ASSERT_EQ(L"xy", *result.begin());

    Core::SplitString(Core::String(L"x\0y", 3), Core::String(L"\0", 1), result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y", *result.rbegin());
}
4
Dmitry

Version rapide qui utilise vector comme classe de base, donnant un accès complet à tous ses opérateurs:

    // Split string into parts.
    class Split : public std::vector<std::string>
    {
        public:
            Split(const std::string& str, char* delimList)
            {
               size_t lastPos = 0;
               size_t pos = str.find_first_of(delimList);

               while (pos != std::string::npos)
               {
                    if (pos != lastPos)
                        Push_back(str.substr(lastPos, pos-lastPos));
                    lastPos = pos + 1;
                    pos = str.find_first_of(delimList, lastPos);
               }
               if (lastPos < str.length())
                   Push_back(str.substr(lastPos, pos-lastPos));
            }
    };

Exemple utilisé pour renseigner un ensemble STL:

std::set<std::string> words;
Split split("Hello,World", ",");
words.insert(split.begin(), split.end());
3
landen

Pas de Boost, pas de flux de chaînes, juste la bibliothèque C standard qui coopère avec les fonctions std::string et std::list: C pour une analyse aisée, des types de données C++ pour une gestion de mémoire facile.

Un espace est considéré comme une combinaison de nouvelles lignes, d'onglets et d'espaces. L'ensemble des caractères d'espacement est établi par la variable wschars.

#include <string>
#include <list>
#include <iostream>
#include <cstring>

using namespace std;

const char *wschars = "\t\n ";

list<string> split(const string &str)
{
  const char *cstr = str.c_str();
  list<string> out;

  while (*cstr) {                     // while remaining string not empty
    size_t toklen;
    cstr += strspn(cstr, wschars);    // skip leading whitespace
    toklen = strcspn(cstr, wschars);  // figure out token length
    if (toklen)                       // if we have a token, add to list
      out.Push_back(string(cstr, toklen));
    cstr += toklen;                   // skip over token
  }

  // ran out of string; return list

  return out;
}

int main(int argc, char **argv)
{
  list<string> li = split(argv[1]);
  for (list<string>::iterator i = li.begin(); i != li.end(); i++)
    cout << "{" << *i << "}" << endl;
  return 0;
}

Courir:

$ ./split ""
$ ./split "a"
{a}
$ ./split " a "
{a}
$ ./split " a b"
{a}
{b}
$ ./split " a b c"
{a}
{b}
{c}
$ ./split " a b c d  "
{a}
{b}
{c}
{d}

Version queue-récursive de split (elle-même divisée en deux fonctions). Toute manipulation destructive de variables a disparu, à l'exception de l'insertion de chaînes dans la liste!

void split_rec(const char *cstr, list<string> &li)
{
  if (*cstr) {
    const size_t leadsp = strspn(cstr, wschars);
    const size_t toklen = strcspn(cstr + leadsp, wschars);

    if (toklen)
      li.Push_back(string(cstr + leadsp, toklen));

    split_rec(cstr + leadsp + toklen, li);
  }
}

list<string> split(const string &str)
{
  list<string> out;
  split_rec(str.c_str(), out);
  return out;
}
2
Kaz

Oui, j'ai parcouru les 30 exemples.

Je ne pouvais pas trouver une version de split qui fonctionne pour les délimiteurs multi-caractères, alors voici le mien: 

#include <string>
#include <vector>

using namespace std;

vector<string> split(const string &str, const string &delim)
{   
    const auto delim_pos = str.find(delim);

    if (delim_pos == string::npos)
        return {str};

    vector<string> ret{str.substr(0, delim_pos)};
    auto tail = split(str.substr(delim_pos + delim.size(), string::npos), delim);

    ret.insert(ret.end(), tail.begin(), tail.end());

    return ret;
}

Ce n’est probablement pas la meilleure des implémentations, mais c’est une solution récursive très simple, utilisant uniquement <string> et <vector>.

Ah, c'est écrit en C++ 11, mais ce code n'a rien de spécial, vous pouvez donc facilement l'adapter au C++ 98.

2
Romário

LazyStringSplitter:

#include <string>
#include <algorithm>
#include <unordered_set>

using namespace std;

class LazyStringSplitter
{
    string::const_iterator start, finish;
    unordered_set<char> chop;

public:

    // Empty Constructor
    explicit LazyStringSplitter()
    {}

    explicit LazyStringSplitter (const string cstr, const string delims)
        : start(cstr.begin())
        , finish(cstr.end())
        , chop(delims.begin(), delims.end())
    {}

    void operator () (const string cstr, const string delims)
    {
        chop.insert(delims.begin(), delims.end());
        start = cstr.begin();
        finish = cstr.end();
    }

    bool empty() const { return (start >= finish); }

    string next()
    {
        // return empty string
        // if ran out of characters
        if (empty())
            return string("");

        auto runner = find_if(start, finish, [&](char c) {
            return chop.count(c) == 1;
        });

        // construct next string
        string ret(start, runner);
        start = runner + 1;

        // Never return empty string
        // + tail recursion makes this method efficient
        return !ret.empty() ? ret : next();
    }
};
  • J'appelle cette méthode la LazyStringSplitter pour une raison - Elle ne divise pas la chaîne en une fois.
  • En substance, il se comporte comme un générateur de python
  • Il expose une méthode appelée next qui retourne la chaîne suivante qui est séparée de la chaîne d'origine.
  • J'ai utilisé le unordered_set de c ++ 11 STL, de sorte que la recherche des délimiteurs est beaucoup plus rapide
  • Et voici comment ça marche

PROGRAMME D'ESSAI

#include <iostream>
using namespace std;

int main()
{
    LazyStringSplitter splitter;

    // split at the characters ' ', '!', '.', ','
    splitter("This, is a string. And here is another string! Let's test and see how well this does.", " !.,");

    while (!splitter.empty())
        cout << splitter.next() << endl;
    return 0;
}

SORTIE

This
is
a
string
And
here
is
another
string
Let's
test
and
see
how
well
this
does

Le plan suivant pour améliorer ceci consiste à implémenter les méthodes begin et end afin que l’on puisse faire quelque chose comme:

vector<string> split_string(splitter.begin(), splitter.end());
2
smac89

Pour ceux qui ont besoin d'alternatives pour scinder une chaîne avec un séparateur de chaîne, vous pouvez peut-être essayer ma solution suivante.

std::vector<size_t> str_pos(const std::string &search, const std::string &target)
{
    std::vector<size_t> founds;

    if(!search.empty())
    {
        size_t start_pos = 0;

        while (true)
        {
            size_t found_pos = target.find(search, start_pos);

            if(found_pos != std::string::npos)
            {
                size_t found = found_pos;

                founds.Push_back(found);

                start_pos = (found_pos + 1);
            }
            else
            {
                break;
            }
        }
    }

    return founds;
}

std::string str_sub_index(size_t begin_index, size_t end_index, const std::string &target)
{
    std::string sub;

    size_t size = target.length();

    const char* copy = target.c_str();

    for(size_t i = begin_index; i <= end_index; i++)
    {
        if(i >= size)
        {
            break;
        }
        else
        {
            char c = copy[i];

            sub += c;
        }
    }

    return sub;
}

std::vector<std::string> str_split(const std::string &delimiter, const std::string &target)
{
    std::vector<std::string> splits;

    if(!delimiter.empty())
    {
        std::vector<size_t> founds = str_pos(delimiter, target);

        size_t founds_size = founds.size();

        if(founds_size > 0)
        {
            size_t search_len = delimiter.length();

            size_t begin_index = 0;

            for(int i = 0; i <= founds_size; i++)
            {
                std::string sub;

                if(i != founds_size)
                {
                    size_t pos  = founds.at(i);

                    sub = str_sub_index(begin_index, pos - 1, target);

                    begin_index = (pos + search_len);
                }
                else
                {
                    sub = str_sub_index(begin_index, (target.length() - 1), target);
                }

                splits.Push_back(sub);
            }
        }
    }

    return splits;
}

Ces extraits se composent de 3 fonctions. La mauvaise nouvelle est d'utiliser la fonction str_split, vous aurez besoin des deux autres fonctions. Oui, c'est un énorme morceau de code. Mais la bonne nouvelle est que ces deux fonctions supplémentaires peuvent fonctionner de manière indépendante et peuvent parfois être utiles aussi. :)

Testé la fonction dans le bloc main() comme ceci:

int main()
{
    std::string s = "Hello, world! We need to make the world a better place. Because your world is also my world, and our children's world.";

    std::vector<std::string> split = str_split("world", s);

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

Et cela produirait:

Hello, 
! We need to make the 
 a better place. Because your 
 is also my 
, and our children's 
.

Je crois que ce n'est pas le code le plus efficace, mais au moins ça marche. J'espère que ça aide.

2
yunhasnawa

J'utilise le suivant

void split(string in, vector<string>& parts, char separator) {
    string::iterator  ts, curr;
    ts = curr = in.begin();
    for(; curr <= in.end(); curr++ ) {
        if( (curr == in.end() || *curr == separator) && curr > ts )
               parts.Push_back( string( ts, curr ));
        if( curr == in.end() )
               break;
        if( *curr == separator ) ts = curr + 1; 
    }
}

PlasmaHH, j'ai oublié d'inclure le contrôle supplémentaire (curr> ts) permettant de supprimer les jetons avec des espaces. 

2
ManiP

Ceci est ma solution à ce problème:

vector<string> get_tokens(string str) {
    vector<string> dt;
    stringstream ss;
    string tmp; 
    ss << str;
    for (size_t i; !ss.eof(); ++i) {
        ss >> tmp;
        dt.Push_back(tmp);
    }
    return dt;
}

Cette fonction renvoie un vecteur de chaînes.

2
pz64_

Voici ma version

#include <vector>

inline std::vector<std::string> Split(const std::string &str, const std::string &delim = " ")
{
    std::vector<std::string> tokens;
    if (str.size() > 0)
    {
        if (delim.size() > 0)
        {
            std::string::size_type currPos = 0, prevPos = 0;
            while ((currPos = str.find(delim, prevPos)) != std::string::npos)
            {
                std::string item = str.substr(prevPos, currPos - prevPos);
                if (item.size() > 0)
                {
                    tokens.Push_back(item);
                }
                prevPos = currPos + 1;
            }
            tokens.Push_back(str.substr(prevPos));
        }
        else
        {
            tokens.Push_back(str);
        }
    }
    return tokens;
}

Cela fonctionne avec des délimiteurs multi-caractères. Il empêche les jetons vides d'entrer dans vos résultats. Il utilise un seul en-tête. Il renvoie la chaîne sous la forme d'un seul jeton lorsque vous ne fournissez aucun délimiteur. Il renvoie également un résultat vide si la chaîne est vide. Il est malheureusement inefficace à cause de l’énorme std::vector copySAUF SIvous compilez avec C++ 11, qui devrait utiliser le schéma de déplacement. En C++ 11, ce code devrait être rapide.

2
mchiasson

Je cherchais un moyen de diviser une chaîne par un séparateur de n'importe quelle longueur. J'ai donc commencé à l'écrire à partir de zéro, car les solutions existantes ne me convenaient pas.

Voici mon petit algorithme, n'utilisant que STL:

//use like this
//std::vector<std::wstring> vec = Split<std::wstring> (L"Hello##world##!", L"##");

template <typename valueType>
static std::vector <valueType> Split (valueType text, const valueType& delimiter)
{
    std::vector <valueType> tokens;
    size_t pos = 0;
    valueType token;

    while ((pos = text.find(delimiter)) != valueType::npos) 
    {
        token = text.substr(0, pos);
        tokens.Push_back (token);
        text.erase(0, pos + delimiter.length());
    }
    tokens.Push_back (text);

    return tokens;
}

Il peut être utilisé avec des séparateurs de n'importe quelle longueur et forme, dans la mesure où j'ai déjà testé .. .. Instanciez-vous avec le type string ou wstring.

Tout l’algorithme fait qu’il recherche le délimiteur, récupère la partie de la chaîne jusqu’au délimiteur, supprime le délimiteur et effectue une nouvelle recherche jusqu’à ce qu’il ne la trouve plus.

Bien entendu, vous pouvez utiliser un nombre illimité d'espaces pour le délimiteur.

J'espère que ça aide.

2
robcsi

C’est une fonction que j’ai écrite et qui m’aide beaucoup à faire. Cela m’a aidé lors de la rédaction du protocole pour WebSockets

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

vector<string> split ( string input , string split_id ) {
  vector<string> result;
  int i = 0;
  bool add;
  string temp;
  stringstream ss;
  size_t found;
  string real;
  int r = 0;
    while ( i != input.length() ) {
        add = false;
        ss << input.at(i);
        temp = ss.str();
        found = temp.find(split_id);
        if ( found != string::npos ) {
            add = true;
            real.append ( temp , 0 , found );
        } else if ( r > 0 &&  ( i+1 ) == input.length() ) {
            add = true;
            real.append ( temp , 0 , found );
        }
        if ( add ) {
            result.Push_back(real);
            ss.str(string());
            ss.clear();
            temp.clear();
            real.clear();
            r = 0;
        }
        i++;
        r++;
    }
  return result;
}

int main() {
    string s = "S,o,m,e,w,h,e,r,e, down the road \n In a really big C++ house.  \n  Lives a little old lady.   \n   That no one ever knew.    \n    She comes outside.     \n     In the very hot Sun.      \n\n\n\n\n\n\n\n   And throws C++ at us.    \n    The End.  FIN.";
    vector < string > Token;
    Token = split ( s , "," );
    for ( int i = 0 ; i < Token.size(); i++)    cout << Token.at(i) << endl;
    cout << endl << Token.size();
    int a;
    cin >> a;
    return a;
}
2
User

Voici mon entrée:

template <typename Container, typename InputIter, typename ForwardIter>
Container
split(InputIter first, InputIter last,
      ForwardIter s_first, ForwardIter s_last)
{
    Container output;

    while (true) {
        auto pos = std::find_first_of(first, last, s_first, s_last);
        output.emplace_back(first, pos);
        if (pos == last) {
            break;
        }

        first = ++pos;
    }

    return output;
}

template <typename Output = std::vector<std::string>,
          typename Input = std::string,
          typename Delims = std::string>
Output
split(const Input& input, const Delims& delims = " ")
{
    using std::cbegin;
    using std::cend;
    return split<Output>(cbegin(input), cend(input),
                         cbegin(delims), cend(delims));
}

auto vec = split("Mary had a little lamb");

La première définition est une fonction générique de style STL prenant deux paires d’itérateurs. La seconde est une fonction pratique qui vous évite d’avoir à faire vous-même toutes les begin()s et end()s. Vous pouvez également spécifier le type de conteneur de sortie en tant que paramètre de modèle si vous souhaitez utiliser une variable list, par exemple.

Ce qui le rend élégant (IMO), c'est que contrairement à la plupart des autres réponses, cela ne se limite pas aux chaînes, mais fonctionne avec n'importe quel conteneur compatible STL. Sans aucune modification du code ci-dessus, vous pouvez dire:

using vec_of_vecs_t = std::vector<std::vector<int>>;

std::vector<int> v{1, 2, 0, 3, 4, 5, 0, 7, 8, 0, 9};
auto r = split<vec_of_vecs_t>(v, std::initializer_list<int>{0, 2});

qui divisera le vecteur v en vecteurs séparés chaque fois qu'un 0 ou un 2 est rencontré.

(Il existe également un avantage supplémentaire: avec les chaînes, cette implémentation est plus rapide que les versions basées sur strtok() et getline(), du moins sur mon système.)

1
Tristan Brindle

Juste pour plus de commodité:

template<class V, typename T>
bool in(const V &v, const T &el) {
    return std::find(v.begin(), v.end(), el) != v.end();
}

Le fractionnement réel basé sur plusieurs délimiteurs:

std::vector<std::string> split(const std::string &s,
                               const std::vector<char> &delims) {
    std::vector<std::string> res;
    auto stuff = [&delims](char c) { return !in(delims, c); };
    auto space = [&delims](char c) { return in(delims, c); };
    auto first = std::find_if(s.begin(), s.end(), stuff);
    while (first != s.end()) {
        auto last = std::find_if(first, s.end(), space);
        res.Push_back(std::string(first, last));
        first = std::find_if(last + 1, s.end(), stuff);
    }
    return res;
}

L'usage:

int main() {
    std::string s = "   aaa,  bb  cc ";
    for (auto el: split(s, {' ', ','}))
        std::cout << el << std::endl;
    return 0;
}
1
AlwaysLearning
// adapted from a "regular" csv parse
std::string stringIn = "my csv  is 10233478 NOTseparated by commas";
std::vector<std::string> commaSeparated(1);
int commaCounter = 0;
for (int i=0; i<stringIn.size(); i++) {
    if (stringIn[i] == " ") {
        commaSeparated.Push_back("");
        commaCounter++;
    } else {
        commaSeparated.at(commaCounter) += stringIn[i];
    }
}

à la fin, vous aurez un vecteur de chaînes avec chaque élément de la phrase séparé par des espaces. std :: vector est la seule ressource non standard (mais comme il s'agit d'un std :: string, je me suis dit que ce serait acceptable).

les chaînes vides sont enregistrées séparément.

1
tony gil

Je crois que personne n'a encore posté cette solution. Plutôt que d’utiliser directement les délimiteurs, elle fait la même chose que boost :: split (), c’est-à-dire qu’elle vous permet de transmettre un prédicat qui renvoie true si un caractère est un délimiteur et false sinon. Je pense que cela donne beaucoup plus de contrôle au programmeur, et l’avantage est que vous n’avez pas besoin de boost.

template <class Container, class String, class Predicate>
void split(Container& output, const String& input,
           const Predicate& pred, bool trimEmpty = false) {
    auto it = begin(input);
    auto itLast = it;
    while (it = find_if(it, end(input), pred), it != end(input)) {
        if (not (trimEmpty and it == itLast)) {
            output.emplace_back(itLast, it);
        }
        ++it;
        itLast = it;
    }
}

Ensuite, vous pouvez l'utiliser comme ceci:

struct Delim {
    bool operator()(char c) {
        return not isalpha(c);
    }
};    

int main() {
    string s("#include<iostream>\n"
             "int main() { std::cout << \"Hello world!\" << std::endl; }");

    vector<string> v;

    split(v, s, Delim(), true);
    /* Which is also the same as */
    split(v, s, [](char c) { return not isalpha(c); }, true);

    for (const auto& i : v) {
        cout << i << endl;
    }
}
1
LLLL

Mon code est:

#include <list>
#include <string>
template<class StringType = std::string, class ContainerType = std::list<StringType> >
class DSplitString:public ContainerType
{
public:
    explicit DSplitString(const StringType& strString, char cChar, bool bSkipEmptyParts = true)
    {
        size_t iPos = 0;
        size_t iPos_char = 0;
        while(StringType::npos != (iPos_char = strString.find(cChar, iPos)))
        {
            StringType strTemp = strString.substr(iPos, iPos_char - iPos);
            if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
                Push_back(strTemp);
            iPos = iPos_char + 1;
        }
    }
    explicit DSplitString(const StringType& strString, const StringType& strSub, bool bSkipEmptyParts = true)
    {
        size_t iPos = 0;
        size_t iPos_char = 0;
        while(StringType::npos != (iPos_char = strString.find(strSub, iPos)))
        {
            StringType strTemp = strString.substr(iPos, iPos_char - iPos);
            if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
                Push_back(strTemp);
            iPos = iPos_char + strSub.length();
        }
    }
};

Exemple:

#include <iostream>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
    DSplitString<> aa("doicanhden1;doicanhden2;doicanhden3;", ';');
    for each (std::string var in aa)
    {
        std::cout << var << std::endl;
    }
    std::cin.get();
    return 0;
}
1
doicanhden

Nous pouvons utiliser strtok en c ++,

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    char str[]="Mickey M;12034;911416313;M;01a;9001;NULL;0;13;12;0;CPP,C;MSC,3D;FEND,BEND,SEC;";
    char *pch = strtok (str,";,");
    while (pch != NULL)
    {
        cout<<pch<<"\n";
        pch = strtok (NULL, ";,");
    }
    return 0;
}
1
Venkata Naidu M
#include <iostream>
#include <vector>
using namespace std;

int main() {
  string str = "ABC AABCD CDDD RABC GHTTYU FR";
  str += " "; //dirty hack: adding extra space to the end
  vector<string> v;

  for (int i=0; i<(int)str.size(); i++) {
    int a, b;
    a = i;

    for (int j=i; j<(int)str.size(); j++) {
      if (str[j] == ' ') {
        b = j;
        i = j;
        break;
      }
    }
    v.Push_back(str.substr(a, b-a));
  }

  for (int i=0; i<v.size(); i++) {
    cout<<v[i].size()<<" "<<v[i]<<endl;
  }
  return 0;
}
1
torayeff

Boucle sur getline avec '' comme jeton.

1
lemic

Voici mon point de vue sur ceci. Je devais traiter la chaîne de saisie mot par mot, ce qui aurait pu être fait en utilisant l'espace pour compter les mots, mais je pensais que ce serait fastidieux et que je devrais scinder les mots en vecteurs. 

#include<iostream>
#include<vector>
#include<string>
#include<stdio.h>
using namespace std;
int main()
{
    char x = '\0';
    string s = "";
    vector<string> q;
    x = getchar();
    while(x != '\n')
    {
        if(x == ' ')
        {
            q.Push_back(s);
            s = "";
            x = getchar();
            continue;
        }
        s = s + x;
        x = getchar();
    }
    q.Push_back(s);
    for(int i = 0; i<q.size(); i++)
        cout<<q[i]<<" ";
    return 0;
}
  1. Ne prend pas soin de plusieurs espaces.
  2. Si le dernier mot n'est pas immédiatement suivi du caractère de nouvelle ligne, il inclut l'espace entre le dernier caractère de Word et le caractère de nouvelle ligne.
1
Saksham Sharma

Je viens d'écrire un bel exemple de la division d'un caractère par symbole, qui place ensuite chaque tableau de caractères (mots séparés par votre symbole) en un vecteur. Pour plus de simplicité, j’ai créé le type vectoriel de chaîne std.

J'espère que cela vous aide et qu'il est lisible.

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

void Push(std::vector<std::string> &WORDS, std::string &TMP){
    WORDS.Push_back(TMP);
    TMP = "";
}
std::vector<std::string> mySplit(char STRING[]){
        std::vector<std::string> words;
        std::string s;
        for(unsigned short i = 0; i < strlen(STRING); i++){
            if(STRING[i] != ' '){
                s += STRING[i];
            }else{
                Push(words, s);
            }
        }
        Push(words, s);//Used to get last split
        return words;
}

int main(){
    char string[] = "My awesome string.";
    std::cout << mySplit(string)[2];
    std::cin.get();
    return 0;
}
1
user2588062

Basé sur la réponse de Galik J'ai écrit ceci. C’est la plupart du temps ici, donc je n’ai pas à continuer à écrire encore et encore C'est fou que C++ n'ait toujours pas de fonction split native. Caractéristiques:

  • Devrait être très rapide.
  • Facile à comprendre (je pense).
  • Fusionne les sections vides.
  • Trivial pour utiliser plusieurs délimiteurs (par exemple, "\r\n")
#include <string>
#include <vector>
#include <algorithm>

std::vector<std::string> split(const std::string& s, const std::string& delims)
{
    using namespace std;

    vector<string> v;

    // Start of an element.
    size_t elemStart = 0;

    // We start searching from the end of the previous element, which
    // initially is the start of the string.
    size_t elemEnd = 0;

    // Find the first non-delim, i.e. the start of an element, after the end of the previous element.
    while((elemStart = s.find_first_not_of(delims, elemEnd)) != string::npos)
    {
        // Find the first delem, i.e. the end of the element (or if this fails it is the end of the string).
        elemEnd = s.find_first_of(delims, elemStart);
        // Add it.
        v.emplace_back(s, elemStart, elemEnd == string::npos ? string::npos : elemEnd - elemStart);
    }
    // When there are no more non-spaces, we are done.

    return v;
}
1
Timmmm

Utilisation de std::string_view et de la bibliothèque range-v3 d'Eric Niebler:

https://wandbox.org/permlink/kW5lwRCL1pxjp2pW

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"
#include "range/v3/algorithm.hpp"

int main() {
    std::string s = "Somewhere down the range v3 library";
    ranges::for_each(s  
        |   ranges::view::split(' ')
        |   ranges::view::transform([](auto &&sub) {
                return std::string_view(&*sub.begin(), ranges::distance(sub));
            }),
        [](auto s) {std::cout << "Substring: " << s << "\n";}
    );
}
1
Porsche9II

J'ai une approche très différente des autres solutions qui offre beaucoup de valeur car les autres solutions font diversement défaut, mais a bien sûr aussi ses propres inconvénients. Ici est l'implémentation de travail, avec l'exemple de mettre <tag></tag> autour des mots.

Pour commencer, ce problème peut être résolu avec une boucle, sans mémoire supplémentaire et en considérant simplement quatre cas logiques. Conceptuellement, nous nous intéressons aux limites. Notre code devrait refléter cela: parcourons la chaîne et examinons deux caractères à la fois, en gardant à l'esprit que nous avons des cas particuliers au début et à la fin de la chaîne.

L'inconvénient est que nous devons écrire l'implémentation, qui est un peu verbeuse, mais surtout commode.

L’avantage, c’est que nous avons écrit l’implémentation. Il est donc très facile de l’adapter à des besoins spécifiques, tels que la distinction des limites gauche et écriture, l’utilisation d’un ensemble de délimiteurs ou la gestion d’autres cas, tels que des positions erronées ou non.

using namespace std;

#include <iostream>
#include <string>

#include <cctype>

typedef enum boundary_type_e {
    E_BOUNDARY_TYPE_ERROR = -1,
    E_BOUNDARY_TYPE_NONE,
    E_BOUNDARY_TYPE_LEFT,
    E_BOUNDARY_TYPE_RIGHT,
} boundary_type_t;

typedef struct boundary_s {
    boundary_type_t type;
    int pos;
} boundary_t;

bool is_delim_char(int c) {
    return isspace(c); // also compare against any other chars you want to use as delimiters
}

bool is_Word_char(int c) {
    return ' ' <= c && c <= '~' && !is_delim_char(c);
}

boundary_t maybe_Word_boundary(string str, int pos) {
    int len = str.length();
    if (pos < 0 || pos >= len) {
        return (boundary_t){.type = E_BOUNDARY_TYPE_ERROR};
    } else {
        if (pos == 0 && is_Word_char(str[pos])) {
            // if the first character is Word-y, we have a left boundary at the beginning
            return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos};
        } else if (pos == len - 1 && is_Word_char(str[pos])) {
            // if the last character is Word-y, we have a right boundary left of the null terminator
            return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
        } else if (!is_Word_char(str[pos]) && is_Word_char(str[pos + 1])) {
            // if we have a delimiter followed by a Word char, we have a left boundary left of the Word char
            return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos + 1};
        } else if (is_Word_char(str[pos]) && !is_Word_char(str[pos + 1])) {
            // if we have a Word char followed by a delimiter, we have a right boundary right of the Word char
            return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
        }
        return (boundary_t){.type = E_BOUNDARY_TYPE_NONE};
    }
}

int main() {
    string str;
    getline(cin, str);

    int len = str.length();
    for (int i = 0; i < len; i++) {
        boundary_t boundary = maybe_Word_boundary(str, i);
        if (boundary.type == E_BOUNDARY_TYPE_LEFT) {
            // whatever
        } else if (boundary.type == E_BOUNDARY_TYPE_RIGHT) {
            // whatever
        }
    }
}

Comme vous pouvez le constater, le code est très simple à comprendre et à ajuster, et son utilisation réelle est très courte et simple. L'utilisation de C++ ne devrait pas nous empêcher d'écrire le code le plus simple et le plus facilement personnalisable possible, même si cela implique de ne pas utiliser la STL. Je pense que ceci est un exemple de ce que Linus Torvalds pourrait appeler "goût" , puisque nous avons éliminé toute la logique dont nous n’avions pas besoin en écrivant dans un style qui permet naturellement de traiter davantage d’affaires quand et si besoin de les gérer survient.

Ce qui pourrait améliorer ce code pourrait être l’utilisation de enum class, en acceptant un pointeur de fonction vers is_Word_char dans maybe_Word_boundary au lieu d’appeler is_Word_char directement et en passant un lambda.

0
okovko

Merci @Jairo Abdiel Toribio Cisneros. Cela fonctionne pour moi, mais votre fonction renvoie un élément vide. Donc pour le retour sans vide, j'ai édité avec ce qui suit:

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

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

    return v;
}

En utilisant:

std::string s = "one:two::three";
std::string delim = ":";
std::vector<std::string> vv = split(s, delim.c_str());
0
Kakashi

mon implémentation générale pour string et u32string ~, en utilisant la signature boost::algorithm::split.

template<typename CharT, typename UnaryPredicate>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           UnaryPredicate predicate)
{
    using ST = std::basic_string<CharT>;
    using std::swap;
    std::vector<ST> tmp_result;
    auto iter = s.cbegin(),
         end_iter = s.cend();
    while (true)
    {
        /**
         * Edge case: empty str -> Push an empty str and exit.
         */
        auto find_iter = find_if(iter, end_iter, predicate);
        tmp_result.emplace_back(iter, find_iter);
        if (find_iter == end_iter) { break; }
        iter = ++find_iter; 
    }
    swap(tmp_result, split_result);
}


template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           const std::basic_string<CharT>& char_candidate)
{
    std::unordered_set<CharT> candidate_set(char_candidate.cbegin(),
                                            char_candidate.cend());
    auto predicate = [&candidate_set](const CharT& c) {
        return candidate_set.count(c) > 0U;
    };
    return split(split_result, s, predicate);
}

template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           const CharT* literals)
{
    return split(split_result, s, std::basic_string<CharT>(literals));
}
0
小文件
#include <iostream>
#include <string>
#include <deque>

std::deque<std::string> split(
    const std::string& line, 
    std::string::value_type delimiter,
    bool skipEmpty = false
) {
    std::deque<std::string> parts{};

    if (!skipEmpty && !line.empty() && delimiter == line.at(0)) {
        parts.Push_back({});
    }

    for (const std::string::value_type& c : line) {
        if (
            (
                c == delimiter 
                &&
                (skipEmpty ? (!parts.empty() && !parts.back().empty()) : true)
            )
            ||
            (c != delimiter && parts.empty())
        ) {
            parts.Push_back({});
        }

        if (c != delimiter) {
            parts.back().Push_back(c);
        }
    }

    if (skipEmpty && !parts.empty() && parts.back().empty()) {
        parts.pop_back();
    }

    return parts;
}

void test(const std::string& line) {
    std::cout << line << std::endl;

    std::cout << "skipEmpty=0 |";
    for (const std::string& part : split(line, ':')) {
        std::cout << part << '|';
    }
    std::cout << std::endl;

    std::cout << "skipEmpty=1 |";
    for (const std::string& part : split(line, ':', true)) {
        std::cout << part << '|';
    }
    std::cout << std::endl;

    std::cout << std::endl;
}

int main() {
    test("foo:bar:::baz");
    test("");
    test("foo");
    test(":");
    test("::");
    test(":foo");
    test("::foo");
    test(":foo:");
    test(":foo::");

    return 0;
}

Sortie:

foo:bar:::baz
skipEmpty=0 |foo|bar|||baz|
skipEmpty=1 |foo|bar|baz|


skipEmpty=0 |
skipEmpty=1 |

foo
skipEmpty=0 |foo|
skipEmpty=1 |foo|

:
skipEmpty=0 |||
skipEmpty=1 |

::
skipEmpty=0 ||||
skipEmpty=1 |

:foo
skipEmpty=0 ||foo|
skipEmpty=1 |foo|

::foo
skipEmpty=0 |||foo|
skipEmpty=1 |foo|

:foo:
skipEmpty=0 ||foo||
skipEmpty=1 |foo|

:foo::
skipEmpty=0 ||foo|||
skipEmpty=1 |foo|
0
Oleg

si vous voulez diviser la chaîne par quelques caractères, vous pouvez utiliser

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

using namespace std;
void replaceOtherChars(string &input, vector<char> &dividers)
{
    const char divider = dividers.at(0);
    int replaceIndex = 0;
    vector<char>::iterator it_begin = dividers.begin()+1,
        it_end= dividers.end();
    for(;it_begin!=it_end;++it_begin)
    {
        replaceIndex = 0;
        while(true)
        {
            replaceIndex=input.find_first_of(*it_begin,replaceIndex);
            if(replaceIndex==-1)
                break;
            input.at(replaceIndex)=divider;
        }
    }
}
vector<string> split(string str, vector<char> chars, bool missEmptySpace =true )
{
    vector<string> result;
    const char divider = chars.at(0);
    replaceOtherChars(str,chars);
    stringstream stream;
    stream<<str;    
    string temp;
    while(getline(stream,temp,divider))
    {
        if(missEmptySpace && temp.empty())
            continue;
        result.Push_back(temp);
    }
    return result;
}
int main()
{
    string str ="milk, pigs.... hot-dogs ";
    vector<char> arr;
    arr.Push_back(' '); arr.Push_back(','); arr.Push_back('.');
    vector<string> result = split(str,arr);
    vector<string>::iterator it_begin= result.begin(),
        it_end= result.end();
    for(;it_begin!=it_end;++it_begin)
    {
        cout<<*it_begin<<endl;
    }
return 0;
}
0
h.o.m.a.n

Mon implémentation peut être une solution alternative:

std::vector<std::wstring> SplitString(const std::wstring & String, const std::wstring & Seperator)
{
    std::vector<std::wstring> Lines;
    size_t stSearchPos = 0;
    size_t stFoundPos;
    while (stSearchPos < String.size() - 1)
    {
        stFoundPos = String.find(Seperator, stSearchPos);
        stFoundPos = (stFoundPos == std::string::npos) ? String.size() : stFoundPos;
        Lines.Push_back(String.substr(stSearchPos, stFoundPos - stSearchPos));
        stSearchPos = stFoundPos + Seperator.size();
    }
    return Lines;
}

Code de test:

std::wstring MyString(L"Part 1SEPsecond partSEPlast partSEPend");
std::vector<std::wstring> Parts = IniFile::SplitString(MyString, L"SEP");
std::wcout << L"The string: " << MyString << std::endl;
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
    std::wcout << *it << L"<---" << std::endl;
}
std::wcout << std::endl;
MyString = L"this,time,a,comma separated,string";
std::wcout << L"The string: " << MyString << std::endl;
Parts = IniFile::SplitString(MyString, L",");
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
    std::wcout << *it << L"<---" << std::endl;
}

Sortie du code de test:

The string: Part 1SEPsecond partSEPlast partSEPend
Part 1<---
second part<---
last part<---
end<---

The string: this,time,a,comma separated,string
this<---
time<---
a<---
comma separated<---
string<---
0
hkBattousai

C'est une extension de l'une des premières réponses. Il supporte maintenant la définition d'un nombre maximum d'éléments retournés, N. Le dernier bit de la chaîne se retrouvera dans le Nième élément. Le paramètre MAXELEMENTS est facultatif. Si la valeur par défaut est 0, il renverra un nombre d'éléments unlimited. :-)

.h:

class Myneatclass {
public:
    static std::vector<std::string>& split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS = 0);
    static std::vector<std::string> split(const std::string &s, char delim, const size_t MAXELEMENTS = 0);
};

.cpp:

std::vector<std::string>& Myneatclass::split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.Push_back(item);
        if (MAXELEMENTS > 0 && !ss.eof() && elems.size() + 1 >= MAXELEMENTS) {
            std::getline(ss, item);
            elems.Push_back(item);
            break;
        }
    }
    return elems;
}
std::vector<std::string> Myneatclass::split(const std::string &s, char delim, const size_t MAXELEMENTS) {
    std::vector<std::string> elems;
    split(s, delim, elems, MAXELEMENTS);
    return elems;
}
0
Jonny

je suis au courant de la soirée, mais je réfléchissais à la façon la plus élégante de le faire si on vous donnait une gamme de délimiteurs plutôt que des espaces, sans utiliser plus que la bibliothèque standard.

Voici mes pensées:

Pour diviser des mots en un vecteur de chaîne par une séquence de délimiteurs:

template<class Container>
std::vector<std::string> split_by_delimiters(const std::string& input, const Container& delimiters)
{
    std::vector<std::string> result;

    for (auto current = begin(input) ; current != end(input) ; )
    {
        auto first = find_if(current, end(input), not_in(delimiters));
        if (first == end(input)) break;
        auto last = find_if(first, end(input), is_in(delimiters));
        result.emplace_back(first, last);
        current = last;
    }
    return result;
}

diviser l’autre en fournissant une séquence de caractères valides:

template<class Container>
std::vector<std::string> split_by_valid_chars(const std::string& input, const Container& valid_chars)
{
    std::vector<std::string> result;

    for (auto current = begin(input) ; current != end(input) ; )
    {
        auto first = find_if(current, end(input), is_in(valid_chars));
        if (first == end(input)) break;
        auto last = find_if(first, end(input), not_in(valid_chars));
        result.emplace_back(first, last);
        current = last;
    }
    return result;
}

is_in et not_in sont définis ainsi:

namespace detail {
    template<class Container>
    struct is_in {
        is_in(const Container& charset)
        : _charset(charset)
        {}

        bool operator()(char c) const
        {
            return find(begin(_charset), end(_charset), c) != end(_charset);
        }

        const Container& _charset;
    };

    template<class Container>
    struct not_in {
        not_in(const Container& charset)
        : _charset(charset)
        {}

        bool operator()(char c) const
        {
            return find(begin(_charset), end(_charset), c) == end(_charset);
        }

        const Container& _charset;
    };

}

template<class Container>
detail::not_in<Container> not_in(const Container& c)
{
    return detail::not_in<Container>(c);
}

template<class Container>
detail::is_in<Container> is_in(const Container& c)
{
    return detail::is_in<Container>(c);
}
0
Richard Hodges