Domanda

Sto cercando un modo C ++ pulito per analizzare una stringa contenente espressioni racchiuse in $ {} e creare una stringa di risultato dalle espressioni valutate a livello di programmazione.

Esempio: " Ciao $ {utente} da $ {host} " verrà valutato a " Ciao foo dalla barra " se implemento il programma per lasciare " utente " valutare su "pippo", ecc.

L'approccio attuale a cui sto pensando è costituito da una macchina a stati che mangia un carattere alla volta dalla stringa e valuta l'espressione dopo aver raggiunto '}'. Eventuali suggerimenti o altri suggerimenti?

Nota: boost :: è il benvenuto! : -)

Aggiorna Grazie per i primi tre suggerimenti! Purtroppo ho reso l'esempio troppo semplice! Devo essere in grado di esaminare i contenuti entro $ {}, quindi non è una semplice ricerca e sostituzione. Forse dirà $ {maiuscolo: pippo} e poi dovrò usare " pippo " come chiave in una hashmap e poi convertirla in maiuscolo, ma ho cercato di evitare i dettagli interni di $ {} quando scrivevo la domanda originale sopra ... :-)

È stato utile?

Soluzione

#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 == '

Sarò felice di spiegare qualsiasi cosa su questo codice :)

) { 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 == '

Sarò felice di spiegare qualsiasi cosa su questo codice :)

) { 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(); }

Sarò felice di spiegare qualsiasi cosa su questo codice :)

Altri suggerimenti

Quante espressioni di valutazione intende avere? Se è abbastanza piccolo, potresti semplicemente voler usare la forza bruta.

Ad esempio, se hai un std :: map < string, string > che va dalla tua chiave al suo valore , per istanza da user a Matt Cruikshank , potresti semplicemente voler iterare sull'intera mappa e fare una semplice sostituzione sulla stringa di ogni " $ {" + chiave + "} " al suo valore .

Boost :: Regex sarebbe la strada che suggerirei. Il regex_replace l'algoritmo dovrebbe fare la maggior parte del lavoro pesante.

Se non ti piace la mia prima risposta, vai a Boost Regex - probabilmente boost :: regex_replace .

Quanto possono diventare complesse le espressioni? Sono solo identificatori o possono essere espressioni reali come " $ {numBad / (double) total * 100,0}% " ;?

Devi usare i delimitatori $ {e} o puoi usare altri delimitatori?

Non ti interessa davvero analizzare. Volete solo generare e formattare stringhe con i dati di segnaposto. Giusto?

Per un approccio neutrale rispetto alla piattaforma, considera l'umile funzione sprintf . È il più onnipresente e fa quello che presumo sia necessario. Funziona su " stelle speciali " quindi dovrai entrare nella gestione della memoria.

Stai usando STL? Quindi considera basic_string & amp; sostituisci . Non fa esattamente quello che vuoi ma potresti farlo funzionare.

Se si utilizza ATL / MFC, prendere in considerazione il metodo CStringT :: Format .

Se gestisci le variabili separatamente, perché non seguire la rotta di un interprete integrabile. Ho usato tcl in passato, ma potresti provare lua progettato per essere incorporato. Ruby e Python sono altri due interpreti incorporabili che sono facili da incorporare, ma non sono altrettanto leggeri. La strategia consiste nell'istanziare un interprete (un contesto), aggiungere variabili ad esso, quindi valutare le stringhe all'interno di quel contesto. Un interprete gestirà correttamente input non validi che potrebbero causare problemi di sicurezza o stabilità per la tua applicazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top