Scopri se la stringa termina con un'altra stringa in C ++


270

Come posso scoprire se una stringa termina con un'altra stringa in C ++?

Risposte:


211

Confronta semplicemente gli ultimi n caratteri usando 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;
}

Sì, questo è il modo migliore per farlo, senza dubbio.
Noldorin,

3
Odio sempre calcolare gli indici delle sottostringhe, è molto incline a uno ... Vorrei piuttosto scorrere indietro alla fine di entrambe le stringhe, cercando di trovare una discrepanza.
xtofl,

17
@Noldorin Non sono d'accordo. Questo è un gioco da ragazzi: il modo migliore per farlo è usare una libreria. È un peccato che la libreria C ++ Standard faccia così poche cose utili.
masterxilo,

1
@masterxilo Quale libreria proponete di risolvere questo problema e in che modo tale libreria è una scelta migliore di una funzione (sostanzialmente) a una riga?
Brandin

33
@Brandin Perché è una tale funzionalità di base. Il C ++ ci costringe a riprogrammare ancora e ancora le stesse funzionalità che sono fornite immediatamente in qualsiasi altro linguaggio informatico moderno. Il fatto che le persone debbano andare su StackOverflow per risolvere questa domanda mostra che c'è un pb.
Conchylicultor,

175

Usa questa funzione:

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
Attenzione che a MSVC10 non piace questa soluzione: std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()in modalità debug, genera:_DEBUG_ERROR("string iterator not decrementable");
remi.chateauneu,

154

Usa boost::algorithm::ends_with(vedi ad esempio 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

Nota che a partire da c ++ 20 std :: string fornirà infine start_with e End_with . Sembra che ci sia la possibilità che da c ++ 30 stringhe in c ++ possano finalmente diventare utilizzabili, se non stai leggendo questo da un futuro lontano, puoi usare questi inizi Con / Fine Con

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

e alcuni sovraccarichi di aiuto aggiuntivi:

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

Le stringhe IMO, c ++ sono chiaramente disfunzionali e non sono state progettate per essere utilizzate nel codice del mondo reale. Ma c'è una speranza che questo possa migliorare almeno.


2
Poiché str.compare non restituisce un valore booleano, non è così intelligente testare "== 0" utilizzando l'operatore not ("!"), Poiché ciò potrebbe confondere i lettori. Si prega di utilizzare "... && str.compare (...) == 0" per chiarezza.
Thomas Tempelmann,

@Pavel C'è un motivo per non usare std :: string :: find nei tuoi metodi "startWith"?
Maxime Oudot,

4
@MaximeOudot Ovviamente c'è! Perché dovresti cercare l'intera stringa se hai bisogno di sapere se inizia con qualcosa? In altre parole, potresti finire per cercare una stringa lunga 100 mb per trovare il pezzo alla fine e quindi ignorare quel risultato perché non è all'inizio della stringa.
Pavel P,

1
Più "1" per la previsione c ++ 30.
Innocent Bystander il

40

Conosco la domanda per C ++, ma se qualcuno ha bisogno di una buona funzione C vecchio stile per fare questo:


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

Il std::mismatchmetodo può servire a questo scopo quando usato per iterare all'indietro dalla fine di entrambe le stringhe:

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. Non avevo mai notato std :: mismatch () prima - mi chiedo cos'altro c'è in quel file di intestazione degli algoritmi che non ho mai visto ...
j_random_hacker,

3
Penso che valga una domanda SO da sola: hai mai sfogliato le funzioni stl disponibili?
xtofl,

2
Si noti che questo ha lo stesso requisito di std::equal: è necessario verificare in anticipo che il supposto suffisso non sia più lungo della stringa in cui lo si sta cercando. Trascurare di verificare ciò porta a un comportamento indefinito.
Rob Kennedy,

18

A mio avviso, la soluzione C ++ più semplice è:

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

10
Questo è piuttosto lento poiché cercherai l'intera stringa sinvece di testarne la fine!
Alexis Wilke,

2
@nodakai, se mi capita di avere una stringa da 1 Mb, sarà molto più di nanosecondi.
Alexis Wilke,

Non penso proprio ... in ogni caso bisogna fare un po 'di sforzo, e poi inizia a guardare dalla fine.
Ten .orf

2
@LtWorf std::string::size()è un'operazione a tempo costante; non serve strlen.
Thomas

2
Come può essere considerata questa soluzione anche se fallisce nel caso in cui suffix.size () == s.size () + 1. Snippet di codice che mostra questo onlinegdb.com/S1ITVqKDL . La complessità è irrilevante se non funziona correttamente in tutti i casi.
c0ntrol,

10

Lascia che asia una stringa e bla stringa che cerchi. Utilizzare a.substrper ottenere gli ultimi n caratteri di ae confrontarli con b (dove n è la lunghezza di b)

Oppure usa std::equal(includi <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());
}

