Evaluación de expresiones dentro de cadenas de C ++: & # 8220; Hola $ {usuario} de $ {host} & # 8221;

StackOverflow https://stackoverflow.com/questions/263339

Pregunta

Estoy buscando una forma limpia de C ++ de analizar una cadena que contiene expresiones envueltas en $ {} y construir una cadena de resultados a partir de las expresiones evaluadas mediante programación.

Ejemplo: " Hola $ {usuario} de $ {host} " será evaluado a "Hola foo de la barra" si implemento el programa para permitir "usuario" evaluar a "foo", etc.

El enfoque actual en el que estoy pensando consiste en una máquina de estados que come un carácter a la vez de la cadena y evalúa la expresión después de llegar a '}'. ¿Alguna sugerencia u otra sugerencia?

Nota: ¡boost :: es bienvenido! :-)

Actualización ¡Gracias por las tres primeras sugerencias! ¡Desafortunadamente hice el ejemplo demasiado simple! Necesito poder examinar el contenido dentro de $ {} para que no sea una simple búsqueda y reemplazo. Tal vez dirá $ {mayúscula: foo} y luego tengo que usar " foo " como una clave en un hashmap y luego convertirlo a mayúsculas, pero traté de evitar los detalles internos de $ {} al escribir la pregunta original anterior ... :-)

¿Fue útil?

Solución

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

Estaré encantado de explicar cualquier cosa sobre este código :)

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

Estaré encantado de explicar cualquier cosa sobre este código :)

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

Estaré encantado de explicar cualquier cosa sobre este código :)

Otros consejos

¿Cuántas expresiones de evaluación pretenden tener? Si es lo suficientemente pequeño, es posible que desee utilizar la fuerza bruta.

Por ejemplo, si tiene un std :: map < string, string > que va desde su key a su value , para instancia usuario a Matt Cruikshank , es posible que solo desee iterar sobre todo el mapa y hacer un reemplazo simple en su cadena de cada " $ {" + tecla + "} " a su valor .

Boost :: Regex sería la ruta que sugeriría. El regex_replace El algoritmo debería hacer la mayor parte de su trabajo pesado.

Si no le gusta mi primera respuesta, busque en Boost Regex - probablemente boost :: regex_replace .

¿Qué tan complejas pueden ser las expresiones? ¿Son solo identificadores, o pueden ser expresiones reales como " $ {numBad / (double) total * 100.0}% " ;?

¿Tiene que usar los delimitadores $ {y} o puede usar otros delimitadores?

Realmente no te importa analizar. Solo desea generar y formatear cadenas con datos de marcador de posición. ¿Cierto?

Para un enfoque neutral de plataforma, considere la humilde función sprintf . Es el más omnipresente y hace lo que supongo que necesita. Funciona en `` char stars '' así que tendrás que entrar en algo de administración de memoria.

¿Estás usando STL? Luego considere la cadena_básica & amp; reemplazar función. No hace exactamente lo que quieres, pero puedes hacer que funcione.

Si está utilizando ATL / MFC, considere el método CStringT :: Format .

Si está administrando las variables por separado, ¿por qué no seguir la ruta de un intérprete incorporable? He usado tcl en el pasado, pero puede intentar lua que está diseñado para incrustar. Ruby y Python son otros dos intérpretes integrables que son fáciles de integrar, pero que no son tan livianos. La estrategia es crear una instancia de un intérprete (un contexto), agregarle variables y luego evaluar cadenas dentro de ese contexto. Un intérprete manejará correctamente las entradas con formato incorrecto que podrían generar problemas de seguridad o estabilidad para su aplicación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top