评估C ++字符串中的表达式:“来自$ {host}的Hi $ {user}”
-
06-07-2019 - |
题
我正在寻找一种干净的C ++方法来解析包含$ {}中包含的表达式的字符串,并从以编程方式计算的表达式构建结果字符串。
示例:来自$ {host}的“Hi $ {user}”将被评估为“Hi foo from bar”如果我实施程序让“用户”评估为“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&lt; string,string&gt;
,它从 key
到 value
,实例 user
到 Matt Cruikshank
,您可能只想迭代整个地图并对每个&quot; $ {&quot;的字符串进行简单替换。 + key +&quot;}&quot;
到 value
。
Boost :: Regex 将是我建议的路线。 regex_replace 算法应该完成大部分繁重工作。
如果您不喜欢我的第一个答案,那么请深入了解Boost Regex - 可能是 boost :: regex_replace 。
表达式有多复杂?它们只是标识符,还是像“$ {numBad /(double)total * 100.0}%”这样的实际表达式?
您是否必须使用$ {和}分隔符,还是可以使用其他分隔符?
你真的不在乎解析。您只想生成包含占位符数据的字符串并对其进行格式化。正确?
对于平台中立的方法,请考虑简单的 sprintf 功能。它是最无处不在的,并且正如我所假设的那样。它适用于“char stars”。所以你将不得不进入一些内存管理。
您使用的是STL吗?然后考虑 basic_string&amp;替换功能。它并不完全符合您的要求,但您可以使其发挥作用。
如果您使用的是ATL / MFC,请考虑 CStringT :: Format 方法。