Finden Sie heraus, ob die Zeichenfolge in C ++ mit einer anderen Zeichenfolge endet

270

Wie kann ich herausfinden, ob eine Zeichenfolge in C ++ mit einer anderen Zeichenfolge endet?

sofr
quelle

Antworten:

211

Vergleichen Sie einfach die letzten n Zeichen mit 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
quelle
Ja, das ist ohne Zweifel der beste Weg.
Noldorin
3
Ich hasse es immer, Indizes von Teilzeichenfolgen zu berechnen, es ist sehr anfällig für eins ... Ich würde lieber vom Ende beider Zeichenfolgen rückwärts iterieren und versuchen, eine Nichtübereinstimmung zu finden.
xtofl
17
@Noldorin Ich stimme nicht zu. Dies ist ein Kinderspiel - der beste Weg, dies zu tun, ist die Verwendung einer Bibliothek. Es ist eine Schande, dass die C ++ Standard-Bibliothek so wenige nützliche Dinge tut.
Masterxilo
1
@masterxilo Welche Bibliothek schlagen Sie vor, um dieses Problem zu lösen, und wie ist diese Bibliothek eine bessere Wahl als eine (im Grunde) einzeilige Funktion?
Brandin
33
@Brandin Weil es eine solche Grundfunktionalität ist. C ++ zwingt uns, immer wieder dieselben Funktionen neu zu programmieren, die in jeder anderen modernen Computersprache sofort verfügbar sind. Die Tatsache, dass Leute zum Stackoverflow gehen müssen, um diese Frage zu lösen, zeigt, dass es eine pb gibt.
Conchylicultor
175

Verwenden Sie diese Funktion:

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
quelle
3
Beachten Sie, dass MSVC10 diese Lösung nicht mag: std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()Im Debug-Modus wird _DEBUG_ERROR("string iterator not decrementable");
Folgendes ausgelöst
154

