З'ясуйте, чи закінчується рядок іншим рядком у 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.
Конхілікультор

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 . Здається, є ймовірність, що c ++ 30 рядків в c ++ нарешті можуть стати корисними, якщо ви не читаєте це з далекого майбутнього, ви можете використовувати ці запускиWith / 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 у ваших методах "beginWith"?
Maxime Oudot

4
@MaximeOudot Звичайно, є! Чому ви хочете шукати весь рядок, якщо вам потрібно знати, чи починається він з чогось? Іншими словами, ви можете шукати рядок довжиною 100 Мб, щоб знайти фрагмент в кінці, а потім ігнорувати цей результат, оскільки це не на початку рядка.
Павло П

1
Плюс "1" для прогнозування с ++ 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 Мб, це буде набагато більше, ніж наносекунд.
Алексіс Вілке

Я не думаю, що так ... це потрібно робити в будь-якому випадку, а потім починає дивитися з кінця.
LtWorf

2
@LtWorf std::string::size()- операція постійного часу; це не потрібно strlen.
Томас

2
Як це може бути навіть розглянутим рішенням, коли воно не вдається для випадку, коли суфікс.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());
}

Як я можу повернути істинне також, якщо воно закінчується після мого рядка з \ r або \ n або обома ??? Дякую!
розм’якшується

@Dario: Ваше рішення, що використовує std :: enako (), добре, рішення, яке використовує 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())?
Дмитро Овдієнко

Просто тому, що start_with був першим, який мені знадобився. Кінцевий_с додано пізніше.
dodjango

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));
}

напевно, не настільки ефективний, але простий у виконанні.


Для всіх, хто має С ++ 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 (ключ, str.length () - key.length (), key.length ());
Ахмед Саїд

Гаразд, це ефективно - але в цьому випадку рядок :: 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 :: рівний алгоритм від <algorithms>із зворотною ітерацією:

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

2
Хоча цей код може вирішити питання, краще додати контекст, чому / як він працює. Це може допомогти майбутнім користувачам навчитися та застосувати ці знання до власного коду. Ви також можете мати позитивні відгуки від користувачів у вигляді оновлень, коли пояснюється код.
borchvm

@borchvm, додав пояснення, сподіваюся, що це допоможе зрозуміти
Сергій

1

Щодо відповіді Гжегожа Базіора. Я використовував цю реалізацію, але оригінальний має помилку (повертає істину, якщо порівнювати ".." з ".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;
}

дякую за додавання цього. світлі рішення завжди чудові
ekkis

1

Знайшов цю приємну відповідь на аналогічну проблему "startWith":

Як я можу перевірити, чи починається рядок C ++ std :: з певного рядка, і перетворити підрядку до int?

Ви можете прийняти рішення лише для пошуку в останньому місці рядка:

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

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


0

Якщо ти схожий на мене, і це не так у пуризм С ++, ось старий гібрид скоолу. Є певна перевага, коли рядки - це більше, ніж жменька символів, як більшість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;
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.