Come posso restituire true anche se termina dopo la mia stringa con \ r o \ n o entrambi ??? Grazie!
sofr,

@Dario: la tua soluzione usando std :: equal () è buona, quella che usa substr () non tanto - a meno che tu non stia usando stringhe COW (e credo che poche persone), substr () implica la creazione di una seconda copia di parte della stringa, implicando l'allocazione dinamica della memoria. Ciò può non riuscire e, in ogni caso, significa che viene utilizzata più memoria rispetto ad altre soluzioni (ed è quasi certamente più lenta di altre soluzioni).
j_random_hacker,

4

Vorrei estendere la soluzione di Joseph con la versione senza distinzione tra maiuscole e minuscole ( 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);
        }
    );
}

3

esattamente come sopra, ecco la mia soluzione

 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
Perché starts_withusa 'string :: compare'? Perché no std::equal(start.begin(), start.end(), str.begin())?
Dmytro Ovdiienko il

Solo perché inizia_con era il primo di cui avevo bisogno. end_with è stato aggiunto in seguito.
dodjango,

3

un'altra opzione è usare regex. Il codice seguente rende la ricerca insensibile alle maiuscole / minuscole:

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

probabilmente non così efficiente, ma facile da implementare.


Per chiunque abbia C ++ 11 o versioni successive, questo è molto conveniente.
Clare Macrae,

Attenzione, le regex possono essere follemente lente in C ++!
mxmlnkn

regex per questo è come ... Devo sottovalutare questo. Non lo farò ma dovrei.
MK.

2

puoi usare string :: rfind

L'esempio completo basato sui commenti:

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. Sì, potresti usarlo, ma è inutilmente lento nel caso in cui la stringa non finisca con la fine fornita - la scansione continuerà fino all'inizio della stringa. Inoltre, non accennare al fatto che è necessario un test successivo per assicurarsi che la fine corrisponda alla fine della stringa , piuttosto che altrove nella stringa.
j_random_hacker,

Ho appena messo il link della funzione necessaria e penso che sia molto facile farlo dalla documentazione str.rfind (key, str.length () - key.length (), key.length ());
Ahmed Said,

OK, è efficiente, ma in questo caso string :: find () funzionerebbe altrettanto bene. Inoltre, è necessario menzionare il caso in cui key.length ()> str.length () - il codice che suggerisci nel tuo commento andrà in crash in questo caso. Se aggiorni la tua risposta con queste informazioni, lascerò cadere il mio -1.
j_random_hacker,

2

Controlla se str ha il suffisso , usando di seguito:

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

Usa l'algoritmo std :: equal <algorithms>con iterazione inversa:

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

2
Sebbene questo codice possa fornire una soluzione alla domanda, è meglio aggiungere un contesto sul perché / come funziona. Questo può aiutare gli utenti futuri a imparare e applicare tali conoscenze al proprio codice. È anche probabile che tu riceva un feedback positivo dagli utenti sotto forma di voti positivi, quando viene spiegato il codice.
Borchvm,

@borchvm, ha aggiunto alcune spiegazioni, spero che aiuti a comprendere
Sergei

1

Per quanto riguarda la risposta di Grzegorz Bazior. Ho usato questa implementazione, ma quella originale ha un bug (restituisce vero se confronto ".." con ".so"). Propongo funzione modificata:

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

1

Ho pensato che fosse logico pubblicare una soluzione grezza che non utilizza alcuna funzione di libreria ...

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

Aggiungendo un semplice std::tolowerpossiamo rendere insensibile questo caso

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

grazie per averlo aggiunto. le soluzioni di luce sono sempre fantastiche
ekkis il

1

Ho trovato questa bella risposta al simile problema "startWith":

Come posso verificare se uno std :: string C ++ inizia con una determinata stringa e convertire una sottostringa in un int?

Puoi adottare la soluzione per cercare solo l'ultimo posto nella stringa:

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

In questo modo puoi renderlo breve, veloce, usare lo standard c ++ e renderlo leggibile.


0

Se sei come me e non così nel purismo C ++, ecco un vecchio ibrido skool. C'è qualche vantaggio quando le stringhe sono più di una manciata di caratteri, poiché la maggior parte delle memcmpimplementazioni confronta le parole automatiche quando possibile.

Devi avere il controllo del set di caratteri. Ad esempio, se questo approccio viene utilizzato con il tipo utf-8 o wchar, c'è qualche svantaggio in quanto non supporta la mappatura dei caratteri, ad esempio quando due o più caratteri sono logicamente identici .

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

I miei due centesimi:

bool endsWith(std::string str, std::string suffix)
{
   return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.