我正在寻找一种干净的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 方法。

如果您分别管理变量,为什么不去嵌入式解释器的路径。我过去曾使用 tcl ,但您可以尝试 lua ,用于嵌入。 Ruby Python 是另外两个易于嵌入的嵌入式解释器,但不是那么轻量级。策略是实例化解释器(上下文),向其添加变量,然后评估该上下文中的字符串。解释器将正确处理格式错误的输入,这可能会导致应用程序出现安全性或稳定性问题。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top