Verwendung boost::algorithm::ends_with(siehe z. B. 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
quelle
83

Beachten Sie, dass ab c ++ 20 std :: string endlich Start_with und Endes_with liefert . Es scheint, dass es eine Chance gibt, dass durch c ++ 30 Zeichenfolgen in c ++ endlich verwendbar werden. Wenn Sie dies nicht aus ferner Zukunft lesen, können Sie diese StartsWith / EndsWith verwenden:

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

und einige zusätzliche Helferüberladungen:

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 ++ - Zeichenfolgen sind eindeutig nicht funktionsfähig und wurden nicht für die Verwendung im Code der realen Welt entwickelt. Aber es besteht die Hoffnung, dass dies zumindest besser wird.

Pavel P.
quelle
2
Da str.compare keinen Booleschen Wert zurückgibt, ist es nicht so klug, mit dem Operator not ("!") Auf "== 0" zu testen, da dies für die Leser verwirrend sein kann. Bitte verwenden Sie aus Gründen der Übersichtlichkeit "... && str.compare (...) == 0".
Thomas Tempelmann
@Pavel Gibt es einen Grund, std :: string :: find in Ihren "launchWith" -Methoden nicht zu verwenden?
Maxime Oudot
4
@ MaximeOudot Natürlich gibt es! Warum sollten Sie die gesamte Zeichenfolge durchsuchen, wenn Sie wissen möchten, ob sie mit etwas beginnt? Mit anderen Worten, Sie können am Ende eine 100 MB lange Zeichenfolge durchsuchen, um das Stück am Ende zu finden, und dieses Ergebnis dann ignorieren, da es sich nicht am Anfang der Zeichenfolge befindet.
Pavel P
1
Plus "1" für die Vorhersage von c ++ 30.
Unschuldiger Zuschauer
40

Ich kenne die Frage für C ++, aber wenn jemand eine gute, altmodische C-Funktion benötigt, um dies zu tun:


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

Die std::mismatchMethode kann diesen Zweck erfüllen, wenn sie verwendet wird, um vom Ende beider Zeichenfolgen rückwärts zu iterieren:

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
quelle
3
+1. Ich hatte std :: mismatch () noch nie bemerkt - ich frage mich, was noch in dieser Algorithmus-Header-Datei enthalten ist, die ich mir noch nie angesehen habe ...
j_random_hacker
3
Ich denke, das ist eine SO-Frage wert: Haben Sie jemals die verfügbaren STL-Funktionen durchgesehen?
xtofl
2
Beachten Sie, dass dies die gleiche Anforderung hat wie std::equal: Sie müssen im Voraus überprüfen, ob das angebliche Suffix nicht länger als die Zeichenfolge ist, in der Sie danach suchen. Wenn Sie dies nicht überprüfen, führt dies zu undefiniertem Verhalten.
Rob Kennedy
18

Meiner Meinung nach ist die einfachste C ++ - Lösung:

bool endsWith(const string& s, const string& suffix)
{
    return s.rfind(suffix) == std::abs(s.size()-suffix.size());
}
Baziorek
quelle
10
Dies ist ziemlich langsam, da Sie die gesamte Zeichenfolge durchsuchen, sanstatt nur das Ende zu testen!
Alexis Wilke
2
@nodakai, wenn ich zufällig eine 1-MB-Zeichenfolge habe, ist das viel mehr als Nanosekunden.
Alexis Wilke
Ich glaube nicht ... es muss auf jeden Fall strlen und beginnt dann von Anfang an zu suchen.
LtWorf
2
@LtWorf std::string::size()ist eine Operation mit konstanter Zeit. es braucht nicht strlen.
Thomas
2
Wie kann diese Lösung überhaupt in Betracht gezogen werden, wenn sie für den Fall fehlschlägt, dass Suffix.size () == s.size () + 1. Code-Snippet, das diese onlinegdb.com/S1ITVqKDL zeigt . Komplexität ist irrelevant, wenn sie nicht in allen Fällen richtig funktioniert.
c0ntrol
10

Sei aeine Zeichenfolge und bdie Zeichenfolge, nach der du suchst. Verwenden Sie a.substrdiese Option , um die letzten n Zeichen von aabzurufen und mit b zu vergleichen (wobei n die Länge von istb ).

Oder verwenden Sie std::equal(enthalten<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
quelle
Wie kann ich true zurückgeben, auch wenn es nach meinem String mit \ r oder \ n oder beidem endet ??? Vielen Dank!
Sofr
@Dario: Ihre Lösung mit std :: same () ist gut, die mit substr () nicht so sehr - es sei denn, Sie verwenden COW-Strings (und ich glaube, nur wenige Leute), substr () impliziert das Erstellen einer zweiten Kopie eines Teils der Zeichenfolge, was eine dynamische Speicherzuweisung impliziert. Dies kann fehlschlagen und bedeutet in jedem Fall, dass mehr Speicher als andere Lösungen verwendet wird (und dies ist mit ziemlicher Sicherheit langsamer als bei anderen Lösungen).
j_random_hacker
4

Lassen Sie mich Josephs Lösung mit der Version ohne Berücksichtigung der Groß- und Kleinschreibung erweitern ( Online-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);
        }
    );
}
Eisbär
quelle
3

Genau wie oben, hier ist meine Lösung

 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
quelle
1
Warum starts_withwird 'string :: compare' verwendet? Warum nicht std::equal(start.begin(), start.end(), str.begin())?
Dmytro Ovdiienko
Nur weil start_with der erste war, den ich brauchte. Ends_with wurde später hinzugefügt.
Dodjango
3

Eine andere Möglichkeit ist die Verwendung von Regex. Der folgende Code macht die Suche unempfindlich gegenüber Groß- / Kleinschreibung:

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

wahrscheinlich nicht so effizient, aber einfach zu implementieren.

Julien Pilet
quelle
Für alle mit C ++ 11 oder höher ist dies sehr praktisch.
Clare Macrae
Achtung, Regexe können in C ++ wahnsinnig langsam sein!
mxmlnkn
Regex dafür ist wie ... Ich muss das ablehnen. Ich werde nicht, aber ich sollte.
MK.
2

