Проверить, является ли одна строка префиксом другой

У меня есть две строки, которые я хотел бы сравнить: String и String:. Есть ли библиотечная функция, которая вернула бы истину при передаче этих двух строк, но ложь, скажем, String и OtherString?

Чтобы быть точным, я хочу знать, является ли одна строка префиксом другой.


person fredley    schedule 27.10.2011    source источник
comment
как насчет использования старого доброго string.compare()?   -  person Alok Save    schedule 27.10.2011
comment
вы имеете в виду сравнение первых N символов?   -  person Donotalo    schedule 27.10.2011
comment
@Donotalo Это было бы хорошо, было бы неплохо, если бы это сделало это за меня, чтобы мне не пришлось тратить время на отработку n.   -  person fredley    schedule 27.10.2011
comment
Ну, строго говоря, одна функция, которая удовлетворяет вашим требованиям, - это оператор ==. ;-)   -  person Frerich Raabe    schedule 27.10.2011
comment
@FrerichRaabe: нет, он не хочет проверять, одинаковы ли они, а скорее, имеют ли они префикс   -  person David Rodríguez - dribeas    schedule 27.10.2011
comment
@ DavidRodríguez-dribeas: Как оказалось, он не хочет проверять, есть ли у них общий префикс, а является ли один префиксом другого (я получил два отрицательных голоса за то, что не догадался).   -  person Björn Pollex    schedule 27.10.2011
comment
@ BjörnPollex Извините за отрицательные голоса ... некоторые люди просто слишком довольны отрицательным голосом, исходный вопрос не был ясен, а третий комментарий здесь, похоже, указывает на то, что N был неизвестен (хотя, если вы ищете префикс, это довольно хорошо известно!)   -  person David Rodríguez - dribeas    schedule 27.10.2011
comment
@David Rodríguez - dribeas: Мой ироничный ответ был нацелен на предложение. Есть ли библиотечная функция, которая возвращала бы истину при передаче этих двух строк (но ложь, скажем, String и OtherString)? в вопросе. Вот почему я написал смайлик в конце. ;-)   -  person Frerich Raabe    schedule 27.10.2011
comment
Вопрос был довольно ясен из примеров до редактирования, сделавшего его явным. == не возвращает истину для 'String' и 'String:', строго говоря или иначе.   -  person Jim Balter    schedule 15.05.2019
comment
Разве это не дублирование (части) stackoverflow.com/questions/1878001/?" Ответ на этот вопрос лучше, чем на любой из приведенных здесь.   -  person Don Hatch    schedule 22.11.2020


Ответы (14)


Используйте std::mismatch. Передайте более короткую строку как первый диапазон итератора и более длинную как второй диапазон итератора. Возврат - это пара итераторов, первый - это итератор в первом диапазоне, а второй - во втором диапазоне. Если первая - конец первого диапазона, то вы знаете, что короткая строка является префиксом более длинной строки, например.

std::string foo("foo");
std::string foobar("foobar");

auto res = std::mismatch(foo.begin(), foo.end(), foobar.begin());

if (res.first == foo.end())
{
  // foo is a prefix of foobar.
}
person Nim    schedule 27.10.2011
comment
+1, и это можно расширить для проверки совместного использования префикса, а не является префиксом, сравнив результат с begin(), а не с концом (и можно получить фактическую длину общий префикс тоже путем вычитания) - person David Rodríguez - dribeas; 27.10.2011
comment
+1, но это опасно, если вторая строка короче, потому что вы перейдете за ее конец. поэтому необходимо проверить, что foo.size() <= foobar.size(). - person Benoit; 27.10.2011
comment
@Benoit, ура; меня смущает то, что они могли так легко принять конец второго итератора и избавить нас от необходимости выполнять проверку раньше ... - person Nim; 27.10.2011
comment
возможно, потому что иногда случается, что вы сравниваете разные контейнеры, вы точно знаете, что первый короче второго, но вычисление размера или конечного итератора во втором контейнере потребует много времени. - person Benoit; 27.10.2011
comment
Это здорово, но решение Джеймса Канце об использовании std :: equal проще. - person crudcore; 09.10.2013
comment
@Benoit Обратите внимание, я думаю, что ваше беспокойство по поводу размера было решено в C ++ 14. См. Комментарии к возвращаемому значению для несоответствия. - person user3731622; 17.03.2016

