Comment puis-je itérer les mots d'une chaîne? [fermé]

j'essaie d'itérer 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 C string ou ce genre de manipulation de caractères/accès. Aussi, merci de donner la priorité à l'élégance plus d'efficacité dans votre réponse.

la meilleure solution que j'ai maintenant est:

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

est - il un plus élégant façon de le faire?

2679
demandé sur Ashwin Nanjappa 2008-10-25 12:58:21

30 réponses

pour ce que ça vaut, voici une autre façon d'extraire des tokens à partir d'une chaîne de caractères, en se basant uniquement sur les facilités standard de la bibliothèque. C'est un exemple de la puissance et de l'élégance derrière le design du 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 tokens extraits dans un flux de sortie, on pourrait 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éer le vector directement:

vector<string> tokens{istream_iterator<string>{iss},
                      istream_iterator<string>{}};
1212
répondu Zunino 2016-06-09 17:47:05

j'utilise ceci pour séparer la chaîne par un délimiteur. La première met les résultats dans un pré-construit vecteur, 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 ne saute pas les jetons vides, de sorte que ce qui suit trouvera 4 articles, dont l'un est vide:

std::vector<std::string> x = split("one:two::three", ':');
2325
répondu Evan Teran 2018-02-28 23:32:54

une solution possible 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 pourrait être encore plus rapide que l'approche stringstream . Et comme il s'agit d'une fonction générique de template, elle peut être utilisée pour séparer d'autres types de chaînes (wchar, etc. ou UTF-8) en utilisant toutes sortes de délimiteurs.

voir la documentation pour plus de détails.

800
répondu ididak 2015-08-03 23:20:33
#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;
}
330
répondu kev 2018-05-19 10:01:31

pour ceux avec qui il ne s'assoit pas bien de sacrifier toute l'efficacité pour la taille du code et de voir "efficace" comme un type d'élégance, ce qui suit devrait frapper un point sucré (et je pense que la classe de conteneur modèle est un ajout très é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 habituellement d'utiliser les types std::vector<std::string> comme second paramètre ( ContainerT )... mais list<> est beaucoup 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 corde et utiliser quelque chose comme std::list<subString>subString ne fait pas de copies pour des augmentations de vitesse incroyables.

C'est plus de deux fois plus rapide que le tokenize le plus rapide sur cette page et presque 5 fois plus rapide que d'autres. Aussi avec les types de paramètre parfaits vous pouvez éliminer toutes les copies de chaîne et de liste pour des augmentations de vitesse supplémentaires.

en outre, il ne fait pas le retour (extrêmement inefficace) du résultat, mais plutôt il passe les tokens comme référence, ce qui vous permet également de créer des tokens en utilisant plusieurs appels si vous le souhaitez.

enfin, il vous permet de spécifier si vous souhaitez découper des jetons vides à partir des résultats via un dernier paramètre optionnel.

Tous std::string ... le reste est facultatif. Il n'utilise pas les flux ou la bibliothèque boost, mais est assez souple pour pouvoir accepter certains de ces types étrangers naturellement.

170
répondu Marius 2016-09-19 13:00:24

voici une autre solution. Il 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 templatisé pour manipuler des séparateurs de chaîne, des chaînes larges, etc.

noter que la division "" résulte en une seule chaîne vide et la division "," (c.-à-d. sep) donne deux chaînes vides.

il peut aussi être facilement étendu pour sauter des 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 l'on souhaite séparer une chaîne à 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;
}
153
répondu Alec Thomas 2016-10-04 22:33:35

c'est ma façon préférée d'itérer à travers une chaîne. Tu peux faire ce que tu veux 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...
}
110
répondu gnomed 2018-04-12 11:37:30

c'est similaire à la question comment tokenize 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;
    }
}
77
répondu Ferruccio 2017-05-23 12:34:53

j'aime ce qui suit parce qu'il met les résultats dans un vecteur, supporte une chaîne comme un delim et donne le contrôle sur la conservation des valeurs vides. Mais, il n'a pas l'air de bonne qualité, 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 cela. Et, si par "espace blanc", vous voulez vraiment dire n'importe quel type d'espace blanc, en utilisant Boost's split avec is_any_of() fonctionne très bien.

66
répondu Shadow2531 2017-01-08 04:33:22

le LTS ne dispose pas déjà d'une telle méthode.

cependant, vous pouvez soit utiliser C 'S strtok() fonction en utilisant le std::string::c_str() membre, ou vous pouvez écrire votre propre. Voici un exemple de code que j'ai trouvé après une recherche rapide sur Google ( "fente de chaîne de caractères 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);
    }
}

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

si vous avez des questions sur l'échantillon de code, laissez un commentaire et je vous expliquerai.

et juste parce qu'il n'implémente pas un typedef appelé itérateur ou surcharge l'opérateur << ne signifie pas qu'il est mauvais code. J'utilise les fonctions C assez fréquemment. Par exemple:, printf et scanf sont plus rapides que std::cin et std::cout (de manière significative), la syntaxe fopen est beaucoup plus conviviale pour les types binaires, et ils ont également tendance à produire de plus petits EXEs.

