質問

$ {}でラップされた式を含む文字列を解析し、プログラムで評価された式から結果の文字列を作成する、きれいなC ++の方法を探しています。

例:"こんにちは$ {user} from $ {host}" " Hi foo from bar"と評価されます。 「ユーザー」を許可するプログラムを実装した場合" foo"などに評価します。

私が考えている現在のアプローチは、文字列から一度に1文字を食べ、 '}'に達した後に式を評価するステートマシンで構成されています。ヒントやその他の提案はありますか?

注:boost ::は大歓迎です! :-)

更新最初の3つの提案をありがとう!残念ながら、この例は単純すぎました! $ {}内の内容を調べることができるようにする必要があるため、単純な検索と置換ではありません。たぶん$ {uppercase:foo}と表示されるので、" foo"を使用する必要があります。ハッシュマップのキーとして、大文字に変換しますが、上記の元の質問を書くときに$ {}の内部の詳細を避けようとしました...:-)

役に立ちましたか?

解決

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

このコードについて何か説明させていただきます:)

他のヒント

いくつの評価式を使用するつもりですか?十分に小さい場合は、ブルートフォースを使用することもできます。

たとえば、 key から value に移動する std :: map&lt; string、string&gt; がある場合、インスタンス user Matt Cruikshank に変更する場合は、マップ全体を繰り返し処理し、すべての&quot; $ {&quot; +キー+&quot;}&quot; value に。

Boost :: Regex が提案するルートです。 regex_replace アルゴリズムはあなたの大部分を持ち上げます。

最初の回答が気に入らない場合は、Boost Regexを掘り下げてください。おそらく boost :: regex_replace

式はどの程度複雑になりますか?それらは単なる識別子ですか、それとも&quot; $ {numBad /(double)total * 100.0}%&quot;のような実際の式ですか?

$ {および}区切り文字を使用する必要がありますか、それとも他の区切り文字を使用できますか?

解析はあまり気にしません。プレースホルダーデータを含む文字列を生成してフォーマットするだけです。そうですか

プラットフォームに依存しないアプローチの場合、控えめな sprintf 関数を検討してください。それは最も遍在的であり、あなたが必要だと私が仮定していることをします。 「charstars」で動作します。そのため、メモリ管理を行う必要があります。

STLを使用していますか?次に、 basic_string&amp;置換機能。望みどおりに動作しませんが、動作させることができます。

ATL / MFCを使用している場合は、 CStringT :: Format メソッドを検討してください。

変数を個別に管理する場合は、埋め込み可能なインタープリターのルートを選択してください。過去に tcl を使用しましたが、 lua は埋め込み用に設計されています。 Ruby および Python は、埋め込みが容易ですが、それほど軽量ではない2つの埋め込み可能なインタープリターです。戦略は、インタープリター(コンテキスト)をインスタンス化し、それに変数を追加し、そのコンテキスト内の文字列を評価することです。インタープリターは、アプリケーションのセキュリティまたは安定性の問題につながる可能性のある不正な入力を適切に処理します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top