Почему std::string.find(text,std::string:npos) не возвращает npos?

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

Я ожидал, что как только позиция достигнет std::string::npos, она останется там, но это не так. Передача std::string::npos в std::string.find, кажется, снова начинает поиск в начале

std::string str("frederick");
std::string::size_type pos = str.find("der",std::string::npos);
TS_ASSERT_EQUALS(pos, std::string::npos); // FAIL, 3 is returned

Почему не принято указывать конец строки?

Обновление: целью является поиск ряда строк по порядку и проверка результата в конце.

pos = str.find(string1, pos)
pos = str.find(string2, pos)
pos = str.find(string3, pos)
if (pos != std:string::npos)
{ // All strings found

person David Sykes    schedule 18.06.2009    source источник
comment
Какой смысл передавать -1 (npos) в качестве места для начала поиска подстроки?   -  person Gishu    schedule 18.06.2009
comment
@Gishu Я добавил пример кода   -  person David Sykes    schedule 18.06.2009
comment
Попробовал ваш код с g++ и find действительно возвращает npos. Я думаю, Чарльз Бейли прав.   -  person mweerden    schedule 18.06.2009
comment
MSVC2005 возвращает npos, а Xcode 3.1.2 и 2.4.1 — нет.   -  person David Sykes    schedule 19.06.2009


Ответы (7)


Глядя на спецификацию, я думаю, что в вашей реализации может быть ошибка.

basic_string::find должен возвращать самую нижнюю позицию xpos, так что pos <= xpos и xpos + str.size() <= size() и at(xpos + I) == str.at(I) для всех элементов I, контролируемых str.

basic_string::npos равно -1, преобразованному в беззнаковый тип, поэтому должно быть наибольшим числом, которое может быть представлено этим беззнаковым типом. Учитывая, что никакая другая позиция xpos не может удовлетворить даже первой части npos ‹= xpos, а find должна возвращать npos в случае ошибки, насколько я вижу, npos является единственным допустимым возвращаемым значением для basic_string::find при передаче npos в качестве второго параметра.

person CB Bailey    schedule 18.06.2009

Сравните string::find() и string::copy(). (В N2798 это 21.3.7.2 и 21.3.6.7, страницы 686/687) Оба принимают аргумент позиции. Однако только string::copy имеет предложение «Requires: pos ‹= size()». Следовательно, string::find не требует pos ‹= size().

С этого момента у Чарльза Бейли правильная логика. Посмотрите на диапазон допустимых возвращаемых значений, и станет ясно, что единственным возвращаемым значением, которое соответствует требованиям, является string::npos. Любое другое возвращаемое значение меньше, чем string::npos, с ошибкой 21.3.7.2/1.


Из N2798=08-0308, авторское право ISO/IEC:

21.3.7.2 basic_string::find [string::find]

size_type find(const basic_string<charT,traits,Allocator>& str, size_type pos = 0) const;

1 Эффекты: Определяет самую нижнюю позицию xpos, если это возможно, так, чтобы выполнялись оба следующих условия: — pos <= xpos и xpos + str.size() <= size();traits::eq(at(xpos+I), str.at(I)) для всех элементов I строки, управляемой str. 2 Возвращает: xpos, если функция может определить такое значение для xpos. В противном случае возвращает npos. 3 замечания: использует traits::eq().

person MSalters    schedule 18.06.2009

std::string::npos не является допустимым аргументом для std::string::find.

Определение find в стандарте упоминает только npos как возможное возвращаемое значение, а не начальную позицию.

person James Hopkin    schedule 18.06.2009
comment
Где это указано в стандарте? Я мог только найти, что npos имеет тип basic_string::size_type, и я не мог найти никаких ограничений на диапазон допустимых значений второго параметра для этой перегрузки find. - person CB Bailey; 18.06.2009
comment
@Charles: здесь cplusplus.com/reference/string/string/find он говорит, что позиция первого символа в строке должна учитываться для возможных совпадений. Значение 0 означает, что рассматривается вся строка. С этой документацией я бы даже не пытался передать ее npos, поскольку поведение может зависеть от реализации. - person Daniel Daranas; 18.06.2009
comment
Но стандарт намного яснее описывает, что должен делать find, и не имеет ограничений на допустимое поведение для любого значения второго параметра. - person CB Bailey; 18.06.2009
comment
Согласен с Чарльзом. cplusplus.com не определяет библиотеку C++. - person MSalters; 18.06.2009
comment
@Charles: Итак, стандарт допускает любое значение для этого параметра. Тогда вы правы, и реализация, которую использует Дэвид, имеет здесь ошибку. Свой комментарий выше оставляю для полноты обсуждения, хотя уже не держу :) - person Daniel Daranas; 18.06.2009

