Dizenin C ++ 'da başka bir dizeyle bitip bitmediğini öğrenin


270

Bir dizenin C ++ 'da başka bir dizeyle bitip bitmediğini nasıl öğrenebilirim?

Yanıtlar:


211

Son n karakteri aşağıdakileri kullanarak karşılaştırmanız yeterlidir 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;
}

Evet, bunu yapmanın en iyi yolu şüphesiz.
Noldorin

3
Her zaman alt dizelerin indekslerini hesaplamaktan nefret ederim, bu birebir eğilimli ... Her iki dizenin sonundan geriye doğru yinelemeyi tercih ederim, bir uyumsuzluk bulmaya çalışıyorum.
xtofl

17
@Noldorin Kabul etmiyorum. Bu beyinsizdir - bunu yapmanın en iyi yolu bir kütüphane kullanmaktır. C ++ Standart kütüphanesinin çok az yararlı şey yaptığı bir utanç.
masterxilo

1
@masterxilo Bu sorunu çözmek için hangi kitaplığı öneriyorsunuz ve bu kitaplık (temel olarak) tek satırlık bir işlevden nasıl daha iyi bir seçim?
Brandin

33
@Brandin Çünkü böyle temel bir işlevsellik. C ++, bizi diğer modern bilgisayar dillerinde hazır olan aynı işlevleri tekrar tekrar programlamaya zorlar. İnsanların bu soruyu çözmek için yığın akışına gitmesi gerektiği bir pb olduğunu gösterir.
Conchylicultor

175

Bu işlevi kullanın:

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'un bu çözümü sevmediğine dikkat edin: std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()Hata ayıklama modunda atar:_DEBUG_ERROR("string iterator not decrementable");
remi.chateauneu

154

