Оценка выражений в строках C ++: «Привет, $ {user} from $ {host}» - PullRequest
4 голосов
/ 04 ноября 2008

Я ищу чистый способ C ++ для анализа строки, содержащей выражения, заключенные в $ {}, и построения строки результата из выражений, полученных программным способом.

Пример: «Hi $ {user} from $ {host}» будет оцениваться как «Hi foo from bar», если я реализую программу, чтобы позволить «user» вычислить значение «foo» и т. Д.

Текущий подход, о котором я думаю, состоит из конечного автомата, который ест один символ за раз из строки и оценивает выражение после достижения '}'. Любые советы или другие предложения?

Примечание: boost :: приветствуется! : -)

Обновление Спасибо за первые три предложения! К сожалению, я сделал пример слишком простым! Мне нужно уметь проверять содержимое в $ {}, так что это не простой поиск и замена. Возможно, он скажет $ {uppercase: foo}, а затем мне придется использовать «foo» в качестве ключа в хэш-карте, а затем преобразовать его в верхний регистр, но я пытался избежать внутренних деталей $ {} при написании исходного вопроса выше ...: -)

Ответы [ 7 ]

5 голосов
/ 05 ноября 2008
#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();
}

Я с удовольствием объясню что-нибудь об этом коде:)

0 голосов
/ 05 ноября 2008

Если вы управляете переменными отдельно, почему бы не пойти по пути встраиваемого интерпретатора. Я использовал tcl в прошлом, но вы можете попробовать lua , который предназначен для встраивания. Ruby и Python - два других встраиваемых интерпретатора, которые легко встраивать, но они не так легковесны. Стратегия состоит в том, чтобы создать экземпляр интерпретатора (контекст), добавить к нему переменные, а затем оценить строки в этом контексте. Интерпретатор будет правильно обрабатывать некорректные данные, которые могут привести к проблемам безопасности или стабильности вашего приложения.

0 голосов
/ 04 ноября 2008

Нужно ли использовать разделители $ {и} или вы можете использовать другие разделители?

Тебя не волнует разбор. Вы просто хотите генерировать и форматировать строки с данными заполнителя в нем. Правильно?

Для подхода, нейтрального к платформе, рассмотрим скромную функцию sprintf . Это самый вездесущий и делает то, что я предполагаю, что вам нужно. Он работает на «звездочках», поэтому вам придется немного поработать с памятью.

Вы используете STL? Затем рассмотрим функцию basic_string & replace . Он не делает то, что вы хотите, но вы можете заставить его работать.

Если вы используете ATL / MFC, рассмотрите метод CStringT :: Format .

0 голосов
/ 04 ноября 2008

Насколько сложными могут быть выражения? Являются ли они просто идентификаторами или могут быть действительными выражениями, такими как "$ {numBad / (double) total * 100.0}%"?

0 голосов
/ 04 ноября 2008

Если вам не нравится мой первый ответ, тогда загляните в Boost Regex - возможно, boost :: regex_replace .

0 голосов
/ 04 ноября 2008

Boost :: Regex будет маршрут, который я бы предложил. Алгоритм regex_replace должен выполнять большую часть вашей тяжелой работы.

0 голосов
/ 04 ноября 2008

Сколько оценочных выражений намеревается иметь? Если он достаточно мал, вы можете использовать грубую силу.

Например, если у вас есть std::map<string, string>, который идет от вашего key до value, например user до Matt Cruikshank, вы можете просто перебрать всю карту и сделать простую замените на вашей строке каждый "${" + key + "}" на value.

...