Регулярное выражение — границы слов не работают

Подводя итог: как я могу предотвратить то, что мой шаблон регулярного выражения ошибочно принимает сегменты строк за имя переменной из целого слова? Он заменяет буквы, которые являются частью большего слова, хотя я использую границы слов \b.

Что я пытаюсь сделать: я работаю над калькулятором. У него есть список переменных, и перед тем, как передать выражение парсеру, я вызываю свою функцию ParseVars(), чтобы выполнить regex_search, используя шаблон для сопоставления переменных. Как только у него есть все токены, соответствующие моему шаблону переменной, я проверяю, действительно ли эта строка находится в списке имен переменных, и если да, я заменяю строку значением переменных. Также каждый раз, когда в парсере производится вычисление, я определяю константу с именем ans1, ans2 и так далее.

Проблема в следующем: допустим, у меня есть определенная переменная с именем a и ее значением является 6. (Кстати, я отслеживаю их в map<string,double> Vars;. Когда я делаю ParseVars("ans1"), результирующая строка равна "ans1". Также с ParseVar() строка ans1+ans2+9 остается прежней. Строка 9+a становится 9+6. Итак, пока мое регулярное выражение работает так, как ожидалось.

НО, если я сделаю ParseVars("ans1+a"), результирующая строка будет "6ns1+6". Я не понимаю, почему границы слов в моем регулярном выражении терпят неудачу только в том случае, если я использую переменную, «a» всегда можно найти в «ans1», но она заменяется только в том случае, если «a» находится где-то еще в строке. .

Что у меня есть. Вот мой шаблон регулярного выражения: \b([a-z][a-z0-9_]*)\b Разве это не должно соответствовать только целым словам? Граница слова работает нормально до тех пор, пока в строке не будет стоять одна буква «а». Возможно, это моя функция ParseVars(), вот код:

map<string,double> Vars;

// Variables must be a whole word, start with a letter, and
// optionally have other letters, numbers, and underscores.
sregex VarPattern = sregex::compile("\\b([a-z][a-z0-9_]*)\\b");

string Calculator::ParseVars(string expr) {
    if (Vars.empty()) return expr;

    string newExpr = StrToLower(expr);
    const sregex_iterator End;

    // Loop through all possible variable matches
    for (sregex_iterator i(expr.begin(), expr.end(), VarPattern); i != End; ++i) {
        string name = (*i)[0];

        // If it is a variable
        if (Vars.find(name) != Vars.end()) {
            int rPos = 0;

            // Replace all occurrences of it
            while ((rPos = newExpr.find(name, rPos)) != string::npos) {
                newExpr.replace(
                    rPos, name.length(),
                    lexical_cast<string,double>(Vars[name])
                );
            }
        }
    }

    return newExpr;
}

Если a равно 6, как я могу предотвратить превращение ans1+a в 6ns1+6 вместо желаемого ans1+6?


person Brandon Miller    schedule 03.10.2012    source источник
comment
Кажется, вы не совсем понимаете, как работают границы слов. \b соответствует между буквенно-цифровым символом ([A-Za-z0-9_]) и либо не-alnum символом, либо началом/концом строки. Таким образом, в строке ans1+a \b совпадают в следующих позициях, отмеченных |: |ans1|+|a|.   -  person Tim Pietzcker    schedule 03.10.2012
comment
+1 за разъяснение. Хотя, по сути, я так и думал. Просто, когда я получил совпадения с регулярными выражениями, я использовал базовую замену строки вместо regex_replace, а метод string.replace() не знает границ слов. Поэтому он пошел дальше и заменил a в ans1 на 6, хотя ans1 никогда не совпадало.   -  person Brandon Miller    schedule 03.10.2012


Ответы (1)


Что ж, я нашел решение. Я помещаю свой ответ здесь для всех, кто столкнулся с подобной проблемой.

Проблема заключалась в том, что я использовал базовую замену строки ПОСЛЕ того, как регулярное выражение совпало, поэтому границы слов работали, просто функция замены строки заменяла каждое вхождение строки независимо от границ слова. Я должен использовать regex_replace(), вот что у меня получилось:

map<string,double> Vars;

// Variables must be a whole word, start with a letter, and
// optionally have other letters, numbers, and underscores.
sregex VarPattern = sregex::compile("\\b([a-z][a-z0-9_]*)\\b");

string Calculator::ParseVars(string expr) {
    if (Vars.empty()) return expr;
    string newExpr = StrToLower(expr);
    const sregex_iterator End;

    // Loop through all possible variable matches
    for (sregex_iterator i(expr.begin(), expr.end(), VarPattern); i != End; ++i) {
        string name = (*i)[0];

        // If it is a variable
        if (Vars.find(name) != Vars.end()) {
            sregex rgxName = sregex::compile("\\b" + name + "\\b");

            // Replace all occurrences of it
            newExpr = xpressive::regex_replace(
                newExpr, rgxName,
                lexical_cast<string,double>(Vars[name])
            );
        }
    }

    return newExpr;
}
person Brandon Miller    schedule 03.10.2012
comment
Как только StackOverflow позволит мне через 2 дня, я обязательно это сделаю;) - person Brandon Miller; 03.10.2012