Découvrez si la chaîne se termine par une autre chaîne en C ++

270

Comment savoir si une chaîne se termine par une autre chaîne en C ++?

sofr
la source

Réponses:

211

Comparez simplement les n derniers caractères en utilisant 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
la source
Oui, c'est sans aucun doute la meilleure façon de le faire.
Noldorin
3
Je déteste toujours calculer les indices des sous-chaînes, c'est très enclin à un ... Je préfère répéter en arrière à partir de la fin des deux chaînes, en essayant de trouver une incompatibilité.
xtofl
17
@Noldorin Je ne suis pas d'accord. C'est une évidence - la meilleure façon de le faire est d'utiliser une bibliothèque. C'est dommage que la bibliothèque C ++ Standard fasse si peu de choses utiles.
masterxilo
1
@masterxilo Quelle bibliothèque proposez-vous pour résoudre ce problème et en quoi cette bibliothèque est-elle un meilleur choix qu'une fonction (essentiellement) à une ligne?
Brandin
33
@Brandin Parce que c'est une telle fonctionnalité de base. C ++ nous oblige à reprogrammer encore et encore les mêmes fonctionnalités qui sont fournies dès le départ dans tout autre langage informatique moderne. Le fait que les gens doivent aller sur stackoverflow pour résoudre cette question montre qu'il y a un pb.
Conchylicultor
175

Utilisez cette fonction:

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());
}
Joseph
la source
3
Attention, MSVC10 n'aime pas cette solution: std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()En mode débogage, il lance:_DEBUG_ERROR("string iterator not decrementable");
remi.chateauneu
154

Utilisation boost::algorithm::ends_with(voir par exemple 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
la source
83

Notez que partir de c ++ 20 std :: string fournira finalement begin_with et ends_with . Il semble qu'il y ait une chance qu'en c ++ 30 chaînes en c ++ puissent enfin devenir utilisables, si vous ne lisez pas ceci dans un avenir lointain, vous pouvez utiliser ces départs avec / fin avec:

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

et quelques surcharges auxiliaires supplémentaires:

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, les chaînes c ++ sont clairement dysfonctionnelles et n'ont pas été conçues pour être utilisées dans le code du monde réel. Mais il y a un espoir que cela s'améliorera au moins.

Pavel P
la source
2
Puisque str.compare ne renvoie pas de booléen, il n'est pas si intelligent de tester "== 0" en utilisant l'opérateur not ("!"), Car cela peut être source de confusion pour les lecteurs. Veuillez utiliser "... && str.compare (...) == 0" pour plus de clarté.
Thomas Tempelmann
@Pavel Y a-t-il une raison de ne pas utiliser std :: string :: find dans vos méthodes "startsWith"?
Maxime Oudot
4
@MaximeOudot Bien sûr qu'il y en a! Pourquoi voudriez-vous rechercher une chaîne entière si vous avez besoin de savoir si elle commence par quelque chose? En d'autres termes, vous pouvez finir par rechercher une chaîne longue de 100 Mo pour trouver la pièce à la fin, puis ignorer ce résultat car ce n'est pas au début de la chaîne.
Pavel P
1
Plus "1" pour la prédiction c ++ 30.
Innocent Bystander
40

Je connais la question pour C ++, mais si quelqu'un a besoin d'une bonne fonction C à l'ancienne pour le faire:


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

À M
la source
25

La std::mismatchméthode peut servir cet objectif lorsqu'elle est utilisée pour effectuer une itération en arrière à partir de la fin des deux chaînes:

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
la source
3
+1. Je n'avais jamais remarqué std :: mismatch () auparavant - je me demande ce qu'il y a d'autre dans ce fichier d'en-tête d'algorithmes que je n'ai jamais regardé ...
j_random_hacker
3
Je pense que cela vaut une question SO en soi: avez-vous déjà parcouru les fonctions stl disponibles?
xtofl
2
Notez que cela a la même exigence que std::equal: vous devez vérifier à l'avance que le suffixe supposé n'est pas plus long que la chaîne dans laquelle vous le recherchez. Négliger de vérifier cela conduit à un comportement indéfini.
Rob Kennedy
18

À mon avis, la solution C ++ est la plus simple:

bool endsWith(const string& s, const string& suffix)
{
    return s.rfind(suffix) == std::abs(s.size()-suffix.size());
}
baziorek
la source
10
C'est assez lent car vous chercherez la chaîne entière sau lieu de simplement tester la fin de celle-ci!
Alexis Wilke
2
@nodakai, s'il m'arrive d'avoir une chaîne de 1 Mo, ça va être beaucoup plus que des nanosecondes.
Alexis Wilke
Je ne le pense pas… il doit faire du strlen dans tous les cas, puis commence à chercher depuis la fin.
LtWorf
2
@LtWorf std::string::size()est une opération à temps constant; il n'a pas besoin strlen.
Thomas
2
Comment peut-on même considérer cette solution quand elle échoue dans le cas où suffix.size () == s.size () + 1. Extrait de code montrant ce onlinegdb.com/S1ITVqKDL . La complexité est sans importance si elle ne fonctionne pas correctement pour tous les cas.
c0ntrol
10

Soit aune chaîne et bla chaîne que vous recherchez. Utilisez a.substrpour obtenir les n derniers caractères de aet les comparer à b (où n est la longueur de b)

Ou utiliser std::equal(inclure <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
la source
Comment puis-je retourner vrai si cela se termine après ma chaîne avec \ r ou \ n ou les deux ??? Merci!
sofr
@Dario: Votre solution utilisant std :: equal () est bonne, celle qui utilise substr () pas tellement - à moins que vous n'utilisiez des chaînes COW (et peu de gens le croient), substr () implique de créer une deuxième copie d'une partie de la chaîne, ce qui implique une allocation dynamique de mémoire. Cela peut échouer, et dans tous les cas, cela signifie que plus de mémoire est utilisée que d'autres solutions (et c'est presque certainement plus lent que d'autres solutions).
j_random_hacker
4

Permettez-moi d'étendre la solution de Joseph avec la version insensible à la casse ( démo en ligne )

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);
        }
    );
}
Ours polaire
la source
3

