Jak mogę się dowiedzieć, czy ciąg znaków kończy się na innym ciągu w C ++?
Jak mogę się dowiedzieć, czy ciąg znaków kończy się na innym ciągu w C ++?
Odpowiedzi:
Po prostu porównaj ostatnie n znaków, używając 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;
}
Użyj tej funkcji:
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());
}
std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()
W trybie debugowania wyświetla:_DEBUG_ERROR("string iterator not decrementable");
Użyj boost::algorithm::ends_with
(patrz np. 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));
Zauważ, że począwszy od c ++ 20 std :: string w końcu zapewni początek_with i koniec_with . Wygląda na to, że istnieje szansa, że c ++ 30 łańcuchów w c ++ może w końcu stać się użytecznymi, jeśli nie czytasz tego z odległej przyszłości, możesz użyć tych początków Z / Końców Z:
#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);
}
i dodatkowe przeciążenia pomocnika:
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));
}
Ciągi IMO, c ++ są wyraźnie dysfunkcyjne i nie zostały stworzone do użycia w kodzie świata rzeczywistego. Ale jest nadzieja, że przynajmniej będzie lepiej.
Wiem, że pytanie dotyczy C ++, ale jeśli ktoś potrzebuje dobrze zaprojektowanej funkcji C, aby to zrobić:
/* 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
Metoda może służyć temu celowi, gdy używany do iteracji wstecznej od końca obu ciągów:
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() );
std::equal
: musisz wcześniej sprawdzić, czy rzekomy przyrostek nie jest dłuższy niż szukany ciąg. Zaniedbanie sprawdzenia, które prowadzi do nieokreślonego zachowania.
Moim zdaniem najprostszym rozwiązaniem C ++ jest:
bool endsWith(const string& s, const string& suffix)
{
return s.rfind(suffix) == std::abs(s.size()-suffix.size());
}
s
zamiast testować tylko jego koniec!
std::string::size()
jest operacją o stałym czasie; nie potrzebuje strlen
.
Niech a
będzie ciągiem i b
ciągiem, którego szukasz. Posługiwać sięa.substr
aby uzyskać ostatnie n znaków a
i porównać je do b (gdzie n jest długością b
)
Albo użyj std::equal
(dołącz <algorithm>
)
Dawny:
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());
}
Pozwól mi rozszerzyć rozwiązanie Josepha o wersję bez rozróżniania wielkości liter ( demo online )
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);
}
);
}
tak samo jak powyżej, oto moje rozwiązanie
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());
}
starts_with
używa „ciąg :: porównaj”? Dlaczego nie std::equal(start.begin(), start.end(), str.begin())
?
inną opcją jest użycie wyrażenia regularnego. Poniższy kod powoduje, że wyszukiwanie jest niewrażliwe na wielkie / małe litery:
bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
return std::regex_search(str,
std::regex(std::string(suffix) + "$", std::regex_constants::icase));
}
prawdopodobnie nie tak wydajny, ale łatwy do wdrożenia.
możesz użyć string :: rfind
Pełny przykład oparty na komentarzach:
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;
}
Sprawdź, czy str ma przyrostek , używając poniżej:
/*
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;
}
Użyj algorytmu std :: równo <algorithms>
z odwrotną iteracją:
std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
…
}
Odnośnie odpowiedzi Grzegorza Baziora. Użyłem tej implementacji, ale oryginalna ma błąd (zwraca true, jeśli porównam „..” z „.so”). Proponuję zmodyfikowaną funkcję:
bool endsWith(const string& s, const string& suffix)
{
return s.size() >= suffix.size() && s.rfind(suffix) == (s.size()-suffix.size());
}
Pomyślałem, że sensowne jest opublikowanie surowego rozwiązania, które nie korzysta z żadnych funkcji bibliotecznych ...
// 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;
}
Dodając prosty std::tolower
możemy sprawić, że wielkość liter nie będzie uwzględniana
// 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;
}
Znalazłem tę przyjemną odpowiedź na podobny problem „startWith”:
Możesz zastosować rozwiązanie wyszukiwania tylko w ostatnim miejscu ciągu:
bool endsWith(const std::string& stack, const std::string& needle) {
return stack.find(needle, stack.size() - needle.size()) != std::string::npos;
}
W ten sposób możesz zrobić to krótko, szybko, użyć standardowego c ++ i uczynić go czytelnym.
Jeśli jesteś podobny do mnie i nie przepadasz za purytem w C ++, oto stara hybryda skool. Zaletą jest to, że ciągi znaków to więcej niż garść znaków, jak większośćmemcmp
implementacji porównuje słowa maszynowe, gdy jest to możliwe.
Musisz kontrolować zestaw znaków. Na przykład, jeśli takie podejście jest używane z typem utf-8 lub wchar, istnieje pewna wada, ponieważ nie obsługuje mapowania znaków - np. Gdy dwa lub więcej znaków jest logicznie identycznych .
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;
}
Moje dwa centy:
bool endsWith(std::string str, std::string suffix)
{
return str.find(suffix, str.size() - suffix.size()) != string::npos;
}