Я пытаюсь изменить ввод пользователя в форме подстановочного знака ("*word*") на формат регулярного выражения. С этой целью я использую приведенный ниже код, чтобы отделить '*' в начале и конце ввода, чтобы я мог добавить символы регулярного выражения с любого конца:
string::iterator iter_begin = expressionBuilder.begin();
string::iterator iter_end = expressionBuilder.end();
iter_end--;
if ((char)*iter_begin == '*' && (char)*iter_end == '*')
{
expressionBuilder.erase(iter_begin);
expressionBuilder.erase(iter_end);
expressionBuilder = "\\b\\w*" + expressionBuilder + "\\w*\\b";
}
Однако при вызове "expressionBuilder.erase(iter_end)"нет стирает завершающий '*' из входной строки, поэтому я получаю неправильное регулярное выражение. Что я здесь делаю не так? "(char)*iter_end == '*'" должен быть истинным для запуска кода внутри if (что он и делает), так почему же тот же итератор не работает при передаче в erase ()?





Попробуйте стереть их в обратном порядке:
expressionBuilder.erase(iter_end);
expressionBuilder.erase(iter_begin);
После стирания первого * iter_end относится к одному символу за концом строки в вашем примере. Документация STL указывает, что итераторы недействительны erase(), поэтому технически мой пример тоже неверен, но я считаю, что он будет работать на практике.
P4tXrx5jrMlbhyludk9pxHBT30kGHo9n: вы правы насчет end (), но там есть iter_end--, который смотрит на фактический последний символ строки.
В этом есть смысл, и изменение порядка решило проблему. Спасибо!
Извините, я пропустил строку со знаком «-».
@Greg: в другом сообщении делается вывод, что итераторы после стертый недействительны. (stackoverflow.com/questions/62340/…)
@xtoff - это не относится к строке. Чтобы обеспечить поддержку строк с подсчетом ссылок, вызов любой неконстантной функции-члена (за исключением некоторых, которые ничего не делают, кроме возврата ссылок или итераторов) может сделать недействительными любые существующие итераторы.
(Пересмотрел, так как пропустил строчку iter_end--).
Вероятно, вам нужен оператор if, который проверяет только наличие *iter_begin == '*', а затем вызывает find(), чтобы получить другой '*'. Или вы можете использовать rbegin(), чтобы получить «начальный итератор последовательности в обратном порядке», переместить его на единицу и затем вызвать base(), чтобы превратить его в обычный итератор. Это даст вам последний символ в последовательности.
Еще лучше, у std::string есть rfind() и find_last_of() методы. Вам дадут последний '*'. Вы также можете просто вызвать replace() вместо удаления '*' и последующего добавления нового материала обратно.
Обратите внимание, что там есть iter_end--, который поддерживает один символ.
Вы пропустили "iter_end--;" строка, которая перемещает итератор обратно к последнему элементу? Я уверен, что ответ Грега верен, потому что итераторы строк - это в основном просто индексы, поэтому конечный индекс становится недействительным при первом стирании.
Я пытался избежать "find_last_of", потому что я уже знаю, где находится персонаж, но, возможно, я слишком сильно над этим подумал.
В вашем исходном коде и предлагаемых решениях есть пара проблем в дополнение к очевидной проблеме, о которой вы писали:
Теперь последние два элемента могут не быть проблемой, если код, который использует фрагмент / процедуру, уже проверяет, что строка содержит как минимум 2 символа, но в случае, если это не ситуация, я считаю, что следующее будет более надежным в лицо произвольных значений для expressionBuilder:
// using the reverse iterator rbegin() is a nice easy way
// to get the last character of a string
if ( (expressionBuilder.size() >= 2) &&
(*expressionBuilder.begin() == '*') &&
(*expressionBuilder.rbegin() == '*') ) {
expressionBuilder.erase(expressionBuilder.begin());
// can't nicely use rbegin() here because erase() wont take a reverse
// iterator, and converting reverse iterators to regular iterators
// results in rather ugly, non-intuitive code
expressionBuilder.erase(expressionBuilder.end() - 1); // note - not invalid since we're getting it anew
expressionBuilder = "\\b\\w*" + expressionBuilder + "\\w*\\b";
}
Обратите внимание, что этот код будет работать, когда expressionBuilder - это "", "*" или "**", поскольку он не выполняет никаких неопределенных действий. Однако в этих случаях он может не дать желаемых результатов (потому что я не знаю точно, что вам нужно в этих случаях). Измените в соответствии с вашими потребностями.
Спасибо. На данный момент я почти знаю, что строка не пуста или не "*", но я согласен с тем, что было бы лучше закодировать ее таким образом на случай, если что-то изменится позже.
Без обработки ошибок вы, вероятно, могли бы сделать это так:
#include <iostream>
#include <string>
using namespace std;
string stripStar(const string& s) {
return string(s.begin() + 1, s.end() - 1);
}
int main() {
cout << stripStar("*word*") << "\n";
}
Что, если вы позвоните stripStar("word") или даже stripStar("word*")? Я думаю, OP хочет такой универсальности.
К счастью, со строками вам не нужно использовать итераторы, большинство функций имеют форму, которая вместо этого принимает индекс. Тем не менее, как вы говорите, даже при индексированном стирании это все равно нужно делать «задом наперед».