Wie vergleicht man die Enden von Strings in C?

Lesezeit: 7 Minuten

Ich möchte sicherstellen, dass meine Zeichenfolge mit “.foo” endet. Ich benutze C, eine Sprache, mit der ich nicht ganz vertraut bin. Der beste Weg, den ich gefunden habe, ist unten. Irgendwelche C-Gurus wollen sicherstellen, dass ich das elegant und klug mache?

int EndsWithFoo(char *str)
{
    if(strlen(str) >= strlen(".foo"))
    {
        if(!strcmp(str + strlen(str) - strlen(".foo"), ".foo"))
        {
            return 1;
        }
    }
    return 0;
}

  • 25 Antworten, nur 4 oder 5 ohne Probleme.

    – chqrlie

    16. August 2021 um 17:31 Uhr

Benutzeravatar von plinth
Sockel

Rufen Sie strlen nicht mehr als einmal pro String auf.

int EndsWith(const char *str, const char *suffix)
{
    if (!str || !suffix)
        return 0;
    size_t lenstr = strlen(str);
    size_t lensuffix = strlen(suffix);
    if (lensuffix >  lenstr)
        return 0;
    return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}

int EndsWithFoo(const char *str) { return EndsWith(str, ".foo"); }

BEARBEITEN: NULL-Prüfung für die Umständlichkeit hinzugefügt. Für die Ultra-Pedantisten, diskutieren Sie, ob es ungleich Null zurückgeben sollte, wenn sowohl str als auch suffix beide NULL sind.

  • In diesem Fall können Sie strcmp() anstelle von strncmp() (oder sogar memcmp()) verwenden, da wir zu diesem Zeitpunkt genau wissen, wie viele Zeichen in beiden Strings noch übrig sind, obwohl der Geschwindigkeitsunterschied kaum wahrnehmbar ist .

    – Adam Rosenfield

    13. April 2009 um 18:23 Uhr

  • jeder Aufruf von strlen verschwindet sowieso aus der Assembly, sobald Sie Optimierungen einschalten, also ist das wahrscheinlich ein Fall von vorzeitiger Optimierung (obwohl C-Strings eklig genug sind, um über solche Probleme nachzudenken).

    – Joey

    13. April 2009 um 18:37 Uhr

  • @Johannes: Wie kann das für andere Zeichenfolgen als zur Kompilierzeit bekannte Zeichenfolgenliterale sein? Sicherlich können Sie den strlen-Code einbetten, aber auf einer bestimmten Ebene müssen Sie immer noch die Länge der Zeichenfolge finden. Bei konstanten Zeichenfolgenliteralen weiß der Compiler, wie lang es ist, aber das gilt im Allgemeinen nicht. Gedanken?

    – Matt J

    13. April 2009 um 19:20 Uhr

  • Wenn !str && !suffix, wäre es nicht sinnvoller, 1 zurückzugeben?

    – wilhelmtel

    14. April 2009 um 19:18 Uhr

  • @MattJ: etwas spät …, aber ich nehme an, Adam meinte, dass der Compiler alle Aufrufe außer dem ersten für jede Zeichenfolge entfernen wird, da sie nicht geändert werden. da hast du natürlich recht nicht alle davon können wegoptimiert werden.

    – stefankt

    19. April 2014 um 20:05 Uhr

int EndsWithFoo( char *string )
{
  string = strrchr(string, '.');

  if( string != NULL )
    return( strcmp(string, ".foo") );

  return( -1 );
}