Sie können string :: rfind verwenden

Das vollständige Beispiel basiert auf Kommentaren:

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
quelle
3
-1. Ja, Sie könnten es verwenden, aber es ist unnötig langsam, falls die Zeichenfolge nicht mit der angegebenen Endung endet. Der Scanvorgang wird bis zum Anfang der Zeichenfolge fortgesetzt. Außerdem erwähnen Sie nicht, dass Sie einen nachfolgenden Test benötigen, um sicherzustellen, dass das Ende am Ende der Zeichenfolge und nicht an anderer Stelle in der Zeichenfolge übereinstimmt .
j_random_hacker
Ich habe nur den Link der benötigten Funktion eingefügt und ich denke, es ist sehr einfach, dies aus der Dokumentation zu tun. Str.rfind (key, str.length () - key.length (), key.length ());
Ahmed sagte
OK, das ist effizient - aber in diesem Fall würde string :: find () genauso gut funktionieren. Außerdem müssen Sie den Fall erwähnen, in dem key.length ()> str.length () - der Code, den Sie in Ihrem Kommentar vorschlagen, in diesem Fall abstürzt. Wenn Sie Ihre Antwort mit diesen Informationen aktualisieren, lasse ich meine -1 fallen.
j_random_hacker
2

Überprüfen Sie, ob str das Suffix hat , indem Sie Folgendes verwenden:

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

Verwenden Sie den Algorithmus std :: same <algorithms>mit umgekehrter Iteration:

std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
   
}
Sergei
quelle
2
Obwohl dieser Code eine Lösung für die Frage bietet, ist es besser, einen Kontext hinzuzufügen, warum / wie er funktioniert. Dies kann zukünftigen Benutzern helfen, zu lernen und dieses Wissen auf ihren eigenen Code anzuwenden. Es ist auch wahrscheinlich, dass Sie positive Rückmeldungen von Benutzern in Form von Upvotes erhalten, wenn der Code erklärt wird.
Borchvm
@borchvm, fügte einige Erklärungen hinzu, hoffe, es hilft zu verstehen
Sergei
1

In Bezug auf Grzegorz Bazior Antwort. Ich habe diese Implementierung verwendet, aber die ursprüngliche hat einen Fehler (gibt true zurück, wenn ich ".." mit ".so" vergleiche). Ich schlage eine modifizierte Funktion vor:

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

Ich fand es sinnvoll, eine Rohlösung zu veröffentlichen, die keine Bibliotheksfunktionen verwendet ...

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

Wenn std::tolowerwir eine einfache hinzufügen, können wir diesen Fall unempfindlich machen

// 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
quelle
Vielen Dank für das Hinzufügen. Lichtlösungen sind immer großartig
ekkis
1

Fand diese nette Antwort auf das ähnliche "startWith" -Problem:

Wie überprüfe ich, ob ein C ++ std :: string mit einem bestimmten String beginnt, und konvertiere einen Teilstring in einen int?

Sie können die Lösung verwenden, um nur an der letzten Stelle in der Zeichenfolge zu suchen:

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

Auf diese Weise können Sie es kurz und schnell machen, Standard-C ++ verwenden und es lesbar machen.

mls
quelle
0

Wenn Sie wie ich sind und sich nicht für C ++ - Purismus interessieren, dann ist hier ein alter Skool-Hybrid. Es gibt einige Vorteile, wenn Zeichenfolgen mehr als eine Handvoll Zeichen enthalten, da die meisten memcmpImplementierungen Maschinenwörter vergleichen, wenn dies möglich ist.

Sie müssen die Kontrolle über den Zeichensatz haben. Wenn dieser Ansatz beispielsweise mit utf-8 oder wchar verwendet wird, gibt es einige Nachteile, da er die Zeichenzuordnung nicht unterstützt - z. B. wenn zwei oder mehr Zeichen logisch identisch sind .

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
quelle
0

Meine zwei Cent:

bool endsWith(std::string str, std::string suffix)
{
   return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
Drop-Tabelle
quelle