Найти, если строка заканчивается другой строкой в C++
Как я могу найти, если строка заканчивается другой строкой в C++?
22 ответа
Просто сравните последние 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;
}
Используйте эту функцию:
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());
}
Использование 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));
Обратите внимание, что начиная с 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++ явно не функционируют и не предназначены для использования в реальном коде. Но есть надежда, что это станет лучше, по крайней мере.
Я знаю вопрос о 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 );
}
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() );
На мой взгляд, самое простое решение C++ это:
bool endsWith(const string& s, const string& suffix)
{
return s.rfind(suffix) == (s.size()-suffix.size());
}
Позволять a
быть строкой и b
Строка, которую вы ищете. использование a.substr
чтобы получить последние n символов a
и сравнить их с б (где п длина 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());
}
Используйте алгоритм std::equal из <algorithms>
с обратной итерацией:
std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
…
}
Позвольте мне расширить решение Джозефа с учетом версии без учета регистра ( онлайн-демонстрация)
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);
}
);
}
Вы можете использовать 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;
}
Другой вариант - использовать регулярные выражения. Следующий код делает поиск нечувствительным к верхнему / нижнему регистру:
bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
return std::regex_search(str,
std::regex(std::string(suffix) + "$", std::regex_constants::icase));
}
Вероятно, не так эффективно, но легко реализовать.
Проверьте, есть ли суффикс 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;
}
То же самое, что и выше, вот мое решение
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());
}
bool EndsWith(const std::string& data, const std::string& suffix)
{
return data.find(suffix, data.size() - suffix.size()) != string::npos;
}
Тесты
#include <iostream>
int main()
{
cout << EndsWith(u8"o!hello!1", u8"o!") << endl;
cout << EndsWith(u8"o!hello!", u8"o!") << endl;
cout << EndsWith(u8"hello!", u8"o!") << endl;
cout << EndsWith(u8"o!hello!o!", u8"o!") << endl;
return 0;
}
Вывод
0
1
1
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;
}
Если, как и я, вам нужно endWith для проверки расширения файла, вы можете использовать
std::filesystem
библиотека:
std::filesystem::path("/foo/bar.txt").extension() == ".txt"
Относительно ответа Гжегожа Базиора. Я использовал эту реализацию, но оригинальная имеет ошибку (возвращает true, если я сравниваю ".." с ".so"). Я предлагаю модифицированную функцию:
bool endsWith(const string& s, const string& suffix)
{
return s.size() >= suffix.size() && s.rfind(suffix) == (s.size()-suffix.size());
}
Нашел этот хороший ответ на аналогичную проблему "startWith":
Вы можете принять решение, чтобы искать только в последнем месте строки:
bool endsWith(const std::string& stack, const std::string& needle) {
return stack.find(needle, stack.size() - needle.size()) != std::string::npos;
}
Таким образом, вы можете сделать его коротким, быстрым, использовать стандартный C++ и сделать его читабельным.
bool endswith(const std::string &str, const std::string &suffix)
{
string::size_type totalSize = str.size();
string::size_type suffixSize = suffix.size();
if(totalSize < suffixSize) {
return false;
}
return str.compare(totalSize - suffixSize, suffixSize, suffix) == 0;
}
Если вы похожи на меня и не так уж любите C++, вот вам старый гибрид skool. Есть некоторое преимущество, когда строки состоят из нескольких символов, так как большинство 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;
}
Мои два цента:
bool endsWith(std::string str, std::string suffix)
{
return str.find(suffix, str.size() - suffix.size()) != string::npos;
}