Оценка выражений внутри строк C ++: & # 8220; Hi $ {пользователь} из $ {host} & # 8221;

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

Вопрос

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

Пример: " Привет, $ {пользователь} из $ {хоста} " будет оцениваться как "Привет foo из бара" если я реализую программу, чтобы позволить "пользователю" оценить как "foo" и т. д.

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

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

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

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top