Pergunta

Eu estou procurando um C ++ limpo maneira de analisar uma string contendo expressões embrulhados em $ {} e construir uma string de resultado das expressões programaticamente avaliados.

Exemplo: "Olá $ {user} de $ {host}" será avaliado para "foo Hi from bar" se eu executar o programa para deixar "user" avaliar a "foo", etc

.

A abordagem atual Estou pensando em consiste em uma máquina de estado que come um carácter de cada vez a partir da cadeia e avalia a expressão depois de atingir '}'. Quaisquer sugestões ou outras sugestões?

Nota: boost :: é muito bem-vindos! : -)

Atualização Obrigado pelas três primeiras sugestões! Infelizmente cometi o exemplo muito simples! Eu preciso ser capaz examinar o conteúdo dentro de $ {} por isso não é uma pesquisa simples e substituir. Talvez ele vai dizer $ {maiúsculas: foo} e, em seguida, eu tenho que usar "foo" como uma chave em um hashmap e convertê-la em maiúsculas, mas eu tentei evitar os detalhes internos de $ {} ao escrever a pergunta original acima ...: -)

Foi útil?

Solução

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

Eu vou ser feliz para explicar qualquer coisa sobre este código:)

Outras dicas

Como muitas expressões de avaliação não pretende ter? Se ele é pequeno o suficiente, você pode apenas querer usar a força bruta.

Por exemplo, se você tem um std::map<string, string> que vai desde o seu key à sua value, por exemplo user para Matt Cruikshank, você pode apenas querer interagir sobre todo o seu mapa e não uma simples substituição em sua seqüência de cada "${" + key + "}" à sua value .

boost :: Regex seria a rota que eu sugiro. A regex_replace algoritmo deve fazer a maior parte do seu trabalho pesado.

Se você não gosta minha primeira resposta, em seguida, cavar para impulsionar Regex - provavelmente boost :: regex_replace .

Como complexo pode expressões começar? São apenas identificadores, ou eles podem ser expressões reais como "$ {numBad / (double) total de 100,0 *}%"?

Você tem que usar o $ {e} delimitadores ou você pode usar outros delimitadores?

Você não realmente se preocupam com a análise. Você só quer gerar e cadeias de formato com dados de espaço reservado na mesma. Certo?

Para uma abordagem neutra plataforma, considere o função humilde sprintf. É a mais ubíqua e faz o que eu estou supondo que você precisa. Ele funciona em "estrelas char" de modo que você vai ter que entrar em algum gerenciamento de memória.

Você está usando STL? Então, considere o basic_string & substitua função. Ele não faz exatamente o que você quer, mas você poderia fazê-lo funcionar.

Se você estiver usando ATL / MFC, em seguida, considerar o CStringT :: Format método.

Se você estiver gerenciando as variáveis ??separadamente, por que não ir a rota de um intérprete embutido. Eu tenho usado tcl no passado, mas você pode tentar lua que é projetado para a incorporação. Rubi e Python são dois outros intérpretes incorporáveis ??que são fáceis de incorporar, mas não são tão leve. A estratégia é instanciar um intérprete (um contexto), adicionar variáveis ??a ele, em seguida, avaliar cordas dentro desse contexto. Um intérprete irá lidar corretamente a entrada malformado que pode levar a problemas de segurança ou de estabilidade para a sua aplicação.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top