4

我正在寻找一种干净的 C++ 方法来解析包含包装在 ${} 中的表达式的字符串,并从以编程方式评估的表达式构建结果字符串。

示例:如果我实现程序让“user”评估为“foo”等,“来自 ${host} 的 Hi ${user}”将被评估为“来自 bar 的 Hi foo”等。

我正在考虑的当前方法包括一个状态机,它一次从字符串中读取一个字符,并在达到“}”后评估表达式。任何提示或其他建议?

注意: boost:: 是最受欢迎的!:-)

更新感谢前三个建议!不幸的是,我的例子太简单了!我需要能够检查 ${} 中的内容,所以这不是简单的搜索和替换。也许它会说 ${uppercase:foo} 然后我必须使用“foo”作为哈希映射中的键,然后将其转换为大写,但是在编写上面的原始问题时,我试图避免 ${} 的内部细节... :-)

4

7 回答 7

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

我很乐意解释有关此代码的任何内容:)

于 2008-11-04T21:01:08.507 回答
0

打算有多少个评估表达式?如果它足够小,您可能只想使用蛮力。

例如,如果您有一个std::map<string, string>从 yourkey到 its 的a value,例如userto Matt Cruikshank,您可能只想遍历整个地图并简单地替换您的字符串 every"${" + key + "}"到 its value

于 2008-11-04T20:50:28.030 回答
0

Boost::Regex将是我建议的路线。regex_replace算法应该可以完成大部分繁重的工作。

于 2008-11-04T20:50:35.050 回答
0

如果您不喜欢我的第一个答案,请深入研究 Boost Regex - 可能是boost::regex_replace

于 2008-11-04T20:52:27.027 回答
0

表达式可以变得多复杂?它们只是标识符,还是像“${numBad/(double)total*100.0}%”这样的实际表达式?

于 2008-11-04T20:56:34.010 回答
0

您必须使用 ${ 和 } 分隔符还是可以使用其他分隔符?

你并不真正关心解析。您只想生成并格式化其中包含占位符数据的字符串。对?

对于平台中立的方法,请考虑不起眼的sprintf函数。它是最普遍的,并且可以满足我假设您的需要。它适用于“char stars”,因此您将不得不进行一些内存管理。

你在使用 STL 吗?然后考虑basic_string& 替换函数。它并不能完全满足您的要求,但您可以使其发挥作用。

如果您使用 ATL/MFC,请考虑CStringT::Format方法。

于 2008-11-04T20:56:45.990 回答
0

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

于 2008-11-04T21:18:53.713 回答