la même chose que ci-dessus, voici ma solution

 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
la source
1
Pourquoi starts_withutilise 'string :: compare'? Pourquoi ne pas std::equal(start.begin(), start.end(), str.begin())?
Dmytro Ovdiienko
Tout simplement parce que starts_with était le premier dont j'avais besoin. ends_with a été ajouté plus tard.
dodjango
3

une autre option consiste à utiliser l'expression régulière. Le code suivant rend la recherche insensible aux majuscules / minuscules:

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

probablement pas aussi efficace, mais facile à mettre en œuvre.

Julien Pilet
la source
Pour toute personne avec C ++ 11 ou supérieur, c'est très pratique.
Clare Macrae
Attention, les expressions régulières peuvent être incroyablement lentes en C ++!
mxmlnkn
regex pour cela est comme ... je dois downvote cela. Je ne le ferai pas mais je devrais.
MK.
2

vous pouvez utiliser string :: rfind

L'exemple complet basé sur les commentaires:

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
la source
3
-1. Oui, vous pouvez l'utiliser, mais c'est inutilement lent dans le cas où la chaîne ne se termine pas avec la fin fournie - l'analyse continuera jusqu'au début de la chaîne. En outre, vous ne mentionnez pas que vous avez besoin d'un test ultérieur pour vous assurer que la fin correspond à la fin de la chaîne , plutôt qu'ailleurs dans la chaîne.
j_random_hacker
Je viens de mettre le lien de la fonction nécessaire et je pense qu'il est très facile de le faire à partir de la documentation str.rfind (key, str.length () - key.length (), key.length ());
Ahmed Said
OK, c'est efficace - mais dans ce cas, string :: find () fonctionnerait tout aussi bien. Vous devez également mentionner le cas où key.length ()> str.length () - le code que vous proposez dans votre commentaire se bloquera dans ce cas. Si vous mettez à jour votre réponse avec cette information, je laisserai tomber mon -1.
j_random_hacker
2

Vérifiez si str a un suffixe , en utilisant ci-dessous:

/*
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
la source
2

Utilisez l'algorithme std :: equal <algorithms>avec une itération inverse:

std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
   
}
Sergei
la source
2
Bien que ce code puisse fournir une solution à la question, il est préférable d'ajouter du contexte pour expliquer pourquoi / comment cela fonctionne. Cela peut aider les futurs utilisateurs à apprendre et à appliquer ces connaissances à leur propre code. Vous êtes également susceptible d'avoir des commentaires positifs des utilisateurs sous forme de votes positifs, lorsque le code est expliqué.
borchvm
@borchvm, a ajouté quelques explications, j'espère que cela aide à comprendre
Sergei
1

Concernant la réponse de Grzegorz Bazior. J'ai utilisé cette implémentation, mais celle d'origine a un bug (retourne vrai si je compare ".." avec ".so"). Je propose une fonction modifiée:

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

J'ai pensé qu'il était logique de publier une solution brute qui n'utilise aucune fonction de bibliothèque ...

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

En ajoutant un simple, std::tolowernous pouvons rendre ce cas insensible

// 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
la source
merci d'avoir ajouté ceci. les solutions d'éclairage sont toujours excellentes
ekkis
1

J'ai trouvé cette belle réponse au problème similaire "startWith":

Comment puis-je vérifier si une chaîne std :: C ++ commence par une certaine chaîne et convertir une sous-chaîne en int?

Vous pouvez adopter la solution pour rechercher uniquement à la dernière place de la chaîne:

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

De cette façon, vous pouvez le rendre court, rapide, utiliser le c ++ standard et le rendre lisible.

mls
la source
0

Si vous êtes comme moi et que vous n'aimez pas le purisme C ++, voici un vieil hybride de skool. Il y a un avantage lorsque les chaînes sont plus d'une poignée de caractères, car la plupart des memcmpimplémentations comparent les mots machine lorsque cela est possible.

Vous devez contrôler le jeu de caractères. Par exemple, si cette approche est utilisée avec le type utf-8 ou wchar, il y a un inconvénient car elle ne prend pas en charge le mappage de caractères - par exemple, lorsque deux ou plusieurs caractères sont logiquement identiques .

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
la source
0

Mes deux centimes:

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