Utilisation de strtok avec une chaîne std::string

j'ai une chaîne que je tiens à marquer. Mais le C strtok() fonction nécessite à ma chaîne pour être un char*. Comment puis-je faire cela simplement?

j'ai essayé:

token = strtok(str.c_str(), " "); 

qui échoue parce qu'il le transforme en const char*, pas char*

41
demandé sur Flexo 2008-11-14 09:14:46

10 réponses

#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 le boost pour plus de flexibilité.

59
répondu Chris Blackwell 2016-08-19 12:25:14
  1. Si boost est disponible sur votre système (je pense que c'est standard sur la plupart des distributions Linux de nos jours), il a un Tokenizer classe que vous pouvez utiliser.

  2. si ce N'est pas le cas, alors un petit Google fait un tokenizer roulé à la main pour std:: string que vous pouvez probablement simplement copier et coller. C'est très court.

  3. et, si vous n'aimez pas l'un ou l'autre de ceux-ci, alors voici une fonction split() que j'ai écrite pour faire mon la vie plus facile. Il cassera une corde en morceaux en utilisant n'importe laquelle des pièces de "delim" comme séparateurs. Les pièces sont ajoutées à la "parties" vecteur:

    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));
        }
      }
    }
    
20
répondu tgamblin 2008-11-14 06:42:35

Dupliquer la chaîne, marquer, puis gratuit.

char *dup = strdup(str.c_str());
token = strtok(dup, " ");
free(dup);
15
répondu DocMax 2008-11-14 06:22:42

Il y a une solution plus élégante.

Avec std:: string, vous pouvez utiliser resize() pour allouer un buffer suffisamment grand, et &s[0] pour obtenir un pointeur vers le buffer interne.

à ce point, beaucoup de gens bien vont sauter et crier sur l'écran. Mais c'est la réalité. Il y a environ 2 ans!--2-->

le groupe de travail bibliothèque a décidé (réunion à Lillehammer) que tout comme pour std::vector, std:: string devrait aussi formellement, pas seulement dans la pratique, avoir une garantie mémoire tampon contiguë.

l'autre problème est does strtok() augmente la taille de la chaîne. La documentation du MSDN dit:

Chaque appel à strtok modifie strToken en insérant un caractère nul après le jeton retourné par cet appel.

Mais ce n'est pas correct. En fait la fonction remplace les premier occurrence d'un caractère de séparateur avec \0. Aucun changement dans la taille de la corde. Si on a ça chaîne de caractères:

un-deux-trois-quatre

nous finirons avec

un\0two\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

5
répondu Martin Dimitrov 2009-10-20 08:24:34

EDIT: l'usage de const cast est utilisé pour démontrer l'effet de strtok() lorsqu'il est appliqué à un pointeur retourné par string::c_str().

Vous ne devrait pas utiliser strtok() puisqu'il modifie la chaîne tokenisée qui peut conduire à un comportement indésirable, sinon indéfini, comme la chaîne C "appartient" à l'instance de la 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é "enlevé" de la chaîne, ou refusé à un non-imprimable caractère, mais la longueur reste inchangée.

>./a.out
hello world - 11
helloworld - 11

vous devez donc recourir au mécanisme natif, à la duplication de la chaîne ou à une bibliothèque tierce comme mentionné précédemment.

2
répondu philant 2008-12-01 08:25:04

je suppose que le langage est C, ou c++...

strtok, IIRC, remplacer les séparateurs par \0. C'est ce qu'il ne peut pas utiliser un const string. Pour contourner ce problème, que "rapidement", si la chaîne n'est pas énorme, vous pouvez simplement strdup (). Ce qui est sage si vous avez besoin de garder la corde non altérée (ce que le const suggère...).

d'un autre côté, vous pourriez vouloir utiliser un autre tokenizer, peut-être roulé à la main, moins violent sur l'argument donné.

1
répondu PhiLho 2008-11-14 06:23:30

en supposant que par "string" vous parlez de std:: string en C++, vous pourriez avoir un regard sur le Tokenizer package Boost.

1
répondu Sherm Pendley 2008-11-14 06:29:09

Il échoue parce que str.c_str() renvoie la chaîne constante mais char * strtok (char * str, const char * delimiters ) nécessite une chaîne volatile. Si vous avez besoin d'utiliser * const_cast < char > afin de 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;
}
1
répondu Satendra 2017-01-03 12:50:13

tout d'abord, je dirais utiliser tokenizer boost.

Si vos données sont séparées par un espace, alors la bibliothèque string stream est très utile.

Mais les deux ci-dessus ont déjà été abordés.

Ainsi, comme troisième alternative de type C, je propose de copier la chaîne std::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]," ");
0
répondu Martin York 2008-11-14 10:05:48

si vous ne vous opposez pas à open source, vous pouvez utiliser les classes subbuffer et subparser de https://github.com/EdgeCast/json_parser