Kullanın boost::algorithm::ends_with(bkz. Örneğin 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

Not başlayarak o c ++ 20 std :: string nihayet sağlayacaktır starts_with ve ends_with . Görünüşe göre c ++ 'daki c ++ 30 dizeleri nihayet kullanılabilir hale gelebilir, eğer bunu uzak bir gelecekten okumuyorsanız, bu start'ları kullanabilirsiniz.

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

ve bazı ekstra yardımcı aşırı yüklemeler:

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 ++ dizeleri açıkça işlevsizdir ve gerçek dünya kodunda kullanılmamıştır. Ancak bunun en azından daha iyi olacağına dair bir umut var.


2
Str.compare bir boole döndürmediği için, not ("!") Operatörünü kullanarak "== 0" değerini test etmek o kadar akıllı değildir, çünkü bu okuyucular için kafa karıştırıcı olabilir. Lütfen anlaşılır olması için "... && str.compare (...) == 0" kullanın.
Thomas Tempelmann

@Pavel "startsWith" yöntemlerinizde std :: string :: find kullanmamak için bir neden var mı?
Maxime Oudot

4
@MaximeOudot Ders dışı var! Bir şeyle başlayıp başlamadığını bilmeniz gerekiyorsa neden tüm dizeyi aramak istersiniz? Başka bir deyişle, en sonunda parçayı bulmak için 100mb uzunluğunda bir dizeyi arayabilir ve daha sonra dizenin başında olmadığı için bu sonucu göz ardı edebilirsiniz.
Pavel P

1
Ayrıca c ++ 30 tahmini için "1".
Masum Bystander

40

Sorunun C ++ için olduğunu biliyorum, ama herkes bunu yapmak için iyi ol 'moda C işlevine ihtiyacınız varsa:


/*  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::mismatchGeriye doğru kullanılan bu amaca hizmet edebilir yöntemi, her iki şeritlerinin ucundan yineleme:

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. Daha önce hiç fark etmemiştim std :: mismatch () - Daha önce hiç bakmadığım algoritmalar başlık dosyasında başka neler olduğunu merak ediyorum ...
j_random_hacker

3
Bence bu kendi başına bir SO soruya değer: hiç mevcut stl fonksiyonları ile göz var mı?
xtofl

2
Bunun aynı gereksinime sahip std::equalolduğuna dikkat edin: sözde son ekin aradığınız dizeden daha uzun olmadığını önceden kontrol etmeniz gerekir. Tanımlanmamış davranışa neden olup olmadığını kontrol etmeyi ihmal etmek.
Rob Kennedy

18

Bence en basit, C ++ çözümü:

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

10
Bu, ssonunu test etmek yerine tüm dizeyi arayacağınız için oldukça yavaştır !
Alexis Wilke

2
@nodakai, 1Mb'lik bir dizeye sahip olursam, nanosaniyeden çok daha fazla olacak.
Alexis Wilke

Ben öyle düşünmüyorum… her durumda strlen yapması gerekiyor ve sonra sondan bakmaya başlıyor.
LtWorf

2
@ LTWorf std::string::size()sabit zamanlı bir işlemdir; gerek yok strlen.
Thomas

2
Suffix.size () == s.size () + 1 olduğunda durum için başarısız olduğunda bu çözüm bile nasıl düşünülebilir ? Tüm durumlar için düzgün çalışmazsa karmaşıklık önemsizdir.
c0ntrol

10

Izin avermek bir dize ve baradığınız dize. Tuşunun a.substrson n karakterini almak ave bunları b ile karşılaştırmak için kullanın (burada n, uzunluğudur b)

Veya kullanın std::equal(dahil <algorithm>)

Ör:

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 veya \ n veya her ikisi ile benim dize sonra biterse de nasıl doğru dönebilir ??? Teşekkürler!
sofr

@Dario: std :: equal () kullanan çözümünüz iyi, substr () kullanan çözüm çok fazla değil - COW dizeleri kullanmadığınız sürece (ve inanıyorum ki birkaç kişi), substr () ikinci bir kopya oluşturma anlamına gelir dizenin bir kısmı, dinamik bellek ayırma anlamına gelir. Bu başarısız olabilir ve her durumda diğer çözümlerden daha fazla bellek kullanıldığı anlamına gelir (ve diğer çözümlerden neredeyse kesinlikle daha yavaştır).
j_random_hacker

4

Durum duyarsız versiyonu ile Joseph'in çözümünü genişletmeme izin verin ( çevrimiçi demo )

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

yukarıdakiyle aynı, işte benim çözümüm

 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
Neden starts_with'string :: compar' kullanılıyor? Neden olmasın std::equal(start.begin(), start.end(), str.begin())?
Dmytro Ovdiienko

Çünkü begin_with ihtiyacım olan ilk kişi olduğu için. End_with daha sonra eklendi.
Dodjango

3

başka bir seçenek regex kullanmaktır. Aşağıdaki kod, aramayı büyük / küçük harfe duyarsız hale getirir:

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

muhtemelen çok verimli değil, ama uygulaması kolaydır.


C ++ 11 veya üstü olan herkes için bu çok uygundur.
Clare Macrae

C ++ 'da regexes delicesine yavaş olabilir!
mxmlnkn

Bunun için regex gibi ... Bunu aşağıya düşürmem gerekiyor. Yapmam ama yapmalıyım.
MK.

2

string :: rfind kullanabilirsiniz

Yorumlara dayalı tam örnek:

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. Evet kullanabilirsiniz, ancak dizenin verilen sonla bitmemesi durumunda gereksiz derecede yavaştır - tarama dizenin başlangıcına kadar devam eder. Ayrıca, bitişin dizenin başka bir yerine değil , dizenin sonunda eşleştiğinden emin olmak için sonraki bir teste ihtiyacınız olduğunu belirtmezsiniz .
j_random_hacker

Sadece gerekli fonksiyonun bağlantısını koydum ve bunu str.rfind (key, str.length () - key.length (), key.length ()) belgelerinden yapmanın çok kolay olduğunu düşünüyorum;
Ahmed Said

Tamam, bu etkili - ama bu durumda string :: find () da işe yarayacaktır. Ayrıca key.length ()> str.length () - yorumunuzda önerdiğiniz kodun bu durumda kilitleneceği durumdan bahsetmeniz gerekir. Cevabınızı bu bilgilerle güncellerseniz -1'i bırakacağım.
j_random_hacker

2

Str'nin son eki olup olmadığını kontrol edin :

/*
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

<algorithms>Ters yinelemeli std :: equal algoritması kullanın :

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

2
Bu kod soruya bir çözüm sunabilse de, neden / nasıl çalıştığına ilişkin bağlam eklemek daha iyidir. Bu, gelecekteki kullanıcıların bu bilgileri öğrenmesine ve kendi kodlarına uygulamalarına yardımcı olabilir. Kod açıklandığında, kullanıcılardan upvotes şeklinde olumlu geri bildirimleriniz de olabilir.
borchvm

@borchvm, biraz açıklama ekledi, umarım anlamaya yardımcı olur
Sergei

1

Grzegorz Bazior yanıtı ile ilgili. Bu uygulamayı kullandım, ancak orijinal bir hata var (".." ile ".so" karşılaştırırsanız doğru döner). Değiştirilmiş işlev öneriyorum:

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

1

Kütüphane işlevlerini kullanmayan ham bir çözüm yayınlamanın mantıklı olduğunu düşündüm ...

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

Basit bir ekleyerek std::tolower, bu vakayı duyarsız yapabiliriz

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

eklediğiniz için teşekkürler. ışık çözümleri her zaman harikadır
ekkis

1

Benzer "startWith" sorusuna bu güzel cevabı buldum:

Bir C ++ std :: dizesinin belirli bir dizeyle başlayıp başlamadığını nasıl kontrol edebilirim ve bir alt dizeyi int'e dönüştürürüm?

Yalnızca dizenin son yerinde arama yapmak için çözümü kullanabilirsiniz:

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

Bu şekilde kısa, hızlı, standart c ++ kullanabilirsiniz ve okunabilir yapabilirsiniz.


0

Eğer benden hoşlanıyorsanız ve C ++ saflığına girmiyorsanız, işte eski bir skool melezi. Dizelerin bir avuç karakterden fazlası olmasının avantajı vardır, çünkü çoğu memcmpuygulama mümkün olduğunda makine sözcüklerini karşılaştırır.

Karakter setinin kontrolünde olmalısınız. Örneğin, bu yaklaşım utf-8 veya wchar türüyle kullanılırsa, karakter eşlemeyi desteklemeyeceği için bazı dezavantajlar vardır - örneğin, iki veya daha fazla karakter mantıksal olarak aynı olduğunda .

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

Benim görüşüm:

bool endsWith(std::string str, std::string suffix)
{
   return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.