39 votes

Analyse de chaîne simple avec C ++

J'utilise C ++ depuis assez longtemps maintenant, mais j'ai néanmoins tendance à me rabattre sur scanf lorsque je dois analyser de simples fichiers texte. Par exemple, étant donné une configuration comme celle-ci (en supposant également que l'ordre des champs puisse varier):

 foo: [3 4 5]
baz: 3.0
 

J'écrirais quelque chose comme:

 char line[SOME_SIZE];
while (fgets(line, SOME_SIZE, file)) {
    int x, y, z;
    if (3 == sscanf(line, "foo: [%d %d %d]", &x, &y, &z)) {
        continue;
    }
    float w;
    if (1 == sscanf(line, "baz: %f", &w)) {
        continue;
    }
}
 

Quelle est la manière la plus concise d'y parvenir en C ++? Chaque fois que j'essaye, je me retrouve avec beaucoup de code d'échafaudage.

33voto

Nikko Points 2009

Il s'agit d'un essai utilisant uniquement le C ++ standard.

La plupart du temps, j'utilise une combinaison de std :: istringstream et std :: getline (qui peut fonctionner pour séparer les mots) pour obtenir ce que je veux. Et si je peux, je fais ressembler mes fichiers de configuration:

foo = 1,2,3,4

ce qui le rend facile.

le fichier texte est comme ceci:

 foo=1,2,3,4
bar=0
 

Et vous l'analysez comme ceci:

 int main()
{
    std::ifstream file( "sample.txt" );

    std::string line;
    while( std::getline( file, line ) )   
    {
        std::istringstream iss( line );

        std::string result;
        if( std::getline( iss, result , '=') )
        {
            if( result == "foo" )
            {
                std::string token;
                while( std::getline( iss, token, ',' ) )
                {
                    std::cout << token << std::endl;
                }
            }
            if( result == "bar" )
            {
               //...
    }
}
 

7voto

Thomas Petit Points 4100

Coup de pouce.L'esprit n'est pas réservé à analyser la structure complexe. Il est très bon au micro-analyse trop, et presque correspondre à la compacité de la C + scanf extrait :

#include <boost/spirit/include/qi.hpp>
#include <string>
#include <sstream>

using namespace boost::spirit::qi;


int main()
{
   std::string text = "foo: [3 4 5]\nbaz: 3.0";
   std::istringstream iss(text);

   std::string line;
   while (std::getline(iss, line))
   {
      int x, y, z;
      if(phrase_parse(line.begin(), line.end(), "foo: [">> int_ >> int_ >> int_ >> "]", space, x, y, z))
         continue;
      float w;
      if(phrase_parse(line.begin(), line.end(), "baz: ">> float_, space , w))
         continue;
   }
}

(Pourquoi ils n'ont pas d'ajouter un "conteneur" est au delà de moi, il serait bien plus pratique si on pouvait juste écrire :

if(phrase_parse(line, "foo: [">> int_ >> int_ >> int_ >> "]", space, x, y, z))
   continue;

Mais il est vrai que :

  • Il ajoute beaucoup de temps de compilation de frais généraux.
  • Les messages d'erreur sont brutaux. Si vous faites une petite erreur avec scanf, il suffit de lancer votre programme et obtenir immédiatement une erreur ou d'une absurde valeur analysée. Faire une petite erreur à l'esprit, et vous obtiendrez sans espoir gigantesque messages d'erreur du compilateur, et il faut BEAUCOUP de pratique avec boost.esprit pour les comprendre.

Donc, finalement, pour une analyse simple j'utilise scanf comme tout le monde...

3voto

Roger Dahl Points 8326

Les expressions régulières peuvent souvent être utilisées pour l'analyse de chaînes. Utiliser capture groups (entre parenthèses) pour obtenir les différentes parties de la ligne pour être analysée.

Par exemple, pour analyser une expression comme foo: [3 4 56], l'utilisation de l'expression (.*): \[(\d+) (\d+) (\d+)\]. Le premier groupe de capture contient "toto", les deuxième, troisième et quatrième contiendra les numéros 3, 4 et 56.

S'il y a plusieurs formats de chaîne qui doivent être analysées, comme dans l'exemple donné par les OP, soit appliquer séparer les expressions régulières, un par un, et de voir celui qui correspond à, ou écrire une expression régulière qui correspond à toutes les variations possibles, généralement à l'aide de l' | (ensemble de l'union) de l'opérateur.

Les expressions régulières sont très souples, de sorte que l'expression peut être prolongée pour permettre à plus de variations, par exemple, un nombre arbitraire d'espaces et d'autres espaces après l' : dans l'exemple. Ou pour autoriser uniquement les numéros de contenir un certain nombre de chiffres.

Comme un bonus supplémentaire, les expressions régulières fournir une validation implicite, car ils nécessitent un match parfait. Par exemple, si le nombre 56 dans l'exemple ci-dessus a été remplacé par 56x, le match serait un échec. Cela peut aussi simplifier le code comme dans l'exemple ci-dessus, les groupes contenant les nombres peuvent être en toute sécurité exprimées en nombres entiers sans aucune vérification supplémentaire étant nécessaire après un match de succès.

Les expressions régulières habitude de courir à la bonne performance et il y a beaucoup de bibliothèques de qualité, a choisi de partir. Par exemple, le coup de pouce.Regex.

1voto

skwllsp Points 9661

Je pense que Boost.Spirit est un bon moyen de décrire une grammaire directement dans votre code C ++. Il faut un certain temps pour s'habituer à Boost.Spirit mais après c'est assez facile à utiliser. Ce n'est peut-être pas aussi concis que vous le souhaitez, mais je pense que c'est un moyen pratique de gérer des grammaires simples.Ses performances peuvent être un problème, il est donc probable que dans les situations où vous avez besoin de vitesse, ce ne soit pas un bon choix.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X