Java dispose d'une méthode de fractionnement pratique :
String str = "The quick brown fox";
String[] results = str.split(" ");
Existe-t-il un moyen simple de faire cela en C++ ?
Java dispose d'une méthode de fractionnement pratique :
String str = "The quick brown fox";
String[] results = str.split(" ");
Existe-t-il un moyen simple de faire cela en C++ ?
El Boost tokenizer peut rendre ce genre de chose assez simple :
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int, char**)
{
string text = "token, test string";
char_separator<char> sep(", ");
tokenizer< char_separator<char> > tokens(text, sep);
BOOST_FOREACH (const string& t, tokens) {
cout << t << "." << endl;
}
}
Mise à jour pour C++11 :
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int, char**)
{
string text = "token, test string";
char_separator<char> sep(", ");
tokenizer<char_separator<char>> tokens(text, sep);
for (const auto& t : tokens) {
cout << t << "." << endl;
}
}
C'est bien, je l'ai utilisé récemment. Mon compilateur Visual Studio se plaint étrangement jusqu'à ce que j'utilise un espace pour séparer les deux caractères ">" avant le bit tokens(text, sep) : (error C2947 : expecting '>' to terminate template-argument-list, found '>>')
@AndyUK oui, sans l'espace le compilateur l'analyse comme un opérateur d'extraction plutôt que deux gabarits de fermeture.
Les algorithmes de la bibliothèque standard C++ sont presque universellement basés sur des itérateurs plutôt que sur des conteneurs concrets. Malheureusement, cela rend difficile la mise à disposition d'une fonction de type Java split
dans la bibliothèque standard C++, même si personne ne soutient que cela serait pratique. Mais quel serait son type de retour ? std::vector<std::basic_string<…>>
? Peut-être, mais alors nous sommes obligés d'effectuer des allocations (potentiellement redondantes et coûteuses).
Au lieu de cela, le C++ offre une pléthore de moyens de diviser les chaînes de caractères en fonction de délimiteurs arbitrairement complexes, mais aucun d'entre eux n'est encapsulé aussi bien que dans d'autres langages. Les nombreux moyens remplir des articles de blog entiers .
Dans sa forme la plus simple, vous pourriez itérer en utilisant std::string::find
jusqu'à ce que tu atteignes std::string::npos
et extraire le contenu en utilisant std::string::substr
.
Une version plus fluide (et idiomatique, mais basique) pour le fractionnement sur les espaces blancs utiliserait une balise std::istringstream
:
auto iss = std::istringstream{"The quick brown fox"};
auto str = std::string{};
while (iss >> str) {
process(str);
}
Utilisation de std::istream_iterator
s En outre, le contenu du flux de chaînes de caractères peut également être copié dans un vecteur à l'aide de son constructeur de plage d'itérateurs.
Plusieurs bibliothèques (telles que Boost.Tokenizer ) proposent des tokénisateurs spécifiques.
Un fractionnement plus avancé nécessite des expressions régulières. Le C++ fournit les std::regex_token_iterator
à cette fin en particulier :
auto const str = "The quick brown fox"s;
auto const re = std::regex{R"(\s+)"};
auto const vec = std::vector<std::string>(
std::sregex_token_iterator{begin(str), end(str), re, -1},
std::sregex_token_iterator{}
);
Malheureusement, le coup de pouce n'est pas toujours disponible pour tous les projets. Je vais devoir chercher une réponse sans boost.
Tous les projets ne sont pas ouverts à l'"open source". Je travaille dans des industries fortement réglementées. Ce n'est pas un problème, vraiment. C'est juste un fait de la vie. Boost n'est pas disponible partout.
@NonlinearIdeas L'autre question/réponse ne concernait pas du tout les projets Open Source. Il en va de même pour cualquier projet. Cela dit, je comprends bien sûr les normes restreintes telles que MISRA C, mais il est entendu que vous construisez tout à partir de zéro de toute façon (à moins que vous ne trouviez une bibliothèque conforme - une rareté). Quoi qu'il en soit, le problème n'est pas que "Boost n'est pas disponible", mais que vous avez des exigences particulières pour lesquelles presque tout le monde est d'accord. cualquier Une réponse générale serait inadaptée.
J'ai eu des problèmes en utilisant cette technique avec des caractères 0x0A dans la chaîne, ce qui faisait sortir la boucle while prématurément. Sinon, c'est une solution simple et rapide.
C'est bien, mais il faut garder à l'esprit qu'en faisant cela, le délimiteur par défaut ' \n n'est pas pris en compte. Cet exemple fonctionnera, mais si vous utilisez quelque chose comme : while(getline(inFile,word,' ')) où inFile est un objet ifstream contenant plusieurs lignes, vous obtiendrez des résultats bizarres
C'est dommage que getline retourne le flux plutôt que la chaîne, ce qui le rend inutilisable dans les listes d'initialisation sans stockage temporaire
Utilisez strtok. À mon avis, il n'y a pas besoin de construire une classe autour de la tokenisation, à moins que strtok ne vous fournisse pas ce dont vous avez besoin. Ce n'est peut-être pas le cas, mais en plus de 15 ans d'écriture de divers codes d'analyse syntaxique en C et C++, j'ai toujours utilisé strtok. Voici un exemple
char myString[] = "The quick brown fox";
char *p = strtok(myString, " ");
while (p) {
printf ("Token: %s\n", p);
p = strtok(NULL, " ");
}
Quelques mises en garde (qui pourraient ne pas répondre à vos besoins). La chaîne est "détruite" dans le processus, ce qui signifie que les caractères EOS sont placés en ligne dans les emplacements de délimitation. Pour une utilisation correcte, vous devrez peut-être créer une version non-const de la chaîne. Vous pouvez également modifier la liste des délimiteurs à mi-parcours.
À mon avis, le code ci-dessus est beaucoup plus simple et facile à utiliser que l'écriture d'une classe séparée pour cela. Pour moi, c'est une de ces fonctions que le langage fournit et il le fait bien et proprement. Il s'agit simplement d'une solution "basée sur le C". C'est approprié, c'est facile, et vous n'avez pas à écrire beaucoup de code supplémentaire :-)
Ce n'est pas que je n'aime pas le C, mais strtok n'est pas thread-safe, et vous devez être certain que la chaîne que vous lui envoyez contient un caractère nul pour éviter un éventuel dépassement de tampon.
@tloach : dans le compilateur MS C++, strtok est thread safe car la variable statique interne est créée sur le TLS (thread local storage) (en fait, cela dépend du compilateur).
Vous pouvez utiliser les flux, les itérateurs et l'algorithme de copie pour faire cela assez directement.
#include <string>
#include <vector>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>
#include <sstream>
#include <algorithm>
int main()
{
std::string str = "The quick brown fox";
// construct a stream from the string
std::stringstream strstr(str);
// use stream iterators to copy the stream to the vector as whitespace separated strings
std::istream_iterator<std::string> it(strstr);
std::istream_iterator<std::string> end;
std::vector<std::string> results(it, end);
// send the vector to stdout.
std::ostream_iterator<std::string> oit(std::cout);
std::copy(results.begin(), results.end(), oit);
}
@Vadi : parce que modifier le message de quelqu'un d'autre est assez intrusif. @pheze : Je préfère laisser le std
De cette façon, je sais d'où vient mon objet, c'est une simple question de style.
Je comprends ta raison et je pense que c'est un bon choix si ça marche pour toi, mais d'un point de vue pédagogique, je suis d'accord avec pheze. Il est plus facile de lire et de comprendre un exemple complètement étranger comme celui-ci avec un "using namespace std" en haut parce que cela demande moins d'effort pour interpréter les lignes suivantes... surtout dans ce cas parce que tout provient de la bibliothèque standard. Vous pouvez le rendre facile à lire et évident d'où viennent les objets par une série de "using std::string ;" etc. D'autant plus que la fonction est très courte.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
230 votes
Je ne peux pas croire que cette tâche de routine soit un tel casse-tête en c++.
6 votes
Ce n'est pas un casse-tête en c++ - il y a plusieurs façons d'y parvenir. Les programmeurs sont moins au courant de c++ que de c# - c'est une question de marketing et d'investissements... voir ceci pour les différentes options c++ pour réaliser la même chose : cplusplus.com/faq/sequences/strings/split
11 votes
@hB0 passer par beaucoup de questions réponses et toujours pas décider des moyens est un mal de tête. l'un a besoin de cette bibliothèque, l'autre est juste pour les espaces, l'autre ne gère pas les espaces .
2 votes
Duplicata possible de Séparer une chaîne de caractères en C++ ?
8 votes
Pourquoi tout ce qui est en C++ doit-il être un combat ?
2 votes
Le C++ étant un langage performant, il ne favorise pas une approche unique de la manipulation des chaînes de caractères. Pour cette application, parfois tout ce que vous voulez est le dernier token ou les premiers tokens, pourtant un "split" va tout tokeniser sans aucun arrêt basé sur le besoin. Quoi qu'il en soit, les opérations sur les chaînes de caractères sont coûteuses dans n'importe quel langage, et c'est pourquoi la norme C++ s'est explicitement prononcée contre les interfaces trop simples qui cachent la véritable complexité.
0 votes
@JohnPhuNguyen :
a "split" will tokenize everything without any need-based stops
. Sans blague. C'est exactement le comportement que l'on souhaite.1 votes
Il est intéressant de noter que toutes les réponses à cette question sont également des réponses à
Why are Java and Python so much more popular than C++?
.1 votes
@stackoverflowuser2010 Je pense que vous n'avez pas compris. Oui, c'est exactement le comportement souhaité pour une approche unique, c'est ce que j'ai dit. Et c'est pertinent pour répondre à la raison pour laquelle il est plus facile de le faire en Java qu'en C++. La bibliothèque standard CPP évite explicitement toute méthode de commodité qui fait abstraction de la complexité algorithmique - c'est pourquoi les méthodes de type "taille unique" peuvent être fournies mais ne le sont pas.
0 votes
@JohnPhuNguyen : Je ne comprends pas ce que vous voulez dire par
one size fits all
. Dans cette question, il n'y a qu'une seule taille : tokenize a string. Il n'y a pas d'autre taille.0 votes
@stackoverflowuser2010 Ce que je veux dire, c'est que la tokénisation est une fonction coûteuse qui est souvent utilisée de manière générique, même lorsqu'elle n'est pas nécessaire. Le C++ encourage la mise en œuvre de cas d'utilisation spécifiques au lieu de la tokénisation, parce que l'acte réel de tokénisation est rarement nécessaire. Elle est souvent utilisée comme un moyen de trouver un ou plusieurs éléments dans une chaîne. Au lieu de cela, le C++ propose la correspondance regex (entre autres), qui est coûteuse, mais jamais aussi coûteuse que la manipulation de chaînes qui se produit dans les tokenizers. Oui, un tokenizer s'adapte facilement à toutes les solutions, mais c'est rarement la meilleure. Le C++ déconseille d'emprunter cette voie.
0 votes
Une solution à cette question exacte semble se trouver ici : riptutorial.com/cplusplus/exemple/2148/tokenize