Если вы знаете, какая строка короче, процедура проста: сначала используйте std::equal с более короткой строкой. Если вы этого не сделаете, должно сработать что-то вроде следующего:

bool
unorderIsPrefix( std::string const& lhs, std::string const& rhs )
{
    return std::equal(
        lhs.begin(),
        lhs.begin() + std::min( lhs.size(), rhs.size() ),
        rhs.begin() );
}
person James Kanze    schedule 27.10.2011

std::string(X).find(Y) равно нулю тогда и только тогда, когда Y является префиксом X

person MSalters    schedule 27.10.2011
comment
Наверное, не самый эффективный. Компилятору нужно будет встроить его, иначе он также должен искать Y с ненулевым смещением. - person MSalters; 27.10.2011
comment
Это кратко, но потенциально неэффективно (представьте, если X очень длинный, а Y не префикса X). - person Frerich Raabe; 27.10.2011
comment
@FrerichRaabe: Вот почему я сам это прокомментировал. Хороший оптимизатор обнаружит сравнение с нулем, обнаружит, что сравнение соответствует индексной переменной, использованной в предыдущем цикле for, и заменит цикл for оператором if. - person MSalters; 27.10.2011
comment
Сообщение из будущего: Используйте std::string_view :) - person Rakete1111; 22.06.2019

Это одновременно эффективно и удобно:

str.compare(0, pre.size(), pre) == 0

compare работает быстро, потому что использует быстрый метод traits::compare и не требует копирования каких-либо данных.

Здесь он будет сравнивать std::min(str.size(), pre.size()) символов, но если символы в двух диапазонах равны, он также проверяет длину pre и возвращает ненулевое значение, если pre длиннее этого.

См. документацию на cplusplus.com.

Я написал тестовую программу, которая использует этот код для сравнения префиксов и строк, заданных в команде. линия.

person Neil Mayhew    schedule 26.02.2014
comment
Зачем вам a.size() >= b.size()? compare() справится и с этим. - person ony; 28.01.2015
comment
Потому что a.compare остановится, когда достигнет конца a, и не будет смотреть на оставшиеся символы b. b не является префиксом a, если он содержит дополнительные символы в конце. - person Neil Mayhew; 13.04.2019
comment
Я изменил имена переменных, чтобы это было легче понять. - person Neil Mayhew; 13.04.2019
comment
@ony Ты прав! Сравнение размеров не требуется. Я только что проверил документы по адресу cplusplus.com/reference/string/string/compare, а compare вернет 0 только в том случае, если два сравниваемых диапазона символов имеют одинаковую длину. Если str короче pre, сравнение вернет отрицательное значение (-1 в моем тестировании). Я отредактирую свой ответ, но вы должны иметь долю кредита. Однако лучшее, что я могу сделать, это проголосовать за ваш комментарий. - person Neil Mayhew; 13.04.2019
comment
если str короче pre, compare вернет отрицательное значение Подождите, что? Это не правильно. Например, если str равно B, а pre равно AA, то str.compare (0, pre.size (), pre) возвращает положительное значение. Мне кажется, что первоначальный тест необходим, и его нужно отложить. - person Don Hatch; 22.11.2020
comment
@DonHatch, если str короче pre И они равны до конца str. В случае AA и B они различаются по первому символу, поэтому compare вернет ненулевой результат. Формулировка в самом ответе яснее. - person Neil Mayhew; 04.12.2020
comment
Это лучший ответ! - person jlstr; 01.02.2021

