Оценка выражений внутри строк C++: "Привет, $ {user} от $ {host}"

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

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

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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
2 116
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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

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

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

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

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

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

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

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

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

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

Извините, мне нужен этот формат. Что касается sprintf, я бы не контролировал строки формата, и использование вызова в стиле printf () со строкой формата, не являющейся литералом, было бы потенциально опасным.

activout.se 05.11.2008 00:03
Ответ принят как подходящий
#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();
}

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

Почему m_char <= m_length, а не m_char < m_length? Если строка заканчивается литералом, "lexme" завершается нулевым байтом.

activout.se 05.11.2008 23:46

Этот код не работает на cpp.sh. Требуются следующие изменения: прокомментируйте #include <conio.h> и _getch();, замените Token::E Token() const на Token::E Token_() const и exp.Token() на exp.Token_().

Elan Hickler 23.11.2015 01:33

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

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