Parse (split) une chaîne en C++ en utilisant le délimiteur de chaîne (standard c++) [dupliquer]
cette question a déjà une réponse ici:
- la façon la plus élégante d'itérer les mots d'une chaîne [fermé] 74 réponses
j'analyse une chaîne en C++ en utilisant ce qui suit:
string parsed,input="text to be parsed";
stringstream input_stringstream(input);
if(getline(input_stringstream,parsed,' '))
{
// do some processing.
}
Parsing avec un seul délimiteur de caractères est très bien. Mais que faire si je veux utiliser une chaîne comme délimiteur.
exemple: je veux diviser:
scott>=tiger
avec >= comme délimiteur de sorte que je peux obtenir scott et le tigre.
11 réponses
vous pouvez utiliser la fonction std::string::find() pour trouver la position de votre délimiteur de chaîne, puis utiliser std::string::substr() pour obtenir un jeton.
exemple:
std::string s = "scott>=tiger";
std::string delimiter = ">=";
std::string token = s.substr(0, s.find(delimiter)); // token is "scott"
-
la fonction
find(const string& str, size_t pos = 0)renvoie la position de la première occurrence destrdans la chaîne, ounpossi la chaîne n'est pas trouvée. -
la fonction
substr(size_t pos = 0, size_t n = npos)renvoie une sous-couche de l'objet, à partir de la positionposet de la longueurnpos.
si vous avez plusieurs délimiteurs, après avoir extrait un token, vous pouvez le supprimer (délimiteur inclus) pour procéder aux extractions suivantes (si vous voulez préserver la chaîne originale, utilisez juste s = s.substr(pos + delimiter.length()); ):
s.erase(0, s.find(delimiter) + delimiter.length());
de cette façon, vous pouvez facilement boucler pour obtenir chaque token.
Exemple Complet
std::string s = "scott>=tiger>=mushroom";
std::string delimiter = ">=";
size_t pos = 0;
std::string token;
while ((pos = s.find(delimiter)) != std::string::npos) {
token = s.substr(0, pos);
std::cout << token << std::endl;
s.erase(0, pos + delimiter.length());
}
std::cout << s << std::endl;
sortie:
scott
tiger
mushroom
cette méthode utilise std::string::find sans muter la chaîne originale en se rappelant le début et la fin du jeton de substrat précédent.
#include <iostream>
#include <string>
int main()
{
std::string s = "scott>=tiger";
std::string delim = ">=";
auto start = 0U;
auto end = s.find(delim);
while (end != std::string::npos)
{
std::cout << s.substr(start, end - start) << std::endl;
start = end + delim.length();
end = s.find(delim, start);
}
std::cout << s.substr(start, end);
}
vous pouvez utiliser la fonction next pour séparer la chaîne de caractères:
vector<string> split(const string& str, const string& delim)
{
vector<string> tokens;
size_t prev = 0, pos = 0;
do
{
pos = str.find(delim, prev);
if (pos == string::npos) pos = str.length();
string token = str.substr(prev, pos-prev);
if (!token.empty()) tokens.push_back(token);
prev = pos + delim.length();
}
while (pos < str.length() && prev < str.length());
return tokens;
}
strtok permet de passer en caractères multiples comme délimiteurs. Je parie que si vous passez dans ">=" votre chaîne d'exemple serait divisée correctement (même si les > et = sont comptés comme des délimiteurs individuels).
EDIT si vous ne voulez pas utiliser c_str() pour convertir une chaîne de caractères*, vous pouvez utiliser substr et find_first_of pour tokenize.
string token, mystring("scott>=tiger");
while(token != mystring){
token = mystring.substr(0,mystring.find_first_of(">="));
mystring = mystring.substr(mystring.find_first_of(">=") + 1);
printf("%s ",token.c_str());
}
ce code divise les lignes du texte, et ajoute tout le monde dans un vecteur.
vector<string> split(char *phrase, string delimiter){
vector<string> list;
string s = string(phrase);
size_t pos = 0;
string token;
while ((pos = s.find(delimiter)) != string::npos) {
token = s.substr(0, pos);
list.push_back(token);
s.erase(0, pos + delimiter.length());
}
return list;
}
appelé par:
vector<string> listFilesMax = split(buffer, "\n");
j'utiliserais boost::tokenizer . Voici de la documentation expliquant comment faire une fonction tokenizer appropriée: http://www.boost.org/doc/libs/1_52_0/libs/tokenizer/tokenizerfunction.htm
En voici un qui fonctionne pour votre affaire.
struct my_tokenizer_func
{
template<typename It>
bool operator()(It& next, It end, std::string & tok)
{
if (next == end)
return false;
char const * del = ">=";
auto pos = std::search(next, end, del, del + 2);
tok.assign(next, pos);
next = pos;
if (next != end)
std::advance(next, 2);
return true;
}
void reset() {}
};
int main()
{
std::string to_be_parsed = "1) one>=2) two>=3) three>=4) four";
for (auto i : boost::tokenizer<my_tokenizer_func>(to_be_parsed))
std::cout << i << '\n';
}
voilà ce que je pense. Il gère les cas de bord et prend un paramètre optionnel pour supprimer les entrées vides des résultats.
bool endsWith(const std::string& s, const std::string& suffix)
{
return s.size() >= suffix.size() &&
s.substr(s.size() - suffix.size()) == suffix;
}
std::vector<std::string> split(const std::string& s, const std::string& delimiter, const bool& removeEmptyEntries = false)
{
std::vector<std::string> tokens;
for (size_t start = 0, end; start < s.length(); start = end + delimiter.length())
{
size_t position = s.find(delimiter, start);
end = position != string::npos ? position : s.length();
std::string token = s.substr(start, end - start);
if (!removeEmptyEntries || !token.empty())
{
tokens.push_back(token);
}
}
if (!removeEmptyEntries &&
(s.empty() || endsWith(s, delimiter)))
{
tokens.push_back("");
}
return tokens;
}
exemples
split("a-b-c", "-"); // [3]("a","b","c")
split("a--c", "-"); // [3]("a","","c")
split("-b-", "-"); // [3]("","b","")
split("--c--", "-"); // [5]("","","c","","")
split("--c--", "-", true); // [1]("c")
split("a", "-"); // [1]("a")
split("", "-"); // [1]("")
split("", "-", true); // [0]()
pour délimiteur de chaîne de caractères
Scinde une chaîne basée sur délimiteur de chaîne de caractères. Comme séparer la chaîne "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih" basée sur la chaîne de délimiteur "-+" , la sortie sera {"adsf", "qwret", "nvfkbdsj", "orthdfjgh", "dfjrleih"}
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
// for string delimiter
vector<string> split(string s, string delimiter) {
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
string token;
vector<string> res;
while ((pos_end = s.find(delimiter, pos_start)) != string::npos) {
token = s.substr(pos_start, pos_end - pos_start);
pos_start = pos_end + delim_len;
res.push_back(token);
}
res.push_back(s.substr(pos_start));
return res;
}
int main() {
string str = "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih";
string delimiter = "-+";
vector<string> v = split(str, delimiter);
for (auto i : v) cout << i << endl;
return 0;
}
pour le délimiteur de caractère unique
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
vector<string> split(const string &s, char delim) {
vector<string> result;
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
result.push_back(item);
}
return result;
}
int main() {
string str = "adsf+qwer+poui+fdgh";
vector<string> v = split(str, '+');
for (auto i : v) cout << i << endl;
return 0;
}
si vous ne voulez pas modifier la chaîne de caractères (comme dans la réponse de Vincenzo Pii) et si vous voulez afficher le dernier jeton, vous pouvez utiliser cette approche:
inline std::vector<std::string> splitString( const std::string &s, const std::string &delimiter ){
std::vector<std::string> ret;
size_t start = 0;
size_t end = 0;
size_t len = 0;
std::string token;
do{ end = s.find(delimiter,start);
len = end - start;
token = s.substr(start, len);
ret.emplace_back( token );
start += len + delimiter.length();
std::cout << token << std::endl;
}while ( end != std::string::npos );
return ret;
}
#include<iostream>
#include<algorithm>
using namespace std;
int split_count(string str,char delimit){
return count(str.begin(),str.end(),delimit);
}
void split(string str,char delimit,string res[]){
int a=0,i=0;
while(a<str.size()){
res[i]=str.substr(a,str.find(delimit));
a+=res[i].size()+1;
i++;
}
}
int main(){
string a="abc.xyz.mno.def";
int x=split_count(a,'.')+1;
string res[x];
split(a,'.',res);
for(int i=0;i<x;i++)
cout<<res[i]<<endl;
return 0;
}
P. S: ne fonctionne que si les longueurs des cordes après séparation sont égales
std::vector<std::string> split(const std::string& s, char c) {
std::vector<std::string> v;
unsigned int ii = 0;
unsigned int j = s.find(c);
while (j < s.length()) {
v.push_back(s.substr(i, j - i));
i = ++j;
j = s.find(c, j);
if (j >= s.length()) {
v.push_back(s.substr(i, s,length()));
break;
}
}
return v;
}