Вы можете обнаружить, что в этой ситуации проще использовать бесплатную функцию std::search. Например.

std::string::const_iterator iter = str.begin();

iter = std::search( iter, str.end(), string1.begin(), string1.end() );
iter = std::search( iter, str.end(), string2.begin(), string2.end() );
iter = std::search( iter, str.end(), string3.begin(), string3.end() );
person CB Bailey    schedule 18.06.2009

Поведение не определено, если вы передаете npos:

[update]
В документации STL (во всяком случае, две копии, которые я могу найти) упоминается string::npos только как возможное возвращаемое значение, а не как допустимое значение для pos. Последний является индексом, с которого начинается поиск.

Но см. также комментарии ниже (я не эксперт по стандарту ISO, я ограничиваю свои ожидания на основе имеющихся у меня документов).

Реализация STL обычно использует значение, явно выходящее за пределы диапазона (например, ((size_type)-1)). Как это обрабатывается как параметр, четко не указано, поэтому я бы не стал полагаться на такое поведение. [/update]< /сильный>

Поэтому вам нужно начать с 0 и проверять pos != npos после каждого вызова, чтобы найти:

 pos = str.find(string1, 0)
 if (pos != std:string::npos)
   pos = str.find(string2, pos)
 if (pos != std:string::npos)
   pos = str.find(string3, pos)

 if (pos != std:string::npos)
 { 
   // All strings found
 }
person peterchen    schedule 18.06.2009
comment
Почему поведение не определено, когда вы передаете npos? - person CB Bailey; 18.06.2009
comment
Но в стандарте не требуется явно указывать, что происходит, когда вы передаете string::npos. string::npos — допустимое значение для string::size_type, и нет никаких явных ограничений на входной диапазон. Насколько я вижу, описание поведения для string::find по-прежнему хорошо указано для всех значений второго параметра. - person CB Bailey; 18.06.2009
comment
И обратите внимание, что документация STL (единственная документация по ISO) действительно указывает, какие значения аргументов допустимы, если это подмножество. См. basic_string::copy() на предыдущей странице. // Более того, никакая реализация STL не может и не может использовать отрицательное значение для npos, так как его тип не имеет знака (и является своего рода целым числом). - person MSalters; 18.06.2009
comment
Хорошая мысль, MSalters. Я удалил соответствующее предложение выше. Тем не менее, я бы не стал ожидать возврата значения npos, если начальный индекс не находится в пределах строковых ограничений, если только это не будет указано явно в документации по поиску или неявно в соглашениях о документации. - person peterchen; 18.06.2009
comment
Возврат npos естественным образом следует из раздела «Эффекты:». Это то, что возвращает функция, если между элементами нет равенства. И эти элементы указываются через .at(), а не через более естественный оператор [], поэтому ясно, что индекс вне диапазона не вызывает неопределенного поведения. - person MSalters; 18.06.2009

Вы должны использовать в качестве начальной позиции длину строки.

person Paolo Tedesco    schedule 18.06.2009
comment
Я хочу использовать возвращаемое значение предыдущей находки, не проверяя его каждый раз - person David Sykes; 18.06.2009

Передача std::string::npos в качестве второго аргумента для поиска означает «начать поиск в или после позиции std::string::npos в строке».

Ясно, что это не то, что вы имели в виду.

РЕДАКТИРОВАТЬ:

Это может сделать то, что вы изначально планировали:

string s;
string::size_type pos;

if ((pos = s.find(s1)) != string::npos && (pos = s.find(s2, pos)) != npos && 
    (pos = s.find(s3,pos)) != string::npos)
{
    // okay
}

Я не проверял это, но это должно работать, вы можете предпочесть стиль peterchen, так как он более читабелен.

person Idan K    schedule 18.06.2009