Cari tahu apakah string berakhir dengan string lain di C ++

270

Bagaimana saya bisa mengetahui jika sebuah string diakhiri dengan string lain di C ++?

sofr
sumber

Jawaban:

211

Cukup bandingkan n karakter terakhir menggunakan 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;
}
kdt
sumber
Ya, ini adalah cara terbaik untuk melakukannya, tanpa ragu.
Noldorin
3
Saya selalu benci menghitung indeks substring, sangat rawan satu per satu ... Saya lebih suka beralih mundur dari akhir kedua string, mencoba untuk menemukan ketidakcocokan.
xtofl
17
@Noldorin saya tidak setuju. Ini adalah no-brainer - cara terbaik untuk melakukannya adalah dengan menggunakan perpustakaan. Sayang sekali perpustakaan C ++ Standard melakukan beberapa hal yang berguna.
masterxilo
1
@ masterxilo Perpustakaan apa yang Anda usulkan untuk mengatasi masalah ini dan bagaimana perpustakaan itu pilihan yang lebih baik daripada fungsi satu-baris (pada dasarnya)?
Brandin
33
@Brandin Karena ini fungsionalitas dasar. C ++ memaksa kita untuk memprogram ulang lagi dan lagi fungsi yang sama yang disediakan di luar kotak dalam bahasa komputer modern lainnya. Fakta bahwa orang perlu pergi ke stackoverflow untuk menyelesaikan pertanyaan ini menunjukkan ada pb.
Conchylicultor
175

Gunakan fungsi ini:

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());
}
Yusuf
sumber
3
Berhati-hatilah karena MSVC10 tidak menyukai solusi ini: std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()Dalam mode debug, ia melempar:_DEBUG_ERROR("string iterator not decrementable");
remi.chateauneu
154

Gunakan boost::algorithm::ends_with(lihat misalnya 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));
Andre Holzner
sumber
83

Catatan, bahwa mulai dari c ++ 20 std :: string akhirnya akan menyediakan begin_with dan ends_with . Sepertinya ada kemungkinan bahwa dengan c ++ 30 string dalam c ++ akhirnya bisa digunakan, jika Anda tidak membaca ini dari masa depan yang jauh, Anda dapat menggunakan ini dimulai Dengan / berakhir Dengan:

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

dan beberapa kelebihan pembantu ekstra:

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 ++ string jelas tidak berfungsi, dan tidak dibuat untuk digunakan dalam kode dunia nyata. Tetapi ada harapan bahwa ini akan menjadi lebih baik setidaknya.

Pavel P
sumber
2
Karena str.compare tidak mengembalikan boolean, tidak terlalu pintar untuk menguji "== 0" dengan menggunakan operator not ("!"), Karena itu mungkin membingungkan pembaca. Silakan gunakan "... && str.compare (...) == 0" untuk kejelasan.
Thomas Tempelmann
@Pavel Apakah ada alasan untuk tidak menggunakan std :: string :: find di metode "beginWith" Anda?
Maxime Oudot
4
@ MaximeOudot Tentu saja ada! Mengapa Anda ingin mencari seluruh string jika Anda perlu tahu apakah itu dimulai dengan sesuatu? Dengan kata lain, Anda mungkin akhirnya mencari string 100MB untuk menemukan bagian di akhir dan kemudian mengabaikan hasil itu karena itu bukan di awal string.
Pavel P
1
Ditambah "1" untuk prediksi c ++ 30.
Innocent Bystander
40

Saya tahu pertanyaannya untuk C ++, tetapi jika ada yang membutuhkan fungsi C yang bagus untuk melakukan ini:


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

Tom
sumber
25

The std::mismatchMetode dapat melayani tujuan ini bila digunakan untuk mundur iterate dari akhir kedua string:

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() );
xtofl
sumber
3
+1. Saya tidak pernah memperhatikan std :: mismatch () sebelumnya - Saya ingin tahu apa lagi yang ada di file header algoritme yang belum pernah saya lihat ...
j_random_hacker
3
Saya pikir itu layak pertanyaan SO sendiri: apakah Anda pernah melihat-lihat fungsi stl yang tersedia?
xtofl
2
Perhatikan bahwa ini memiliki persyaratan yang sama dengan std::equal: Anda harus memeriksa terlebih dahulu bahwa sufiks yang seharusnya tidak lebih panjang dari string yang Anda cari. Tidak mengikutinya yang mengarah ke perilaku yang tidak terdefinisi.
Rob Kennedy
18

Menurut pendapat saya paling sederhana, solusi C ++ adalah:

bool endsWith(const string& s, const string& suffix)
{
    return s.rfind(suffix) == std::abs(s.size()-suffix.size());
}
baziorek
sumber
10
Ini agak lambat karena Anda akan mencari seluruh string salih-alih hanya menguji ujungnya!
Alexis Wilke
2
@nodakai, jika saya memiliki string 1Mb, itu akan menjadi lebih dari nanodetik.
Alexis Wilke
Saya tidak berpikir begitu ... perlu melakukan strlen dalam hal apapun, dan kemudian mulai mencari dari akhir.
LtWorf
2
@LtWorf std::string::size()adalah operasi waktu-konstan; tidak perlu strlen.
Thomas
2
Bagaimana ini bisa dianggap solusi ketika gagal untuk kasus ketika suffix.size () == s.size () + 1. Potongan kode yang menunjukkan onlinegdb.com/S1ITVqKDL ini . Kompleksitas tidak relevan jika tidak bekerja dengan baik untuk semua kasus.
c0ntrol
10

