J'ai une chaîne que je voudrais tokenize . Mais la fonction C strtok()
nécessite que ma chaîne soit un char*
. Comment puis-je le faire simplement?
J'ai essayé:
token = strtok(str.c_str(), " ");
qui échoue car il le transforme en un const char*
, pas un char*
#include <iostream>
#include <string>
#include <sstream>
int main(){
std::string myText("some-text-to-tokenize");
std::istringstream iss(myText);
std::string token;
while (std::getline(iss, token, '-'))
{
std::cout << token << std::endl;
}
return 0;
}
Ou, comme mentionné, utilisez boost pour plus de flexibilité.
Si boost est disponible sur votre système (je pense que c'est standard sur la plupart des distributions Linux de nos jours), il dispose d'une classe Tokenizer que vous pouvez utiliser.
Si ce n'est pas le cas, un rapide Google met en place un tokenizer roulé à la main pour std :: string que vous pouvez probablement simplement copier et coller. C'est très court.
Et, si vous n'aimez ni l'un ni l'autre, voici la fonction split () que j'ai écrite pour faciliter ma vie. Il va briser une chaîne en utilisant n'importe lequel des caractères de "delim" comme séparateurs. Les pièces sont ajoutées au vecteur "parties":
void split(const string& str, const string& delim, vector<string>& parts) {
size_t start, end = 0;
while (end < str.size()) {
start = end;
while (start < str.size() && (delim.find(str[start]) != string::npos)) {
start++; // skip initial whitespace
}
end = start;
while (end < str.size() && (delim.find(str[end]) == string::npos)) {
end++; // skip to end of Word
}
if (end-start != 0) { // just ignore zero-length strings.
parts.Push_back(string(str, start, end-start));
}
}
}
Dupliquer la chaîne, la marquer, puis la libérer.
char *dup = strdup(str.c_str());
token = strtok(dup, " ");
free(dup);
Il existe une solution plus élégante.
Avec std :: string, vous pouvez utiliser resize () pour allouer un tampon suffisamment grand et & s [0] pour obtenir un pointeur sur le tampon interne .
À ce stade, beaucoup de braves gens vont sauter et crier à l'écran. Mais c'est le fait. Il y a environ 2 ans
le groupe de travail des bibliothèques a décidé (lors d'une réunion à Lillehammer) que, tout comme pour std :: vector, std :: string devrait également disposer, de manière formelle, et non seulement dans la pratique, d'un tampon contigu garanti .
L'autre préoccupation est que strtok () augmente la taille de la chaîne. La documentation MSDN indique:
Chaque appel à strtok modifie strToken en insérant un caractère nul après le jeton renvoyé par cet appel .
Mais ce n'est pas correct. En réalité, la fonction remplace l'occurrence first d'un caractère séparateur par\0. Aucun changement dans la taille de la chaîne. Si nous avons cette chaîne:
un deux trois quatre
nous finirons avec
un\0deux\0 - trois\0-quatre
Donc ma solution est très simple:
std::string str("some-text-to-split");
char seps[] = "-";
char *token;
token = strtok( &str[0], seps );
while( token != NULL )
{
/* Do your thing */
token = strtok( NULL, seps );
}
Lire la discussion sur http://www.archivum.info/comp.lang.c++/2008-05/02889/does_std::string_have_something_like_CString::GetBuffer
EDIT: l'utilisation de const cast est seulement est utilisée pour démontrer l'effet de strtok()
lorsqu'il est appliqué à un pointeur renvoyé par string :: c_str ().
Vous ne devez pas utiliser strtok()
car il modifie la chaîne sous forme de jetons qui peut entraîner un comportement indésirable, voire indéfini, car la chaîne C "appartient" à l'instance de chaîne.
#include <string>
#include <iostream>
int main(int ac, char **av)
{
std::string theString("hello world");
std::cout << theString << " - " << theString.size() << std::endl;
//--- this cast *only* to illustrate the effect of strtok() on std::string
char *token = strtok(const_cast<char *>(theString.c_str()), " ");
std::cout << theString << " - " << theString.size() << std::endl;
return 0;
}
Après l'appel à strtok()
, l'espace a été "supprimé" de la chaîne ou réduit à un caractère non imprimable, mais la longueur reste inchangée.
>./a.out
hello world - 11
helloworld - 11
Par conséquent, vous devez recourir au mécanisme natif, à la duplication de la chaîne ou à une bibliothèque tierce, comme indiqué précédemment.
Il échoue carstr.c_str()
renvoie une chaîne constante, mais char * strtok (char * str, const char * delimiters )
nécessite une chaîne volatile. Donc vous devez utiliser * const_cast <char >pour le rendre voletile . Je vous donne un programme complet mais petit pour tokeniser la chaîne en utilisant la fonction C strtok ().
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
int main() {
string s="20#6 5, 3";
char *str=const_cast< char *>(s.c_str());
char *tok;
tok=strtok(str, "#, " );
int arr[4], i=0;
while(tok!=NULL){
arr[i++]=stoi(tok);
tok=strtok(NULL, "#, " );
}
for(int i=0; i<4; i++) cout<<arr[i]<<endl;
return 0;
}
Je suppose que le langage est C, ou C++ ...
strtok, IIRC, remplacez les séparateurs par\0. C’est ce qu’il ne peut pas utiliser une chaîne de const. Ce qui est judicieux si vous devez garder la chaîne inchangée (ce que le const suggère ...).
D'un autre côté, vous pouvez utiliser un autre tokenizer, peut-être roulé à la main, moins violent sur l'argument donné.
Si vous n’aimez pas l’open source, vous pouvez utiliser les classes de sous-tampon et de sous-fournisseur de https://github.com/EdgeCast/json_parser . La chaîne d'origine est laissée intacte, il n'y a pas d'allocation ni de copie des données. Je n'ai pas compilé ce qui suit, il peut donc y avoir des erreurs.
std::string input_string("hello world");
subbuffer input(input_string);
subparser flds(input, ' ', subparser::SKIP_EMPTY);
while (!flds.empty())
{
subbuffer fld = flds.next();
// do something with fld
}
// or if you know it is only two fields
subbuffer fld1 = input.before(' ');
subbuffer fld2 = input.sub(fld1.length() + 1).ltrim(' ');
Tout d'abord, je dirais utiliser boost tokenizer.
Sinon, si vos données sont séparées par des espaces, la bibliothèque de flux de chaînes est très utile.
Mais les deux ci-dessus ont déjà été couverts.
Donc, comme troisième alternative C-Like, je propose de copier std :: string dans un tampon pour modification.
std::string data("The data I want to tokenize");
// Create a buffer of the correct length:
std::vector<char> buffer(data.size()+1);
// copy the string into the buffer
strcpy(&buffer[0],data.c_str());
// Tokenize
strtok(&buffer[0]," ");