проверьте, начинается ли строка с другой строки: найти или сравнить?

#c #string #stl

#c #строка #stl

Вопрос:

Если вы хотите узнать, начинается ли строка с другой, как бы вы это сделали в C / STL? В Java есть String.startsWith , в Python тоже есть string.startwith , в STL нет прямого метода для этого. Вместо этого есть std::string::find и std::string::compare . До сих пор я использовал оба метода, в основном в зависимости от моего текущего настроения:

 if ( str1.compare( 0, str2.length(), str2 ) == 0 )
    do_something();
if ( str1.find(str2) == 0 )
    do_something();
  

Конечно, вы также могли бы сделать str.substr(0,str2.length()) == str2 , может быть, есть еще какие-то другие способы добиться того же. find немного удобнее, чем compare , но я видел больше людей, рекомендующих compare это find .

Но какой из них предпочтительнее? Есть ли разница в производительности? Зависит ли это от реализации (GCC, VC и т. Д.)?

Ответ №1:

Недостатком find является то, что if str1 длинный, тогда он будет бессмысленно искать его по всему пути str2 . Я никогда не замечал, чтобы оптимизатор был достаточно умен, чтобы понять, что вам важно только, равен ли результат 0 или нет, и прекратить поиск после начала str1 .

Недостатком compare является то, что вам нужно проверить, что str2.length() это не больше str1.length() (или перехватить результирующее исключение и обработать его как ложный результат).

К сожалению, самое близкое к тому, что вы хотите в стандартной библиотеке std::strncmp (и, конечно, вам нужно использовать c_str() с этим), отсюда и необходимость boost::starts_with или ваш собственный эквивалент, который включает проверки границ.

Комментарии:

1. Хорошо, это имеет смысл. Поэтому, если я не знаю длину str1, было бы лучше использовать boost starts_with . В противном случае, если я знаю, что str1 всегда будет больше, но не намного больше, чем str2, find и compare тоже будет в порядке. Верно?

2. @craesh: да, если недостаток не влияет на вас, тогда либо find или compare дает правильный ответ, либо все в порядке. Существует множество ситуаций, когда дополнительный проход по строке незначителен.

3. @SteveJessop аналогично, существует множество ситуаций, когда c_str может потребоваться выполнить копирование или когда строка длинная, что делает проверку очень дорогостоящей

4. @Foo Bah: я на самом деле не просматривал ни один источник в последнее время, но я сомневаюсь, что существует много реализаций, в которых c_str() выполняется копирование — обычно они завершают строку строковыми данными на месте. Конечно, я бы не стал полагаться на c_str то, чтобы не создавать копию, и это одна из причин разочарования, которая strncmp является самой близкой в стандартной библиотеке. Но я не думаю, что это сделает проверку дорогостоящей на практике, просто теоретически может быть дорогостоящей.

5. @SteveJessop другое предостережение при использовании семейства C string заключается в том, что вы не можете сравнивать строки с нулевыми символами. Хотя это педанты и может быть неуместно в данном случае.

Ответ №2:

boost имеет алгоритм starts_with , который реализует его довольно эффективно: http://www.boost.org/doc/libs/1_41_0/doc/html/boost/algorithm/starts_with.html

Нет никаких требований относительно того, как реализации STL должны реализовывать поиск или сравнение, кроме стандартного материала (возвращаемые значения …), Поэтому это полностью зависит от реализации.

Комментарии:

1. Ах, этого я не знал! Но я думаю, str1.find(str2) == 0 что все еще читается немного лучше, чем starts_with(str1,str2) . Может быть, это просто вопрос вкуса…

2. @craesh в конце концов, человек, использующий вашу программу, не может заботиться о внешнем виде базового кода 🙂

3. @craesh: ну, это вопрос того, сколько вы готовы заплатить за хороший вкус 🙂

4. Конечно, мои клиенты никогда не будут платить мне за хороший код или хороший вкус 🙂 Но как только вы посмотрите на код, который вы написали год назад, вы захотите понять, что вы написали, просто взглянув на него. Вы не можете прокомментировать все, поэтому код должен быть подробным (не всегда возможно).

Ответ №3:

Поскольку find() , возможно, придется искать по всему string , несмотря ни на что, вы можете обернуть compare() так, если хотите:

 #include <iostream>
#include <string>
using namespace std;

bool starts_with(const stringamp; s1, const stringamp; s2) {
    return s2.size() <= s1.size() amp;amp; s1.compare(0, s2.size(), s2) == 0;
}

int main() {
    const string s("zipzambam");
    cout << starts_with(s, "zip") << endl;
}
  

Комментарии:

1. Хорошо, но это будет точно так же, как при использовании boost::algorithm::starts_with . Так что это решение может быть запасным вариантом на случай, если вы не сможете получить доступ к Boost, верно?

2. FWIW , когда Y это type bool , return X ? Y : false; эквивалентно return X amp;amp; Y; , и последнее немного более лаконично. Итак, return s2.size() <= s1.size() amp;amp; s1.compare(0, s2.size(), s2) == 0;

3. @SteveJessop Действительно. Исправлено. Спасибо

Ответ №4:

find возможно, придется искать совпадения по всей строке, даже если первый символ не совпадает, поэтому я бы предложил compare , или, как упоминал @Foo Bah, вы могли бы использовать starts_with алгоритм boost.

Ответ №5:

Вы можете попробовать std::mismatch , единственная глупость в этом алгоритме заключается в том, что вы должны убедиться, что первый диапазон меньше или равен второму диапазону.