С помощью string :: compare вы сможете написать что-то вроде:

bool match = (0==s1.compare(0, min(s1.length(), s2.length()), s2,0,min(s1.length(),s2.length())));

В качестве альтернативы, если мы не хотим использовать функцию-член length():

bool isPrefix(string const& s1, string const&s2)
{
    const char*p = s1.c_str();
    const char*q = s2.c_str();
    while (*p&&*q)
        if (*p++!=*q++)
            return false;
    return true;
}
person Vlad    schedule 27.10.2011
comment
Это потенциально неэффективно, если string1 очень длинный - вызов length() - это O (n), и нет необходимости знать точную длину строки. Тебя волнует только то, достаточно ли это долго или нет. - person Frerich Raabe; 27.10.2011
comment
Я не думаю, что дело обстоит так, не с std::string. Из того, что я вижу в basic_string.h, где-то есть поле длины. - person Vlad; 27.10.2011
comment
.length() is O(n)? Вы случайно не смотрите на character_traits стол? - person MSalters; 27.10.2011
comment
@Vlad: См. Этот вопрос для обсуждения на std::string::length(); кажется, что это должно быть O (1), как вы говорите, но это не обязательно: stackoverflow.com/questions/256033/ - person Frerich Raabe; 27.10.2011
comment
@Frerich: Признаюсь, я этого не знал. Но опять же, это, вероятно, O (1) для большинства современных компиляторов. В качестве альтернативы вы можете просто начать с начала и сравнивать символы, пока один из них не станет \0. - person Vlad; 27.10.2011
comment
В C ++ 11 length() должно занимать постоянное время; в C ++ 03 это должно быть. - person Mike Seymour; 27.10.2011
comment
@FrerichRaabe: Комментарий к ответу в первом вопросе является ключевым. Хотя общее требование состоит в том, что size() не обязательно должен быть постоянным временем во всех контейнерах (например, std::list), но std::string должен знать размер (т. Е. Он не может рассчитать его на муха, поскольку все символы в строке допустимы, вы не можете искать часового). Если требуется, чтобы размер был известен (нет необходимости хранить фактический размер, может хранить два указателя) и не может быть вычислен на основе данных, реализация должна быть O ( 1) - person David Rodríguez - dribeas; 27.10.2011
comment
@ Дэвид Родригес - dribeas: std::string не нужно знать размер строки, только ее конец. Вот почему вы можете всегда получить размер строки за постоянное время, вычитая begin итератор из end итератора. - person Frerich Raabe; 27.10.2011
comment
@FrerichRaabe: Обоснование 1) строка должна знать begin() и end() за постоянное время, итераторы случайны, поэтому их можно вычесть за постоянное время, а разница в размере строки, чтобы она была известна < / i> в постоянное время. Обоснование 2), если строка не реализована с помощью ropes (запрещена в C ++ 11, не реализована ни в какой известной текущей реализации стандартной библиотеки), память является непрерывной, и что означает, что знание begin() и end() и знание size() эквивалентно, вам нужно сохранить два из трех, а другой может быть вычислен за постоянное время. - person David Rodríguez - dribeas; 27.10.2011

Если можно разумно игнорировать любые многобайтовые кодировки (например, UTF-8), тогда вы можете использовать _ 1_ для этого:

// Yields true if the string 's' starts with the string 't'.
bool startsWith( const std::string &s, const std::string &t )
{
    return strncmp( s.c_str(), t.c_str(), t.size() ) == 0;
}

Если вы настаиваете на использовании причудливой версии C ++, вы можете использовать алгоритм std::equal (с дополнительное преимущество, заключающееся в том, что ваша функция также работает для других коллекций, а не только для строк):

