تقييم التعبيرات داخل سلاسل C++:"مرحبًا ${user} من ${host}"

StackOverflow https://stackoverflow.com/questions/263339

سؤال

أنا أبحث عن طريقة C++ نظيفة لتحليل سلسلة تحتوي على تعبيرات ملفوفة في ${} وإنشاء سلسلة نتائج من التعبيرات التي تم تقييمها برمجيًا.

مثال:سيتم تقييم "Hi ${user} from ${host}" إلى "Hi foo from bar" إذا قمت بتنفيذ البرنامج للسماح لـ "user" بالتقييم إلى "foo"، وما إلى ذلك.

يتكون النهج الحالي الذي أفكر فيه من آلة حالة تأكل حرفًا واحدًا في كل مرة من السلسلة وتقيم التعبير بعد الوصول إلى '}'.أي تلميحات أو اقتراحات أخرى؟

ملحوظة:يعزز::هو موضع ترحيب كبير!:-)

تحديث شكرا على الاقتراحات الثلاثة الأولى!لسوء الحظ جعلت المثال بسيطًا جدًا!أحتاج إلى أن أكون قادرًا على فحص المحتويات داخل ${}، لذا لا يكون البحث والاستبدال بسيطًا.ربما سيقول ${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();
}

سأكون سعيدًا بشرح أي شيء حول هذا الرمز :)

نصائح أخرى

كم عدد تعبيرات التقييم التي تنوي الحصول عليها؟إذا كانت صغيرة بما فيه الكفاية، قد ترغب فقط في استخدام القوة الغاشمة.

على سبيل المثال، إذا كان لديك std::map<string, string> الذي يذهب من الخاص بك key لها value, ، على سبيل المثال user ل Matt Cruikshank, ، قد ترغب فقط في التكرار على خريطتك بأكملها وإجراء استبدال بسيط على سلسلة كل منها "${" + key + "}" لها value.

تعزيز :: التعبير العادي سيكون الطريق الذي أقترحه.ال regex_replace يجب أن تقوم الخوارزمية بمعظم أعمالك الثقيلة.

إذا لم تعجبك إجابتي الأولى، فابحث في Boost Regex - على الأرجح دفعة::regex_replace.

ما مدى التعقيد الذي يمكن أن تصل إليه التعبيرات؟هل هي مجرد معرفات أم يمكن أن تكون تعبيرات فعلية مثل "${numBad/(double)total*100.0}%"؟

هل يتعين عليك استخدام المحددات ${ و } أم يمكنك استخدام محددات أخرى؟

أنت لا تهتم حقًا بالتحليل.أنت فقط تريد إنشاء سلاسل وتنسيقها باستخدام بيانات العنصر النائب.يمين؟

للحصول على نهج محايد للمنصة، فكر في المتواضع sprintf وظيفة.إنه الأكثر انتشارًا ويفعل ما أفترض أنك تحتاجه.إنه يعمل على "char Stars" لذا سيتعين عليك الدخول في بعض إدارة الذاكرة.

هل تستخدم المحكمة الخاصة بلبنان؟ثم فكر في basic_string&replace وظيفة.إنه لا يفعل بالضبط ما تريده ولكن يمكنك إنجاحه.

إذا كنت تستخدم ATL/MFC، ففكر في CStringT::تنسيق طريقة.

إذا كنت تدير المتغيرات بشكل منفصل، فلماذا لا تسلك طريق المترجم القابل للتضمين.لقد استخدمت تى سى ال في الماضي، ولكن يمكنك أن تحاول لوا الذي تم تصميمه للتضمين. روبي و بايثون هناك مترجمان فوريان آخران قابلان للتضمين يسهل تضمينهما، لكنهما ليسا خفيفين الوزن.تتمثل الإستراتيجية في إنشاء مثيل لمترجم (سياق)، وإضافة متغيرات إليه، ثم تقييم السلاسل ضمن هذا السياق.سيتعامل المترجم بشكل صحيح مع المدخلات المشوهة التي قد تؤدي إلى مشاكل تتعلق بالأمان أو الاستقرار لتطبيقك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top