문제

나는 $ {}에 랩핑 된 표현식을 포함하는 문자열을 구문 분석하고 프로그래밍 방식으로 평가 된 표현식에서 결과 문자열을 구축하는 깨끗한 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 :: 형식 방법.

변수를 별도로 관리하는 경우 임베드 가능한 통역사의 경로를 사용해보십시오. 나는 사용했다 TCL 과거에는하지만 시도 할 수도 있습니다 루아 임베딩을 위해 설계되었습니다. 루비 그리고 파이썬 임베드가 쉽지만 가볍지 않은 두 개의 다른 임베드 가능한 통역사입니다. 전략은 통역사 (컨텍스트)를 인스턴스화하고 변수를 추가 한 다음 해당 컨텍스트 내에서 문자열을 평가하는 것입니다. 통역사는 신청에 대한 보안 또는 안정성 문제로 이어질 수있는 기형 입력을 올바르게 처리합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top