Ne faites pas vendus sur ce "Élégance par rapport à la performance" affaire.

50
répondu nlaq 2018-04-12 11:35:55

Voici une fonction split qui:

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

    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'usage:

    vector<string> v = split<string>("Hello, there; World", ";,");
    vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");
39
répondu Marco M. 2017-05-23 22:17:34

j'ai une solution de 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;

alors au lieu d'imprimer vous pouvez le mettre dans un vecteur.

33
répondu rhomu 2013-01-15 00:12:16

encore une autre voie 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 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 ça! Et c'est juste une façon d'utiliser le tokenizer, comme comment 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 ;)

33
répondu Robert 2013-09-11 08:11:28

Voici une solution simple qui n'utilise que la bibliothèque standard 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;
}

l'argument regex permet de vérifier plusieurs arguments (espaces, virgules, etc.)

Je ne vérifie habituellement que les espaces et les virgules, donc 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 ( , ).

Note, Si vous voulez pour séparer wstring au lieu de string ,

  • changer toutes les std::regex en std::wregex
  • changer toutes les sregex_token_iterator en wsregex_token_iterator

Note, vous pourriez aussi vouloir prendre l'argument string par référence, en fonction de votre compilateur.

29
répondu dk123 2015-06-24 09:31:50

si vous aimez utiliser boost, mais que vous voulez utiliser une chaîne entière comme délimiteur (au lieu de caractères simples 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;
}
24
répondu zerm 2012-02-09 09:32:17

en utilisant std::stringstream comme vous avez fonctionne parfaitement bien, et faire exactement ce que vous vouliez. Si vous êtes juste à la recherche d'une façon différente de faire les choses, 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;
}
23
répondu KTC 2018-04-12 11:42:30

il y a une fonction appelé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;
}
18
répondu Pratik Deoghare 2014-05-02 14:49:30

est une solution regex qui n'utilise que 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
}
17
répondu AJMansfield 2012-10-29 16:15:47

le stringstream peut être pratique si vous avez besoin d'analyser la chaîne de caractères par des symboles non spatiaux:

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, ';')
15
répondu lukmac 2015-06-22 17:02:21

Jusqu'à présent j'ai utilisé celui dans Boost , mais j'avais besoin de quelque chose qui ne dépend pas de lui, donc j'en suis venu à 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, c'est que dans separators , vous pouvez passer plus d'un caractère.

14
répondu Goran 2011-05-22 23:02:42

j'ai roulé le mien en utilisant strtok et j'ai utilisé boost pour fendre une corde. La meilleure méthode que j'ai trouvé est le Chaîne C++ Bibliothèque d'outils . Il 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 flexibilité que cet exemple simple montre, mais son utilité dans l'analyse d'une chaîne en éléments utiles est incroyable.

13
répondu DannyK 2014-01-07 20:28:03

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 délimiteur, peut aussi être utilisé avec des données binaires (std::string supporte les données binaires, y compris nulls)

utilisant:

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

sortie:

this
is
!example!string
13
répondu user1438233 2016-07-14 20:17:10

j'ai fait cela parce que j'avais besoin d'un moyen facile de séparer les cordes et les cordes en C... J'espère que quelqu'un d'autre peut le trouver utile. De plus, il ne s'appuie 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 qui peuvent être faites pour améliorer encore plus son élégance et s'il vous plaît 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.rpc:

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

sortira:

Ce

is

an

exemple

cstring

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 entrées vides seront exclues):

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

le but était de rendre similaire à C # ' S Split() méthode où séparer une chaîne est aussi facile 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
répondu Steve Dell 2017-02-19 17:47:57

Qu'à ce sujet:

#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
répondu gibbz 2012-12-19 22:05:24

Voici une autre façon de 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
répondu 2 revsuser246110 2010-01-08 03:27:24

j'aime utiliser les méthodes boost/regex pour cette tâche car elles offrent un maximum de flexibilité pour spécifier les critères de division.

#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
répondu Marty B 2011-06-12 09:25:38

récemment, j'ai dû partager un mot Camel-cased en sous-mots. Il n'y a pas de délimiteur, 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 cordes étroites et larges. Parce qu'il respecte le lieu actuel, il divise "RaumfahrtÜberwachungsVerordnung" en "Raumfahrt", "Überwachungs" et "Verordnung".

Note std::upper doit être réellement passé comme argument de modèle de fonction. Puis le plus généralisé de cette fonction peut se diviser aux délimiteurs comme "," , ";" ou " " aussi.

9
répondu Andreas Spindler 2011-09-14 09:47:57

cette réponse prend la corde et la met dans un vecteur de cordes. Il utilise la bibliothèque boost.

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));
9
répondu NL628 2017-12-09 21:14:38

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
répondu Aleksey Bykov 2013-04-07 16:07:55

le code ci-dessous utilise strtok() pour séparer 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
répondu Software_Developer 2015-04-29 15:06:31