Оценка выражений внутри строк C ++: & # 8220; Hi $ {пользователь} из $ {host} & # 8221;
-
06-07-2019 - |
Вопрос
Я ищу чистый способ 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 - два других встраиваемых интерпретатора, которые легко встраивать, но они не так легковесны. Стратегия состоит в том, чтобы создать экземпляр интерпретатора (контекст), добавить к нему переменные, а затем оценить строки в этом контексте. Интерпретатор будет правильно обрабатывать некорректные данные, которые могут привести к проблемам безопасности или стабильности вашего приложения.