// Yields true if the string 's' starts with the string 't'.
template <class T>
bool startsWith( const T &s, const T &t )
{
    return s.size() >= t.size() &&
           std::equal( t.begin(), t.end(), s.begin() );
}
person Frerich Raabe    schedule 27.10.2011
comment
Что произойдет с вашим решением std :: equal, если s короче t? Похоже, что он мог читать после конца s. - person teambob; 18.10.2012
comment
@teambob: Вы правы; Я увеличил ответ, чтобы проверить размеры двух струн. - person Frerich Raabe; 18.10.2012

Как насчет того, чтобы просто:

bool prefix(const std::string& a, const std::string& b) {
  if (a.size() > b.size()) {
    return a.substr(0,b.size()) == b;
  }
  else {
    return b.substr(0,a.size()) == a;
  }
}

C ++ не C, безопасно, просто, эффективно.

Протестировано с:

#include <string>
#include <iostream>

bool prefix(const std::string& a, const std::string& b);

int main() {
  const std::string t1 = "test";
  const std::string t2 = "testing";
  const std::string t3 = "hello";
  const std::string t4 = "hello world";
  std::cout << prefix(t1,t2) << "," << prefix(t2,t1) << std::endl;
  std::cout << prefix(t3,t4) << "," << prefix(t4,t3) << std::endl;
  std::cout << prefix(t1,t4) << "," << prefix(t4,t1) << std::endl;
  std::cout << prefix(t1,t3) << "," << prefix(t3,t1) << std::endl;

}

Если у вас C ++ 17, вы можете написать лучшую версию этого, используя вместо этого std::string_view:

#include <string>
#include <string_view>

bool prefix(const std::string& a, const std::string& b) {
  if (a.size() > b.size()) {
    return std::string_view(a.c_str(),b.size()) == b;
  }
  else {
    return std::string_view(b.c_str(),a.size()) == a;
  }
}

В g ++ 7 с параметром -O3 это сводится к одному вызову memcmp, что является довольно существенным улучшением по сравнению со старой версией.

person Flexo    schedule 27.10.2011
comment
Почему std::for_each + лямбда вместо гораздо менее шумного цикла for? - person R. Martinho Fernandes; 27.10.2011
comment
@ R.MartinhoFernandes - удалено. Я добавил этот бит только для того, чтобы показать, что вы вызываете его с большим списком. - person Flexo; 27.10.2011
comment
Эта функция сообщит, что пустая строка содержит все остальные строки в качестве префикса. Для префиксной функции нет смысла делать ее симметричной. - person Tali; 02.11.2017
comment
Этот метод сложен и неэффективен. Он всегда создает временные строковые объекты, потенциально связанные с выделением памяти в куче, и может вызывать. - person user7860670; 11.04.2021
comment
Я бы определенно использовал string_view, если бы написал этот ответ еще раз. - person Flexo; 13.04.2021

Самый простой способ - использовать функции-члены substr () и compare ():

string str = "Foobar";
string prefix = "Foo";

if(str.substr(0, prefix.size()).compare(prefix) == 0) cout<<"Found!";
person Community    schedule 03.06.2013
comment
Операция substr обычно создает копию данных, поэтому это не так эффективно, как могло бы быть. - person Neil Mayhew; 27.02.2014
comment
если вы собираетесь использовать substr(), вы можете просто написать str.substr(0, prefix.size()) == prefix - person ony; 28.01.2015

Вы можете использовать это:

для c ++ 14 или меньше

bool has_prefix
    (const std::string& str, const std::string& prefix)  {
    return str.find(prefix, 0) == 0;
}

для c ++ 17

//it's a little faster
auto has_prefix
    (const std::string& str, const std::string_view& prefix) -> decltype(str.find(prefix) == 0) {
    return str.find(prefix, 0) == 0;
}
person xninja    schedule 24.07.2018
comment
Разве это не будет значительно медленнее, чем некоторые другие методы, если строка не имеет префикса и str длиннее, чем prefix? Поскольку метод find() будет искать любой экземпляр prefix в str, даже если он не является смещением 0. Например, при проверке bbbbbbba на префикс a потребуется выполнить поиск по всей строке, найти последний a, а затем вернуть false, потому что это не при нулевом смещении, а не возвращает false после сравнения только первого символа. - person TrentP; 15.05.2019
comment
@TrentP да. Использование вместо этого rfind () исправит это, как предлагается в принятом ответе на вопрос, на который это дубликат: stackoverflow .com / questions / 1878001 /. - person Don Hatch; 22.11.2020

