Quelle serait la méthode la plus simple pour diviser une chaîne en utilisant c ++ 11?
J'ai vu la méthode utilisée par post , mais j'estime qu'il devrait y avoir une façon moins verbeuse de le faire en utilisant le nouveau standard.
Edit: je voudrais avoir un vector<string>
à la suite et être capable de délimiter sur un seul caractère.
std::regex_token_iterator
effectue une génération de jetons générique basée sur une expression régulière. Cela peut être exagéré de faire une simple division sur un seul caractère, mais cela fonctionne et n’est pas trop bavard:
std::vector<std::string> split(const string& input, const string& regex) {
// passing -1 as the submatch index parameter performs splitting
std::regex re(regex);
std::sregex_token_iterator
first{input.begin(), input.end(), re, -1},
last;
return {first, last};
}
Voici un moyen (peut-être moins détaillé) de scinder une chaîne (basé sur le post que vous avez mentionné).
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> split(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> elems;
while (std::getline(ss, item, delim)) {
elems.Push_back(item);
// elems.Push_back(std::move(item)); // if C++11 (based on comment from @mchiasson)
}
return elems;
}
Voici un exemple de fractionnement d'une chaîne et de remplissage d'un vecteur avec les éléments extraits à l'aide de boost
.
#include <boost/algorithm/string.hpp>
std::string my_input("A,B,EE");
std::vector<std::string> results;
boost::algorithm::split(results, my_input, is_any_of(","));
assert(results[0] == "A");
assert(results[1] == "B");
assert(results[2] == "EE");
Une autre solution regex inspirée d’autres réponses mais espérons-le plus courte et plus lisible
std::string s{"String to split here, and here, and here,..."};
std::regex regex{R"([\s,]+)"}; // split on space and comma
std::sregex_token_iterator it{s.begin(), s.end(), regex, -1};
std::vector<std::string> words{it, {}};
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
vector<string> split(const string& str, int delimiter(int) = ::isspace){
vector<string> result;
auto e=str.end();
auto i=str.begin();
while(i!=e){
i=find_if_not(i,e, delimiter);
if(i==e) break;
auto j=find_if(i,e, delimiter);
result.Push_back(string(i,j));
i=j;
}
return result;
}
int main(){
string line;
getline(cin,line);
vector<string> result = split(line);
for(auto s: result){
cout<<s<<endl;
}
}
Mon choix est boost::tokenizer
mais je n'ai pas eu de tâches lourdes et j'ai testé avec des données énormes. Exemple de boost doc avec modification lambda:
#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>
#include <vector>
int main()
{
using namespace std;
using namespace boost;
string s = "This is, a test";
vector<string> v;
tokenizer<> tok(s);
for_each (tok.begin(), tok.end(), [&v](const string & s) { v.Push_back(s); } );
// result 4 items: 1)This 2)is 3)a 4)test
return 0;
}
Je ne sais pas si c'est moins bavard, mais il serait peut-être plus facile de parler pour ceux qui sont plus expérimentés dans des langages dynamiques tels que javascript. La seule fonctionnalité C++ 11 utilisée est lambdas.
#include <algorithm>
#include <string>
#include <cctype>
#include <iostream>
#include <vector>
int main()
{
using namespace std;
string s = "hello how are you won't you tell me your name";
vector<string> tokens;
string token;
for_each(s.begin(), s.end(), [&](char c) {
if (!isspace(c))
token += c;
else
{
if (token.length()) tokens.Push_back(token);
token.clear();
}
});
if (token.length()) tokens.Push_back(token);
return 0;
}
Ceci est ma réponse. Verbose, lisible et efficace.
std::vector<std::string> tokenize(const std::string& s, char c) {
auto end = s.cend();
auto start = end;
std::vector<std::string> v;
for( auto it = s.cbegin(); it != end; ++it ) {
if( *it != c ) {
if( start == end )
start = it;
continue;
}
if( start != end ) {
v.emplace_back(start, it);
start = end;
}
}
if( start != end )
v.emplace_back(start, end);
return v;
}
Voici une solution C++ 11 qui utilise uniquement std :: string :: find (). Le délimiteur peut comporter un nombre quelconque de caractères. Les jetons analysés sont générés via un itérateur de sortie, qui est généralement un std :: back_inserter dans mon code.
Je n'ai pas testé cela avec UTF-8, mais je m'attends à ce que cela fonctionne tant que l'entrée et le délimiteur sont des chaînes UTF-8 valides.
#include <string>
template<class Iter>
Iter splitStrings(const std::string &s, const std::string &delim, Iter out)
{
if (delim.empty()) {
*out++ = s;
return out;
}
size_t a = 0, b = s.find(delim);
for ( ; b != std::string::npos;
a = b + delim.length(), b = s.find(delim, a))
{
*out++ = std::move(s.substr(a, b - a));
}
*out++ = std::move(s.substr(a, s.length() - a));
return out;
}
Quelques cas de test:
void test()
{
std::vector<std::string> out;
size_t counter;
std::cout << "Empty input:" << std::endl;
out.clear();
splitStrings("", ",", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, empty delimiter:" << std::endl;
out.clear();
splitStrings("Hello, world!", "", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", no delimiter in string:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxya", "xyz", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string:" << std::endl;
out.clear();
splitStrings("abxycdxy!!xydefxya", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string"
", input contains blank token:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxya", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string"
", nothing after last delimiter:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxy", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", only delimiter exists string:" << std::endl;
out.clear();
splitStrings("xy", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
}
Production attendue:
Entrée vide: 0: Entrée non vide, délimiteur vide: 0: Hello, world! Entrée non vide, délimiteur non vide, pas de délimiteur dans la chaîne : 0: abxycdxyxydefxya Entrée non vide, délimiteur non vide, le délimiteur existe chaîne: 0: ab 1: cd 2: !! 3: def 4: a Entrée non vide, délimiteur non vide, le séparateur existe chaîne, l'entrée contient un jeton vierge: 0: ab 1: cd 2: 3: def 4: a Entrée non vide, délimiteur non vide, le séparateur existe chaîne, rien après le dernier séparateur: 0: ab 1: cd 2: 3: def 4: Une entrée non vide, un délimiteur non vide, seul le séparateur existe chaîne: 0: 1:
#include <string>
#include <vector>
#include <sstream>
inline vector<string> split(const string& s) {
vector<string> result;
istringstream iss(s);
for (string s; iss >> s; )
result.Push_back(s);
return result;
}