C ++ 문자열 내부의 표현식 평가 :“$ {host}의 Hi $ {user}”
-
06-07-2019 - |
문제
나는 $ {}에 랩핑 된 표현식을 포함하는 문자열을 구문 분석하고 프로그래밍 방식으로 평가 된 표현식에서 결과 문자열을 구축하는 깨끗한 C ++ 방법을 찾고 있습니다.
예 : "$ {host}의 Hi $ {user}" ""foo "등으로"사용자 "를 평가하도록 프로그램을 구현하면"Hi foo from bar "로 평가됩니다.
내가 생각하는 현재 접근법은 문자열에서 한 번에 한 문자를 먹고 '}'에 도달 한 후 표현식을 평가하는 상태 기계로 구성됩니다. 힌트 나 기타 제안이 있습니까?
참고 : 부스트 :: 가장 환영합니다! :-)
업데이트 처음 세 가지 제안에 감사드립니다! 불행히도 나는 그 예를 너무 간단하게 만들었습니다! $ {} 내의 내용을 검사 할 수 있어야하므로 간단한 검색 및 교체가 아닙니다. 어쩌면 $ {대문자 : 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();
}
나는이 코드에 대해 무엇이든 설명하게되어 기쁩니다 :)
다른 팁
얼마나 많은 평가 표현이 의도합니까? 충분히 작다면 무차별 인 힘을 사용하고 싶을 수도 있습니다.
예를 들어, 당신이있는 경우 std::map<string, string>
그것은 당신의 것입니다 key
그것에 value
, 예를 들어 user
에게 Matt Cruikshank
, 당신은 당신의 전체지도를 반복하고 매번 줄을 간단하게 교체하고 싶을 수도 있습니다. "${" + key + "}"
그것에 value
.
부스트 :: Regex 내가 제안하는 경로가 될 것입니다. 그만큼 regex_replace 알고리즘은 대부분의 무거운 리프팅을 수행해야합니다.
내 첫 번째 대답이 마음에 들지 않으면 Regex를 부스트하기 위해 파헤칩니다. 부스트 :: regex_replace.
표현은 얼마나 복잡 할 수 있습니까? 그들은 단지 식별자입니까, 아니면 "$ {numbad/(double) Total*100.0}%"와 같은 실제 표현 일 수 있습니까?
$ {및} 구분 제를 사용해야합니까, 아니면 다른 구분자를 사용할 수 있습니까?
당신은 실제로 구문 분석에 관심이 없습니다. 자리 표시 자 데이터로 문자열을 생성하고 형식화하려고합니다. 오른쪽?
플랫폼 중립 접근법의 경우 겸손을 고려하십시오 Sprintf 기능. 그것은 가장 유비쿼터스이며 당신이 필요하다고 가정하는 일을합니다. 그것은 "char stars"에서 작동하므로 메모리 관리에 들어가야합니다.
STL을 사용하고 있습니까? 그런 다음 고려하십시오 BASIC_STRING 및 교체 기능. 그것은 당신이 원하는 것을 정확하게 수행하지 않지만 작동하게 할 수 있습니다.
ATL/MFC를 사용하는 경우 cstringt :: 형식 방법.