C ++文字列内の式の評価:“ Hi $ {user} from $ {host}”
-
06-07-2019 - |
質問
$ {}でラップされた式を含む文字列を解析し、プログラムで評価された式から結果の文字列を作成する、きれいな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 メソッドを検討してください。