После C ++ 20 мы можем использовать start_with, чтобы проверить, есть ли строка начинается с заданного префикса.

str.starts_with(prefix)

Кроме того, существует end_with для проверки суффикса

person RY_ Zheng    schedule 10.03.2021

str1.find (str2) возвращает 0, если вся строка str2 находится в индексе 0 строки str1:

#include <string>
#include <iostream>

// does str1 have str2 as prefix?
bool StartsWith(const std::string& str1, const std::string& str2)
{   
    return (str1.find(str2)) ? false : true;
}

// is one of the strings prefix of the another?
bool IsOnePrefixOfAnother(const std::string& str1, const std::string& str2)
{   
    return (str1.find(str2) && str2.find(str1)) ? false : true;
}

int main()
{
    std::string str1("String");
    std::string str2("String:");
    std::string str3("OtherString");

    if(StartsWith(str2, str1))
    {
        std::cout << "str2 starts with str1" << std::endl;      
    }
    else
    {
        std::cout << "str2 does not start with str1" << std::endl;
    }

    if(StartsWith(str3, str1))
    {
        std::cout << "str3 starts with str1" << std::endl;      
    }
    else
    {
        std::cout << "str3 does not start with str1" << std::endl;
    }

        if(IsOnePrefixOfAnother(str2, str1))
        {
            std::cout << "one is prefix of another" << std::endl;      
        }
        else
        {
            std::cout << "one is not prefix of another" << std::endl;
        }

        if(IsOnePrefixOfAnother(str3, str1))
        {
            std::cout << "one is prefix of another" << std::endl;      
        }
        else
        {
            std::cout << "one is not prefix of another" << std::endl;
        }

    return 0;
}

Вывод:

  str2 starts with str1
  str3 does not start with str1
  one is prefix of another
  one is not prefix of another
person Bojan Komazec    schedule 27.10.2011

Что не так с "поиском" и проверкой результата на позицию 0?

string a = "String";
string b = "String:";

if(b.find(a) == 0)
{
// Prefix

}
else
{
// No Prefix
}
person João Augusto    schedule 27.10.2011
comment
find выполняет поиск по всей строке, и compare делает это лучше. - person ProgramCpp; 08.09.2015

Я думаю, что strncmp ближе всего к тому, что вы ищете.

Хотя, если изменить формулировку, вы можете искать strstr(s2,s1)==s2, что не обязательно является наиболее эффективным способом сделать это. Но вы же не хотите тренироваться n ;-)

Хорошо, хорошо, версия для C ++ будет !s1.find(s2).

Ладно, можно сделать еще больше c ++, примерно так: std::mismatch(s1.begin(),s1.end(),s2.begin()).first==s1.end().

person Michael Krelin - hacker    schedule 27.10.2011
comment
Вопрос помечен как C++, а не C. - person Paul R; 27.10.2011
comment
.c_str() не так уж и сложно назвать :) - person Michael Krelin - hacker; 27.10.2011

person    schedule
comment
Это дубликат ранее отправленного ответа и использует сравнение длины, которое было признано ненужным в комментариях к этому ответу. - person Neil Mayhew; 24.09.2019
comment
Я проголосовал против, согласившись с @NeilMayhew, но, подумав, я не согласен с этим отрицательным голосом (который, к сожалению, сейчас заблокирован). Если я не ошибаюсь, первоначальный тест необходим (для производительности), и комментарии в этом ответе, говорящие об обратном, неверны. См. Мой ответ в этой ветке. - person Don Hatch; 22.11.2020