2012-10-06 7 views
1

J'ai un flux d'entrée contenant des entiers et des caractères de signification spéciaux "#". Il se présente comme suit: ... 12 18 16 # 22 24 26 15 # 17 # 32 35 33 ... Les jetons sont séparés par un espace. Il n'y a pas de modèle pour la position de "#".Tokenize stringstream basé sur le type

je tentais de tokenizer le flux d'entrée comme ceci:

int value; 
std::ifstream input("data"); 
if (input.good()) { 
    string line; 
    while(getline(data, line) != EOF) { 
    if (!line.empty()) { 
     sstream ss(line); 
     while (ss >> value) { 
     //process value ... 

     } 
    } 
    } 
} 

Le problème avec ce code est que le traitement s'arrête lorsque le premier « # » est rencontré.

La seule solution que je peux penser est d'extraire chaque jeton individuel dans une chaîne (pas '#') et utiliser la fonction atoi() pour convertir la chaîne en entier. Cependant, c'est très inefficace car les jetons majoritaires sont entiers. Appeler atoi() sur les jetons introduit de gros frais généraux.

Y a-t-il un moyen d'analyser le jeton individuel par son type? c'est-à-dire, pour les entiers, l'analyser comme des entiers alors que pour '#', l'ignorer. Merci!

+1

est d'utiliser getline acccepable deux fois? si c'est le cas, utilisez 'getline (data, line, '#');' fisrt. – andre

+0

@ahenderson Je n'ai pas compris votre point. L'une des signatures de fonction de getline est: istream & getline (istream & is, chaîne & str, char delim); En passant '#' comme troisième argument de getline, getline() prendra '#' comme délimiteur. – itnovice

+0

J'ai posté une réponse. – andre

Répondre

2

Une possibilité serait d'ignorer explicitement les espaces (ss >> std::ws), puis d'utiliser ss.peek() pour savoir si un # suit. Si oui, utilisez ss.get() pour le lire et continuer, sinon utilisez ss >> value pour lire la valeur. Si les positions de # ne comptent pas, vous pouvez également supprimer tous les '#' de la ligne avant d'initialiser le stringstream avec lui.

+0

Merci. Votre solution regarde toujours vers l'avant, pour '#', utilisez ss.get() pour la consommer et continuer. Si le '#' peut être d'autres caractères non numériques, pouvons-nous avoir une solution plus générale? – itnovice

+0

@itnovice: Si vos nombres sont tous des entiers positifs (c'est-à-dire composés uniquement des chiffres '0' à' 9'), vous pouvez simplement passer à 'isdigit'. Si c'est un chiffre, vous pouvez continuer à lire la valeur, sinon vous savez que c'est un caractère non numérique. Vous pouvez également utiliser le test 'next <'0' || next> '9' (où 'next' contient le résultat de' peek') pour identifier les non-chiffres. – celtschk

0

Personnellement, si votre séparateur est toujours va être l'espace indépendamment de ce qui suit, je vous recommande de prendre l'entrée comme une chaîne et analyser à partir de là. De cette façon, vous pouvez prendre la chaîne, voir si c'est un nombre ou un # et autres joyeusetés.

1

Peut-être que vous pouvez lire toutes les valeurs que std :: string et vérifier si elle est « # » ou non (et sinon - convertir en int)

0

Je pense que vous devriez revoir votre prémisse que « Appel atoi() sur les jetons introduit de gros frais généraux- "

Il n'y a pas de magie à std::cin >> val. Sous le capot, il finit par appeler (quelque chose de très similaire à) atoi.

Si vos jetons sont énormes, il peut y avoir des frais généraux pour créer un std::string mais comme vous le dites, la grande majorité sont des nombres (et le reste sont des #), ils devraient donc être généralement courts.

+0

Je n'étais pas au courant que std :: cin >> val finit par appeler une fonction similaire à atoi(). Donc, mon hypothèse était fausse. Merci de l'avoir signalé. – itnovice

1
int value; 
std::ifstream input("data"); 
if (input.good()) { 
    string line; 
    std::sstream ss(std::stringstream::in | std::stringstream::out); 
    std::sstream ss2(std::stringstream::in | std::stringstream::out); 
    while(getline(data, line, '#') { 
     ss << line; 
     while(getline(ss, line, ' ') { 
      ss2 << line; 
      ss2 >> value 
      //process values ... 
      ss2.str(""); 
     } 
     ss.str(""); 
    } 
} 

Ici nous avons divisé la ligne par le jeton « # » dans la première boucle while puis dans la seconde boucle while nous diviser la ligne par « ».

+0

Je n'ai pas de compilateur C++ sur cette machine o le code n'est pas test. Donc, utilisez ceci comme un aperçu général de la façon dont cela devrait fonctionner. – andre

+0

Je vois ce que vous voulez dire. C'est une solution intelligente. Merci – itnovice

+0

@itnovice petite erreur j'ai eu 'ss >> valeur' qui devrait être ss2 >> valeur – andre

2

Habituellement ne vaut pas l'essai contre le bien()

if (input.good()) { 

À moins que votre prochaine opération génère un message d'erreur ou d'exception. Si ce n'est pas bon, toutes les autres opérations échoueront de toute façon.

Ne pas tester contre EOF.

while (getline (données, ligne)!= EOF) {

Le résultat de std :: getline() n'est pas un entier. C'est une référence au flux d'entrée. Le flux d'entrée est convertible en un objet semblable à un bool qui peut être utilisé dans un contexte (comme whileif etc.). Donc ce que vous voulez faire:

while(getline(data, line)) { 

Je ne suis pas sûr de lire une ligne. Vous pourriez simplement lire un mot (puisque l'entrée est séparée par un espace). Utilisation de l'opérateur >> sur la chaîne

std::string word; 
while(data >> word) { // reads one space separated word 

Maintenant, vous pouvez tester le mot pour voir si elle est votre caractère spécial:

if (word[0] == "#") 

Si le mot ne convertit pas en nombre.

C'est ce que je ferais:

// define a class that will read either value from a stream 
class MyValue 
{ 
    public: 
    bool isSpec() const {return isSpecial;} 
    int value() const {return intValue;} 

    friend std::istream& operator>>(std::istream& stream, MyValue& data) 
    { 
     std::string item; 
     stream >> item; 
     if (item[0] == '#') { 
      data.isSpecial = true; 
     } else 
     { data.isSpecial = false; 
      data.intValue = atoi(&item[0]); 
     } 
     return stream; 
    } 
    private: 
    bool isSpecial; 
    int intValue; 
}; 

// Now your loop becomes: 
MyValue val; 
while(file >> val) 
{ 
    if (val.isSpec()) { /* Special processing */ } 
    else    { /* We have an integer */ } 
} 
+0

Votre solution est très agréable. Je vous remercie! – itnovice