Gibt 0 zurück, wenn es mit “.foo” endet.

  • bescheidener Bogen Sie sind sehr lieb 🙂

    Benutzer82238

    13. April 2009 um 21:13 Uhr

  • Rückgabewerte sollten umgekehrt werden, da Null falsch und Nicht-Null wahr ist und der Name der Funktion auf eine boolesche Rückgabe hinweist.

    – Traumlax

    14. April 2009 um 0:17 Uhr

  • @dreamlax -> ja, das stimmt

    Benutzer82238

    14. April 2009 um 6:34 Uhr

  • @Eichhörnchen strcmp prüft beide Strings auf das Null-Terminatorzeichen. Wenn eine Zeichenfolge kürzer als die andere ist, wird sie einen Wert ungleich Null zurückgeben. Es wird nicht überlesen.

    – cgmb

    15. November 2019 um 0:49 Uhr


  • Die Verwendung der Variablen “string” scheint mir keine gute Idee zu sein, da sie mit std::string in c++ kollidieren kann. Ich bin mir nicht sicher, wie gut der Compiler das kann, aber ich würde auch die Verwendung von const char * empfehlen. Also wird es int EndsWithFoo(const char *s1 ) { const char * s2 = strrchr(s1, ‘.’); …. und jetzt alles mit s2 weiter.

    – Tõnu Samuel

    27. Oktober 2020 um 10:37 Uhr


Ich habe gerade keinen Zugriff auf einen Compiler, könnte mir also jemand sagen, ob das funktioniert?

#include <stdio.h>
#include <string.h>

int EndsWithFoo(const char* s);

int
main(void)
{
  printf("%d\n", EndsWithFoo("whatever.foo"));

  return 0;
}

int EndsWithFoo(const char* s)
{
  int ret = 0;

  if (s != NULL)
  {
    size_t size = strlen(s);

    if (size >= 4 &&
        s[size-4] == '.' &&
        s[size-3] == 'f' &&
        s[size-2] == 'o' &&
        s[size-1] == 'o')
    {
      ret = 1;
    }
  }

  return ret;
}

Achten Sie auf jeden Fall darauf, den Parameter als zu qualifizieren constteilt es allen (einschließlich dem Compiler) mit, dass Sie nicht beabsichtigen, die Zeichenfolge zu ändern.

  • +1 am besten optimiert. Ich bevorzuge eine solche Version, wenn sich ‘foo’ nicht ändert!

    – dirkgent

    13. April 2009 um 18:07 Uhr

  • Nur ein Tipp: Wenn Sie eine Internetverbindung haben, steht Ihnen auf codepad.org ein C-Compiler zur Verfügung

    – John Cromartie

    13. April 2009 um 18:10 Uhr

  • Murphy’s Law besagt, dass “.foo” sich ändern wird und zwar im ungünstigsten Moment.

    – Sockel

    13. April 2009 um 18:16 Uhr

  • Es ist nicht “optimiert”, da Sie den .foo-Teil zweimal durchlesen – einmal für strlen, einmal zum Vergleich. Aber gut und praktisch. +1

    – Aib

    13. April 2009 um 21:41 Uhr

  • Ich bin mir ziemlich sicher, dass eine Standardversion mit einer Schleife und const char * sowieso mit -O3 auf so etwas optimiert werden würde.

    – Samuel Danielson

    28. Januar 2016 um 23:28 Uhr

Hier ist eine allgemeine Lösung, die dieselben Werte wie Pythons str.endswith() zurückgibt, wobei memcmp() verwendet wird. Es ist beabsichtigt, str / suffix nicht auf NULL zu prüfen, andere libc str-Funktionen prüfen ebenfalls nicht auf NULL:

int ends_with(const char *str, const char *suffix) {
  size_t str_len = strlen(str);
  size_t suffix_len = strlen(suffix);

  return (str_len >= suffix_len) &&
         (!memcmp(str + str_len - suffix_len, suffix, suffix_len));
}

Prüfung C:

printf("%i\n", ends_with("", ""));
printf("%i\n", ends_with("", "foo"));
printf("%i\n", ends_with("foo", ""));
printf("%i\n", ends_with("foo", "foo"));
printf("%i\n", ends_with("foo", "foobar"));
printf("%i\n", ends_with("foo", "barfoo"));
printf("%i\n", ends_with("foobar", "foo"));
printf("%i\n", ends_with("barfoo", "foo"));
printf("%i\n", ends_with("foobarfoo", "foo"));

Ergebnis C:

1
0
1
1
0
0
0
1
1

Test-Python:

print("".endswith(""))
print("".endswith("foo"))
print("foo".endswith(""))
print("foo".endswith("foo"))
print("foo".endswith("foobar"))
print("foo".endswith("barfoo"))
print("foobar".endswith("foo"))
print("barfoo".endswith("foo"))
print("foobarfoo".endswith("foo"))

Ergebnis-Python:

True
False
True
True
False
False
False
True
True

Benutzeravatar von wilhelmtell
wilhelmell

Wenn Sie die Signatur Ihrer Funktion ändern können, ändern Sie sie in

int EndsWith(char const * str, char const * suffix, int lenstr, int lensuf);

Dies führt zu einem sichereren, wiederverwendbaren und effizienteren Code:

  1. Die hinzugefügten konstanten Qualifizierer stellen sicher, dass Sie die Eingabezeichenfolgen nicht versehentlich ändern. Diese Funktion ist ein Prädikat, daher gehe ich davon aus, dass sie niemals Nebenwirkungen haben soll.
  2. Das zu vergleichende Suffix wird als Parameter übergeben, sodass Sie diese Funktion zur späteren Wiederverwendung mit anderen Suffixen speichern können.
  3. Diese Signatur gibt Ihnen die Möglichkeit, die Längen der Saiten anzugeben, wenn Sie sie bereits kennen. Wir nennen das dynamische Programmierung.

Wir können die Funktion wie folgt definieren:

int EndsWith(char const * str, char const * suffix, int lenstr, int lensuf)
{
    if( ! str && ! suffix ) return 1;
    if( ! str || ! suffix ) return 0;
    if( lenstr < 0 ) lenstr = strlen(str);
    if( lensuf < 0 ) lensuf = strlen(suffix);
    return strcmp(str + lenstr - lensuf, suffix) == 0;
}

Das offensichtliche Gegenargument für die zusätzlichen Parameter ist, dass sie mehr Rauschen im Code oder einen weniger ausdrucksstarken Code implizieren.

  • Ihr Code hat undefiniertes Verhalten, wenn lenstr < lensufob als Argumentwerte oder wie berechnet mit strlen(). Ein weiteres Gegenargument ist int hat einen kleineren positiven Bereich als size_t.

    – chqrlie

    16. August 2021 um 17:08 Uhr


Das strlen(".foo")s sind nicht erforderlich. Wenn Sie es wirklich flexibel haben wollten, könnten Sie es verwenden sizeof ".foo" - 1 — eine Kompilierzeitkonstante.

Auch eine Null-String-Prüfung wäre gut.

  • Ihr Code hat undefiniertes Verhalten, wenn lenstr < lensufob als Argumentwerte oder wie berechnet mit strlen(). Ein weiteres Gegenargument ist int hat einen kleineren positiven Bereich als size_t.

    – chqrlie

    16. August 2021 um 17:08 Uhr


Getesteter Code, beinhaltet den Test:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int ends_with_foo(const char *str)
{
    char *dot = strrchr(str, '.');

    if (NULL == dot) return 0;
    return strcmp(dot, ".foo") == 0;
}

int main (int argc, const char * argv[]) 
{
    char *test[] = { "something", "anotherthing.foo" };
    int i;

    for (i = 0; i < sizeof(test) / sizeof(char *); i++) {
        printf("'%s' ends %sin '.foo'\n",
               test[i],
               ends_with_foo(test[i]) ? "" : "not ");
    }
    return 0;
}

  • Schnapp! Obwohl ich mir nicht die Mühe mache, das Ergebnis von strcmp () zu vergleichen, gebe ich es einfach direkt zurück.

    Benutzer82238

    13. April 2009 um 18:26 Uhr

  • Sie gehen davon aus, dass es kein anderes ‘.’ in der Eingabezeichenfolge.

    – Naveen

    13. April 2009 um 18:50 Uhr

  • Es macht keinen Unterschied. strcmp() gibt nur dann 0 zurück, wenn es zwei identische Strings gibt (zB die Länge muss gleich sein). Der Vergleich wird vorzeitig beendet, wenn sich die Zeichenfolgen in der Länge unterscheiden.

    Benutzer82238

    13. April 2009 um 19:05 Uhr

  • @Blank Xavier: Die Version in der Frage gibt 1 zurück, wenn sie mit “.foo” endet. Nur das Ergebnis von strcmp() zurückzugeben, würde bei Erfolg 0 zurückgeben.

    – stesch

    13. April 2009 um 19:12 Uhr

  • @Naveen: strrchr() gibt das letzte Vorkommen des gesuchten Zeichens zurück.

    – stesch

    13. April 2009 um 19:15 Uhr

1411540cookie-checkWie vergleicht man die Enden von Strings in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy