Узнайте, заканчивается ли строка другой строкой в ​​C ++


270

Как я могу узнать, заканчивается ли строка другой строкой в ​​C ++?

Ответы:


211

Просто сравните последние n символов, используя std::string::compare:

#include <iostream>

bool hasEnding (std::string const &fullString, std::string const &ending) {
    if (fullString.length() >= ending.length()) {
        return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
    } else {
        return false;
    }
}

int main () {
    std::string test1 = "binary";
    std::string test2 = "unary";
    std::string test3 = "tertiary";
    std::string test4 = "ry";
    std::string ending = "nary";

    std::cout << hasEnding (test1, ending) << std::endl;
    std::cout << hasEnding (test2, ending) << std::endl;
    std::cout << hasEnding (test3, ending) << std::endl;
    std::cout << hasEnding (test4, ending) << std::endl;

    return 0;
}

Да, это лучший способ сделать это, без сомнения.
Нолдорин

3
Я всегда ненавижу вычислять индексы подстрок, это очень непривычно ... Я бы предпочел итерацию в обратном направлении от конца обеих строк, пытаясь найти несоответствие.
xtofl

17
@ Noldorin Я не согласен. Это очень просто - лучший способ сделать это - использовать библиотеку. Обидно, что C ++ Standard делает так мало полезных вещей.
masterxilo

1
@masterxilo Какую библиотеку вы предлагаете для решения этой проблемы и как эта библиотека является лучшим выбором, чем (в основном) однострочная функция?
Брандин

33
@Brandin Потому что это такая базовая функциональность. C ++ заставляет нас снова и снова перепрограммировать те же функциональные возможности, которые предоставляются «из коробки» на любом другом современном компьютерном языке. Тот факт, что люди должны перейти к stackoverflow для решения этого вопроса, показывает, что есть pb.
Conchylicultor

175

Используйте эту функцию:

inline bool ends_with(std::string const & value, std::string const & ending)
{
    if (ending.size() > value.size()) return false;
    return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}

3
Помните, что MSVC10 не нравится это решение: std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()в режиме отладки он выдает:_DEBUG_ERROR("string iterator not decrementable");
remi.chateauneu

154

Используйте boost::algorithm::ends_with(см., Например, http://www.boost.org/doc/libs/1_34_0/doc/html/boost/algorithm/ends_with.html ):

#include <boost/algorithm/string/predicate.hpp>

// works with const char* 
assert(boost::algorithm::ends_with("mystring", "ing"));

// also works with std::string
std::string haystack("mystring");
std::string needle("ing");
assert(boost::algorithm::ends_with(haystack, needle));

std::string haystack2("ng");
assert(! boost::algorithm::ends_with(haystack2, needle));

83

Обратите внимание, что начиная с c ++ 20 std :: string в конечном итоге предоставит start_with и end_with . Похоже, что есть шанс, что с ++ 30 строки в с ++ могут наконец стать пригодными для использования, если вы не читаете это из далекого будущего, вы можете использовать эти startWith / EndWith:

#include <string>

static bool endsWith(const std::string& str, const std::string& suffix)
{
    return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
}

static bool startsWith(const std::string& str, const std::string& prefix)
{
    return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix);
}

и некоторые дополнительные вспомогательные перегрузки:

static bool endsWith(const std::string& str, const char* suffix, unsigned suffixLen)
{
    return str.size() >= suffixLen && 0 == str.compare(str.size()-suffixLen, suffixLen, suffix, suffixLen);
}

static bool endsWith(const std::string& str, const char* suffix)
{
    return endsWith(str, suffix, std::string::traits_type::length(suffix));
}

static bool startsWith(const std::string& str, const char* prefix, unsigned prefixLen)
{
    return str.size() >= prefixLen && 0 == str.compare(0, prefixLen, prefix, prefixLen);
}

static bool startsWith(const std::string& str, const char* prefix)
{
    return startsWith(str, prefix, std::string::traits_type::length(prefix));
}

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


2
Поскольку str.compare не возвращает логическое значение, проверять наличие «== 0» с помощью оператора not («!») Не так уж и разумно, так как это может сбить читателей с толку. Пожалуйста, используйте "... && str.compare (...) == 0" для ясности.
Томас Темпельманн

@Pavel Есть ли причина не использовать std :: string :: find в ваших методах "launchWith"?
Максим

4
@MaximeOudot Конечно, есть! Зачем вам искать всю строку, если вам нужно знать, начинается ли она с чего-то? Другими словами, вы можете в конечном итоге искать строку длиной 100 Мб, чтобы найти фрагмент в конце, а затем игнорировать этот результат, потому что он не находится в начале строки.
Павел П

1
Плюс «1» для прогноза c ++ 30.
Невинный

40

Я знаю вопрос о C ++, но если кому-то нужна хорошая старомодная функция C, чтобы сделать это:


/*  returns 1 iff str ends with suffix  */
int str_ends_with(const char * str, const char * suffix) {

  if( str == NULL || suffix == NULL )
    return 0;

  size_t str_len = strlen(str);
  size_t suffix_len = strlen(suffix);

  if(suffix_len > str_len)
    return 0;

  return 0 == strncmp( str + str_len - suffix_len, suffix, suffix_len );
}


25

std::mismatchМетод может служить этой цели , когда используется для задом наперед итерации с конца обеих строк:

const string sNoFruit = "ThisOneEndsOnNothingMuchFruitLike";
const string sOrange = "ThisOneEndsOnOrange";

const string sPattern = "Orange";

assert( mismatch( sPattern.rbegin(), sPattern.rend(), sNoFruit.rbegin() )
          .first != sPattern.rend() );

assert( mismatch( sPattern.rbegin(), sPattern.rend(), sOrange.rbegin() )
          .first == sPattern.rend() );

3
+1. Я никогда раньше не замечал std :: mismatch () - мне интересно, что еще находится в заголовочном файле алгоритмов, который я никогда не просматривал ...
j_random_hacker

3
Я думаю, что это стоит отдельного вопроса: вы когда-нибудь просматривали доступные функции stl?
xtofl

2
Обратите внимание, что к нему предъявляются те же требования, что и std::equal: вам нужно заранее проверить, что предполагаемый суффикс не длиннее строки, в которой вы его ищете. Пренебрежение проверкой приводит к неопределенному поведению.
Роб Кеннеди

18

На мой взгляд, самое простое решение C ++ это:

bool endsWith(const string& s, const string& suffix)
{
    return s.rfind(suffix) == std::abs(s.size()-suffix.size());
}

10
Это довольно медленно, так как вы будете искать всю строку, sа не просто проверять ее конец!
Алексис Уилке

2
@nodakai, если у меня будет строка размером 1 Мб, это будет намного больше, чем наносекунды.
Алексис Уилк

Я так не думаю ... это нужно делать в любом случае strlen, а затем начинает смотреть с конца.
17

2
@LtWorf std::string::size()- операция с постоянным временем; это не нужно strlen.
Томас

2
Как это может быть даже рассмотрено как решение, когда оно терпит неудачу для случая, когда suffix.size () == s.size () + 1. Фрагмент кода, показывающий этот onlinegdb.com/S1ITVqKDL . Сложность не имеет значения, если она не работает должным образом для всех случаев.
c0ntrol

10

Позвольте aбыть строкой и bстрокой, которую вы ищете. Используйте, a.substrчтобы получить последние n символов aи сравнить их с b (где n - длина b)

Или использовать std::equal(включить <algorithm>)

Пример:

bool EndsWith(const string& a, const string& b) {
    if (b.size() > a.size()) return false;
    return std::equal(a.begin() + a.size() - b.size(), a.end(), b.begin());
}

Как я могу вернуть true также, если он заканчивается после моей строки с \ r или \ n или обоими ??? Спасибо!
sofr

@Dario: Ваше решение, использующее std :: equal (), хорошо, тот, который использует substr () не так уж и много - если вы не используете строки COW (и я верю, что мало кто из них), substr () подразумевает создание второй копии части строки, подразумевающей динамическое выделение памяти. Это может произойти сбой, и в любом случае означает, что используется больше памяти, чем другие решения (и это почти наверняка медленнее, чем другие решения).
j_random_hacker

4

Позвольте мне расширить решение Джозефа с учетом версии без учета регистра ( онлайн-демонстрация )

static bool EndsWithCaseInsensitive(const std::string& value, const std::string& ending) {
    if (ending.size() > value.size()) {
        return false;
    }
    return std::equal(ending.rbegin(), ending.rend(), value.rbegin(),
        [](const char a, const char b) {
            return tolower(a) == tolower(b);
        }
    );
}

3

то же самое, что и выше, вот мое решение

 template<typename TString>
  inline bool starts_with(const TString& str, const TString& start) {
    if (start.size() > str.size()) return false;
    return str.compare(0, start.size(), start) == 0;
  }
  template<typename TString>
  inline bool ends_with(const TString& str, const TString& end) {
    if (end.size() > str.size()) return false;
    return std::equal(end.rbegin(), end.rend(), str.rbegin());
  }

1
Почему starts_withиспользуется строка :: сравнить? Почему нет std::equal(start.begin(), start.end(), str.begin())?
Дмитрий Овдиенко

Просто потому, что старты - это было первое, что мне было нужно. конец_с добавлением позже.
Доджанго

3

Другой вариант - использовать регулярные выражения. Следующий код делает поиск нечувствительным к верхнему / нижнему регистру:

bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
  return std::regex_search(str,
     std::regex(std::string(suffix) + "$", std::regex_constants::icase));
}

Вероятно, не так эффективно, но легко реализовать.


Для любого с C ++ 11 или выше, это очень удобно.
Клэр Макрей

Остерегайтесь, регулярные выражения могут быть безумно медленными в C ++!
mxmlnkn

регулярное выражение для этого, как ... мне нужно понизить это. Я не буду, но я должен.
МК.

2

ты можешь использовать string :: rfind

Полный пример на основе комментариев:

bool EndsWith(string &str, string& key)
{
size_t keylen = key.length();
size_t strlen = str.length();

if(keylen =< strlen)
    return string::npos != str.rfind(key,strlen - keylen, keylen);
else return false;
}

3
-1. Да, вы можете использовать его, но это неоправданно медленно в том случае, если строка не заканчивается указанным окончанием - сканирование будет продолжаться вплоть до начала строки. Кроме того, вы не упоминаете, что вам нужен последующий тест, чтобы убедиться, что конец совпадает с концом строки , а не где-нибудь в строке.
j_random_hacker

Я просто поставил ссылку на нужную функцию и думаю, что это очень легко сделать из документации str.rfind (key, str.length () - key.length (), key.length ());
Ахмед Саид

Хорошо, это эффективно - но в этом случае string :: find () будет работать так же хорошо. Также вам нужно упомянуть случай, когда key.length ()> str.length () - код, который вы предлагаете в своем комментарии, в этом случае потерпит крах. Если вы обновите свой ответ этой информацией, я уроню -1.
j_random_hacker

2

Проверьте, есть ли суффикс str , используя ниже:

/*
Check string is end with extension/suffix
*/
int strEndWith(char* str, const char* suffix)
{
  size_t strLen = strlen(str);
  size_t suffixLen = strlen(suffix);
  if (suffixLen <= strLen) {
    return strncmp(str + strLen - suffixLen, suffix, suffixLen) == 0;
  }
  return 0;
}

2

Используйте алгоритм std :: equal <algorithms>с обратной итерацией:

std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
   
}

2
Хотя этот код может обеспечить решение вопроса, лучше добавить контекст относительно того, почему / как он работает. Это может помочь будущим пользователям учиться и применять эти знания в своем собственном коде. Вы также, вероятно, получите положительные отзывы от пользователей в виде откликов, когда код будет объяснен.
Борчвм

@borchvm, добавил некоторые пояснения, надеюсь, это поможет понять
Сергей

1

Относительно ответа Гжегожа Базиора. Я использовал эту реализацию, но оригинальная имеет ошибку (возвращает true, если я сравниваю «..» с «.so»). Я предлагаю модифицированную функцию:

bool endsWith(const string& s, const string& suffix)
{
    return s.size() >= suffix.size() && s.rfind(suffix) == (s.size()-suffix.size());
}

1

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

// Checks whether `str' ends with `suffix'
bool endsWith(const std::string& str, const std::string& suffix) {
    if (&suffix == &str) return true; // str and suffix are the same string
    if (suffix.length() > str.length()) return false;
    size_t delta = str.length() - suffix.length();
    for (size_t i = 0; i < suffix.length(); ++i) {
        if (suffix[i] != str[delta + i]) return false;
    }
    return true;
}

Добавив простой, std::tolowerмы можем сделать этот регистр нечувствительным

// Checks whether `str' ends with `suffix' ignoring case
bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
    if (&suffix == &str) return true; // str and suffix are the same string
    if (suffix.length() > str.length()) return false;
    size_t delta = str.length() - suffix.length();
    for (size_t i = 0; i < suffix.length(); ++i) {
        if (std::tolower(suffix[i]) != std::tolower(str[delta + i])) return false;
    }
    return true;
}

спасибо за добавление этого. световые решения всегда
хороши

1

Нашел этот хороший ответ на похожую проблему "startWith":

Как проверить, начинается ли строка C ++ std :: string с определенной строки, и преобразовать подстроку в int?

Вы можете принять решение, чтобы искать только в последнем месте в строке:

bool endsWith(const std::string& stack, const std::string& needle) {
    return stack.find(needle, stack.size() - needle.size()) != std::string::npos;
}

Таким образом, вы можете сделать его коротким, быстрым, использовать стандартный c ++ и сделать его читабельным.


0

Если вы похожи на меня и не любите C ++, то вот вам старый гибрид скула. Есть некоторое преимущество, когда строки больше, чем несколько символов, как большинствоmemcmp реализаций сравнивают машинные слова, когда это возможно.

Вы должны контролировать набор символов. Например, если этот подход используется с типом utf-8 или wchar, есть некоторый недостаток, поскольку он не поддерживает сопоставление символов - например, когда два или более символов логически идентичны .

bool starts_with(std::string const & value, std::string const & prefix)
{
    size_t valueSize = value.size();
    size_t prefixSize = prefix.size();

    if (prefixSize > valueSize)
    {
        return false;
    }

    return memcmp(value.data(), prefix.data(), prefixSize) == 0;
}


bool ends_with(std::string const & value, std::string const & suffix)
{
    size_t valueSize = value.size();
    size_t suffixSize = suffix.size();

    if (suffixSize > valueSize)
    {
        return false;
    }

    const char * valuePtr = value.data() + valueSize - suffixSize;

    return memcmp(valuePtr, suffix.data(), suffixSize) == 0;
}

0

Мои два цента:

bool endsWith(std::string str, std::string suffix)
{
   return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.