Biarkan amenjadi string dan bstring yang Anda cari. Gunakan a.substruntuk mendapatkan n karakter terakhir adan bandingkan dengan b (di mana n adalah panjang b)

Atau gunakan std::equal(termasuk <algorithm>)

Ex:

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());
}
Dario
sumber
Bagaimana saya bisa mengembalikan true juga jika berakhir setelah string saya dengan \ r atau \ n atau keduanya ??? Terima kasih!
Sofr
@Dario: Solusi Anda menggunakan std :: equal () baik, yang menggunakan substr () tidak begitu banyak - kecuali Anda menggunakan string COW (dan beberapa orang yang saya percaya), substr () menyiratkan membuat salinan kedua bagian dari string, menyiratkan alokasi memori dinamis yang terlibat. Ini bisa gagal, dan dalam hal apapun berarti lebih banyak memori digunakan daripada solusi lain (dan hampir pasti lebih lambat dari solusi lain).
j_random_hacker
4

Biarkan saya memperluas solusi Joseph dengan versi case case ( 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);
        }
    );
}
Beruang kutub
sumber
3

sama seperti di atas, di sini adalah solusi saya

 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());
  }
dodjango
sumber
1
Mengapa starts_withmenggunakan 'string :: compare'? Mengapa tidak std::equal(start.begin(), start.end(), str.begin())?
Dmytro Ovdiienko
Hanya karena begin_with adalah yang pertama saya butuhkan. ends_with ditambahkan kemudian.
dodjango
3

Pilihan lain adalah menggunakan regex. Kode berikut membuat pencarian tidak sensitif terhadap huruf besar / kecil:

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

mungkin tidak begitu efisien, tetapi mudah diimplementasikan.

Julien Pilet
sumber
Bagi siapa pun dengan C ++ 11 atau lebih, ini sangat nyaman.
Clare Macrae
Waspadalah, regex bisa sangat lambat di C ++!
mxmlnkn
regex untuk ini seperti ... Saya perlu downvote ini. Saya tidak akan tetapi saya harus.
MK.
2

Anda dapat menggunakan string :: rfind

Contoh lengkap berdasarkan komentar:

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;
}
Ahmed Said
sumber
3
-1. Ya, Anda bisa menggunakannya, tetapi itu tidak perlu lambat jika string tidak berakhir dengan akhir yang disediakan - pemindaian akan terus berlanjut hingga kembali ke awal string. Juga, Anda tidak menyebutkan bahwa Anda memerlukan tes berikutnya untuk memastikan bahwa akhir cocok dengan di akhir string , daripada di tempat lain di string.
j_random_hacker
Saya hanya meletakkan tautan dari fungsi yang dibutuhkan dan saya pikir sangat mudah untuk melakukannya dari str.rfind dokumentasi (key, str.length () - key.length (), key.length ());
Ahmed Said
OK, itu efisien - tetapi dalam hal ini string :: find () akan bekerja dengan baik. Anda juga perlu menyebutkan kasus di mana key.length ()> str.length () - kode yang Anda sarankan dalam komentar Anda akan macet dalam kasus ini. Jika Anda memperbarui jawaban Anda dengan info ini, saya akan menjatuhkan -1 saya.
j_random_hacker
2

Periksa apakah str memiliki akhiran , menggunakan di bawah ini:

/*
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;
}
James Yang
sumber
2

Gunakan std :: equal algoritma from <algorithms>dengan iterasi terbalik:

std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
   
}
Sergei
sumber
2
Meskipun kode ini dapat memberikan solusi untuk pertanyaan, lebih baik menambahkan konteks mengapa / cara kerjanya. Ini dapat membantu pengguna di masa depan belajar, dan menerapkan pengetahuan itu ke kode mereka sendiri. Anda juga cenderung mendapat umpan balik positif dari pengguna dalam bentuk upvotes, ketika kode dijelaskan.
borchvm
@borchvm, menambahkan beberapa penjelasan, semoga membantu memahami
Sergei
1

Mengenai tanggapan Grzegorz Bazior. Saya menggunakan implementasi ini, tetapi yang asli memiliki bug (mengembalikan true jika saya membandingkan ".." dengan ".so"). Saya mengusulkan fungsi yang dimodifikasi:

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

Saya pikir masuk akal untuk mengirim solusi mentah yang tidak menggunakan fungsi pustaka ...

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

Menambahkan sederhana, std::tolowerkita bisa membuat case ini tidak sensitif

// 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;
}
cute_ptr
sumber
terima kasih telah menambahkan ini. solusi ringan selalu bagus
ekkis
1

Menemukan jawaban yang bagus untuk masalah "startWith" yang serupa:

Bagaimana cara memeriksa apakah string C ++ std :: dimulai dengan string tertentu, dan mengonversi substring ke int?

Anda dapat mengadopsi solusi hanya mencari di tempat terakhir di string:

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

Dengan cara ini Anda dapat membuatnya pendek, cepat, menggunakan standar c ++ dan membuatnya mudah dibaca.

ml
sumber
0

Jika Anda seperti saya dan tidak begitu menyukai C ++ purism, inilah skool hybrid lama. Ada beberapa keuntungan ketika string lebih dari beberapa karakter, karena sebagian besar memcmpimplementasi membandingkan kata-kata mesin bila memungkinkan.

Anda harus mengendalikan set karakter. Misalnya, jika pendekatan ini digunakan dengan utf-8 atau tipe wchar, ada beberapa kelemahan karena tidak akan mendukung pemetaan karakter - misalnya, ketika dua atau lebih karakter identik secara logis .

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;
}
jws
sumber
0

Dua sen saya:

bool endsWith(std::string str, std::string suffix)
{
   return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
drop table
sumber