2008-11-04 17 views
4

Je recherche une méthode C++ propre pour analyser une chaîne contenant des expressions enveloppées dans $ {} et générer une chaîne de résultat à partir de l'évaluation par programmation expressions.

Exemple: "Salut $ {user} de $ {host}" sera évalué à "Salut foo bar" si j'exécuter le programme de laisser "utilisateur" d'évaluer à "foo", etc.

L'approche actuelle à laquelle je pense consiste en une machine d'état qui mange un caractère à la fois à partir de la chaîne et évalue l'expression après avoir atteint '}'. Des conseils ou d'autres suggestions?

Note: boost :: est le bienvenu! :-)

Mise à jour Merci pour les trois premières suggestions! Malheureusement, j'ai rendu l'exemple trop simple! Je dois pouvoir examiner le contenu dans $ {} afin que ce ne soit pas une simple recherche et un remplacement. Peut-être dira-t-il $ {majuscule: foo} et ensuite je devrais utiliser "foo" comme une clé dans un hashmap et ensuite le convertir en majuscule, mais j'ai essayé d'éviter les détails intérieurs de $ {} ... :-)

Répondre

5
#include <iostream> 
#include <conio.h> 
#include <string> 
#include <map> 

using namespace std; 

struct Token 
{ 
    enum E 
    { 
     Replace, 
     Literal, 
     Eos 
    }; 
}; 

class ParseExp 
{ 
private: 
    enum State 
    { 
     State_Begin, 
     State_Literal, 
     State_StartRep, 
     State_RepWord, 
     State_EndRep 
    }; 

    string   m_str; 
    int    m_char; 
    unsigned int m_length; 
    string   m_lexme; 
    Token::E  m_token; 
    State   m_state; 

public: 
    void Parse(const string& str) 
    { 
     m_char = 0; 
     m_str = str; 
     m_length = str.size(); 
    } 

    Token::E NextToken() 
    { 
     if (m_char >= m_length) 
      m_token = Token::Eos; 

     m_lexme = ""; 
     m_state = State_Begin; 
     bool stop = false; 
     while (m_char <= m_length && !stop) 
     { 
      char ch = m_str[m_char++]; 
      switch (m_state) 
      { 
      case State_Begin: 
       if (ch == '$') 
       { 
        m_state = State_StartRep; 
        m_token = Token::Replace; 
        continue; 
       } 
       else 
       { 
        m_state = State_Literal; 
        m_token = Token::Literal; 
       } 
       break; 

      case State_StartRep: 
       if (ch == '{') 
       { 
        m_state = State_RepWord; 
        continue; 
       } 
       else 
        continue; 
       break; 

      case State_RepWord: 
       if (ch == '}') 
       { 
        stop = true; 
        continue; 
       } 
       break; 

      case State_Literal: 
       if (ch == '$') 
       { 
        stop = true; 
        m_char--; 
        continue; 
       } 
      } 

      m_lexme += ch; 
     } 

     return m_token; 
    } 

    const string& Lexme() const 
    { 
     return m_lexme; 
    } 

    Token::E Token() const 
    { 
     return m_token; 
    } 
}; 

string DoReplace(const string& str, const map<string, string>& dict) 
{ 
    ParseExp exp; 
    exp.Parse(str); 
    string ret = ""; 
    while (exp.NextToken() != Token::Eos) 
    { 
     if (exp.Token() == Token::Literal) 
      ret += exp.Lexme(); 
     else 
     { 
      map<string, string>::const_iterator iter = dict.find(exp.Lexme()); 
      if (iter != dict.end()) 
       ret += (*iter).second; 
      else 
       ret += "undefined(" + exp.Lexme() + ")"; 
     } 
    } 
    return ret; 
} 

int main() 
{ 
    map<string, string> words; 
    words["hello"] = "hey"; 
    words["test"] = "bla"; 
    cout << DoReplace("${hello} world ${test} ${undef}", words); 
    _getch(); 
} 

Je serai heureux de vous expliquer quoi que ce soit à propos de ce code :)

+0

Pourquoi 'm_char <= m_length' et non' m_char

+0

Ce code ne fonctionne pas sur cpp.sh. Nécessite les changements suivants: commenter '#include ' et '_getch();', changer 'Token :: E Token() const' en' Token :: E Token_() const' et 'exp.Token()' 'exp.Token _()' –

0

Combien d'expressions d'évaluation souhaitez-vous avoir? Si c'est assez petit, vous pouvez juste utiliser la force brute.

Par exemple, si vous avez un std::map<string, string> qui va de votre key à son value, par exemple user-Matt Cruikshank, vous pourriez vouloir itérer sur votre carte entière et faire un simple remplacer sur votre chaîne de chaque "${" + key + "}" à sa value.

0

Si vous n'aimez pas ma première réponse, alors creusez dans Boost Regex - probablement boost::regex_replace.

0

Quelle est la complexité des expressions? Sont-ils simplement des identifiants, ou peuvent-ils être des expressions réelles comme "$ {numBad/(double) total * 100.0}%"?

0

Avez-vous besoin d'utiliser les délimiteurs $ {et} ou pouvez-vous utiliser d'autres délimiteurs?

Vous ne vous souciez pas vraiment de l'analyse syntaxique. Vous souhaitez simplement générer et mettre en forme des chaînes avec des données d'espace réservé. Droite?

Pour une approche plate-forme neutre, considérez l'humble sprintf fonction. C'est le plus omniprésent et fait ce que je suppose que vous avez besoin. Cela fonctionne sur les "étoiles char" donc vous allez devoir entrer dans la gestion de la mémoire.

Utilisez-vous STL? Ensuite, considérez le basic_string & remplacez la fonction. Il ne fait pas exactement ce que vous voulez, mais vous pouvez le faire fonctionner. Si vous utilisez ATL/MFC, prenez en compte la méthode CStringT :: Format.

+0

Désolé, j'ai besoin de ce format. En ce qui concerne sprintf, je ne contrôlerais pas les chaînes de format et l'utilisation d'un appel printf() avec une chaîne de format qui n'est pas un littéral serait potentiellement dangereuse. –

0

Si vous gérez les variables séparément, pourquoi ne pas la route d'un interprète intégrable. J'ai utilisé tcl dans le passé, mais vous pouvez essayer lua qui est conçu pour l'intégration. Ruby et Python sont deux autres interpréteurs intégrables faciles à intégrer, mais qui ne sont pas aussi légers. La stratégie consiste à instancier un interpréteur (un contexte), à ​​y ajouter des variables, puis à évaluer les chaînes dans ce contexte. Un interprète gérera correctement les entrées mal formées pouvant entraîner des problèmes de sécurité ou de stabilité pour votre application.