Was sind die Unterschiede zwischen strtok und strsep in C

Lesezeit: 11 Minuten

Benutzer-Avatar
mizuki

Kann mir jemand erklären, welche Unterschiede es da gibt strtok() und strsep()? Welche Vor- und Nachteile haben sie? Und warum sollte ich das eine dem anderen vorziehen?

Benutzer-Avatar
Jonathan Leffler

Ein wesentlicher Unterschied zw strtok() und strsep() ist das strtok() ist standardisiert (durch den C-Standard und damit auch durch POSIX), aber strsep() ist nicht standardisiert (von C oder POSIX; es ist in der GNU C Library verfügbar und stammt von BSD). Daher wird eher portabler Code verwendet strtok() als strsep().

Ein weiterer Unterschied besteht darin, dass Anrufe an die strsep() Funktion auf verschiedenen Zeichenfolgen kann verschachtelt werden, während Sie dies mit nicht tun können strtok() (obwohl Sie mit strtok_r()). Also verwenden strsep() in einer Bibliothek beschädigt nicht versehentlich anderen Code, während die Verwendung von strtok() in einer Bibliotheksfunktion muss dokumentiert werden, da anderer Code verwendet wird strtok() gleichzeitig kann die Bibliotheksfunktion nicht aufgerufen werden.

Die Handbuchseite für strsep() bei Kernel.org sagt:

Die Funktion strsep() wurde als Ersatz für strtok(3) eingeführt, da letztere keine leeren Felder verarbeiten kann.

Der andere große Unterschied ist also derjenige, der von George Gaál in seiner Antwort hervorgehoben wurde. strtok() erlaubt mehrere Trennzeichen zwischen einem einzelnen Token, wohingegen strsep() erwartet ein einzelnes Trennzeichen zwischen Token und interpretiert benachbarte Trennzeichen als leeres Token.

Beide strsep() und strtok() ändern ihre Eingabezeichenfolgen und lassen Sie auch nicht erkennen, welches Trennzeichen das Ende des Tokens markiert hat (da beide eine NUL schreiben '\0' über das Trennzeichen nach dem Ende des Tokens).

Wann verwendet man sie?

  • Du würdest verwenden strsep() wenn Sie leere Tokens möchten, anstatt mehrere Trennzeichen zwischen Tokens zuzulassen, und wenn Ihnen Portabilität nichts ausmacht.
  • Du würdest verwenden strtok_r() wenn Sie mehrere Trennzeichen zwischen Token zulassen möchten und keine leeren Token möchten (und POSIX für Sie ausreichend portabel ist).
  • Sie würden nur verwenden strtok() wenn jemand dein Leben bedroht, wenn du es nicht tust. Und Sie würden es nur lange genug benutzen, um sich aus der lebensbedrohlichen Situation zu befreien; Sie würden dann jeden Gebrauch davon wieder aufgeben. Es ist giftig; benutze es nicht. Es wäre besser, selbst zu schreiben strtok_r() oder strsep() als zu verwenden strtok().

Warum ist strtok() giftig?

Das strtok() Funktion ist giftig, wenn sie in einer Bibliotheksfunktion verwendet wird. Wenn Ihre Bibliotheksfunktion verwendet strtok()muss eindeutig dokumentiert werden.

Das ist, weil:

  1. Wenn eine aufrufende Funktion verwendet wird strtok() und ruft Ihre Funktion auf, die auch verwendet strtok()unterbrechen Sie die aufrufende Funktion.
  2. Wenn Ihre Funktion eine Funktion aufruft, die aufruft strtok()die die Verwendung Ihrer Funktion unterbricht strtok().
  3. Wenn Ihr Programm multithreaded ist, kann höchstens ein Thread verwendet werden strtok() zu einem bestimmten Zeitpunkt – über eine Folge von strtok() Anrufe.

Die Wurzel dieses Problems ist der gespeicherte Zustand zwischen Anrufen, der dies zulässt strtok() um dort weiterzumachen, wo es aufgehört hat. Es gibt keine vernünftige Möglichkeit, das Problem zu beheben, außer “nicht verwenden strtok()“.

  • Sie können verwenden strsep() wenn es verfügbar ist.
  • Sie können POSIX verwenden strtok_r() wenn es verfügbar ist.
  • Sie können die von Microsoft verwenden strtok_s() wenn es verfügbar ist.
  • Nominell könnten Sie die Funktion ISO/IEC 9899:2011 Anhang K.3.7.3.1 verwenden strtok_s()aber seine Schnittstelle unterscheidet sich von beiden strtok_r() und Microsofts strtok_s().

BSD strsep():

char *strsep(char **stringp, const char *delim);

Posix strtok_r():

char *strtok_r(char *restrict s, const char *restrict sep, char **restrict state);

Microsoft strtok_s():

char *strtok_s(char *strToken, const char *strDelimit, char **context);

Anhang K strtok_s():

char *strtok_s(char * restrict s1, rsize_t * restrict s1max,
               const char * restrict s2, char ** restrict ptr);

Beachten Sie, dass dies 4 Argumente hat, nicht 3 wie bei den anderen beiden Varianten strtok().

  • Beachten Sie, dass der Anhang K strtok_s() wird deklariert als: char *strtok_s(char * restrict s1, rsize_t * restrict s1max, const char * restrict s2, char ** restrict ptr); die nicht mit der Schnittstelle von Microsoft übereinstimmt strtok_s() oder POSIX’s strtok_r(). Selbst wenn es implementiert wurde, ist der Unterschied ärgerlich – es schränkt die Nützlichkeit der Annex-K-Funktion ein. Siehe auch Verwenden Sie die „sicheren“ Funktionen von TR 24731?

    – Jonathan Leffler

    30. September 2017 um 20:52 Uhr


  • Ich habe sehr wenig tatsächliche Informationen aus Ihrer hoch bewerteten Erklärung erhalten. Ich sage das nur, weil Sie glauben, dass Ihre Erklärung ziemlich erschöpfend sein könnte und die ganze Angelegenheit ziemlich klar machen könnte, und das tut es wahrscheinlich für hochintelligente Menschen, aber für mich war es fast völlig undurchsichtig. Das beantwortet die Frage für mich überhaupt nicht. Ich musste den Begriff Interleave (Alternate Layers) nachschlagen. Also kein Multithreading, denke ich. “mehrere Trennzeichen zwischen einem einzelnen Token” Mit “Token” meinen Sie eine Teilzeichenfolge? Aber jeder Aufruf erzeugt einen Teilstring, wenn er ein ‘\0’ schreibt. Ich bin total verwirrt.

    – iamoumuamua

    27. Mai 2020 um 4:41 Uhr


  • Es tut mir leid, dass Sie aus meiner Antwort keine Informationen gewonnen haben, @iamoumuamua. Die Angabe von strtok() sagt: Eine Folge von Aufrufen an die strtok Funktion unterbricht die Zeichenfolge, auf die von verwiesen wird s1 in eine Folge von Tokens, von denen jedes durch ein Zeichen aus der Zeichenfolge, auf die gezeigt wird, begrenzt ist s2. Token sind also was strtok() identifiziert. […continued 1…]

    – Jonathan Leffler

    27. Mai 2020 um 4:49 Uhr


  • […continuation 1…] Die Aussage „Anrufe an die strsep() Funktion auf verschiedenen Zeichenfolgen kann verschachtelt werden, während Sie dies mit nicht tun können strtok()” bedeutet, dass Sie verwenden können strsep() zwei verschiedene Stränge parallel zu schneiden und zu würfeln, wobei zuerst ein Token genommen wird string1 dann ein Token aus string2wohingegen strtok() erfordert, dass Sie sich vollständig aufteilen string1 bevor du anpackst string2 oder umgekehrt. Ja, das bedeutet kein Multithreading mit strtok()aber es schränkt auch Singlethread-Programme stark ein. […continued 2…]

    – Jonathan Leffler

    27. Mai 2020 um 4:52 Uhr

  • […continuation 2…] Die von gefundenen Token strtok() und strsep() werden durch Trennzeichen getrennt. Mit strtok()werden mehrere benachbarte Trennzeichen als Teil einer einzelnen Lücke zwischen Token behandelt (also können Sie keine leeren Token mit haben strtok()), wohingegen strsep() geht davon aus, dass jedes Token vom nächsten durch ein einzelnes Trennzeichen getrennt ist und zwei benachbarte Trennzeichen bedeuten, dass sich zwischen ihnen ein leeres Token befindet.

    – Jonathan Leffler

    27. Mai 2020 um 4:54 Uhr


Benutzer-Avatar
George Gaal

Aus dem Handbuch der GNU C Library – Finden von Tokens in einer Zeichenfolge:

Ein Unterschied zwischen strsep und strtok_r ist das, wenn die Eingabezeichenfolge mehr als ein Zeichen von Trennzeichen in einer Reihe enthält strsep gibt eine leere Zeichenfolge für jedes Zeichenpaar vom Trennzeichen zurück. Dies bedeutet, dass ein Programm normalerweise auf testen sollte strsep Rückgabe einer leeren Zeichenfolge vor der Verarbeitung.

  • Kannst du mir bitte ein Beispiel geben, ich bin etwas verwirrt

    – mizuki

    28. August 2011 um 2:18 Uhr

  • Beispiele für die Verwendung dieser Funktionen finden Sie, wenn Sie auf klicken Verknüpfung 🙂 Beachten Sie das bitte auch strsep Die Funktion fehlt möglicherweise in Ihrem C-Compiler.

    – George Gaal

    28. August 2011 um 2:20 Uhr


Benutzer-Avatar
HS

Erster Unterschied in strtok() und strsep() ist die Art und Weise, wie sie zusammenhängende Trennzeichen in der Eingabezeichenfolge behandeln.

Behandlung von zusammenhängenden Trennzeichen durch strtok():

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

int main(void) {
    const char* teststr = "aaa-bbb --ccc-ddd"; //Contiguous delimiters between bbb and ccc sub-string
    const char* delims = " -";  // delimiters - space and hyphen character
    char* token;
    char* ptr = strdup(teststr);

    if (ptr == NULL) {
        fprintf(stderr, "strdup failed");
        exit(EXIT_FAILURE);
    }

    printf ("Original String: %s\n", ptr);

    token = strtok (ptr, delims);
    while (token != NULL) {
        printf("%s\n", token);
        token = strtok (NULL, delims);
    }

    printf ("Original String: %s\n", ptr);
    free (ptr);
    return 0;
}

Ausgabe:

# ./example1_strtok
Original String: aaa-bbb --ccc-ddd
aaa
bbb
ccc
ddd
Original String: aaa

In der Ausgabe sehen Sie das Token "bbb" und "ccc" einer nach demanderen. strtok() gibt nicht das Vorkommen zusammenhängender Trennzeichen an. Auch die strtok() Ändern Sie die Eingabezeichenfolge.

Behandlung von zusammenhängenden Trennzeichen durch strsep():

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

int main(void) {
    const char* teststr = "aaa-bbb --ccc-ddd"; //Contiguous delimiters between bbb and ccc sub-string
    const char* delims = " -";  // delimiters - space and hyphen character
    char* token;
    char* ptr1;
    char* ptr = strdup(teststr);

    if (ptr == NULL) {
        fprintf(stderr, "strdup failed");
        exit(EXIT_FAILURE);
    }

    ptr1 = ptr;

    printf ("Original String: %s\n", ptr);
    while ((token = strsep(&ptr1, delims)) != NULL) {
        if (*token == '\0') {
            token = "<empty>";
        }
        printf("%s\n", token);
    }

    if (ptr1 == NULL) // This is just to show that the strsep() modifies the pointer passed to it
        printf ("ptr1 is NULL\n");
    printf ("Original String: %s\n", ptr);
    free (ptr);
    return 0;
}

Ausgabe:

# ./example1_strsep
Original String: aaa-bbb --ccc-ddd
aaa
bbb
<empty>             <==============
<empty>             <==============
ccc
ddd
ptr1 is NULL
Original String: aaa

In der Ausgabe sehen Sie die beiden leeren Zeichenfolgen (angezeigt durch <empty>) zwischen bbb und ccc. Diese beiden leeren Zeichenfolgen sind für "--" zwischen "bbb" und "ccc". Wann strsep() Trennzeichen gefunden ' ' nach "bbb"es hat das Trennzeichen durch ersetzt '\0' Zeichen und zurückgegeben "bbb". Danach, strsep() anderes Trennzeichen gefunden '-'. Dann wurde das Trennzeichen durch ersetzt '\0' Zeichen und gab den leeren String zurück. Gleiches gilt für das nächste Trennzeichen.

Aufeinanderfolgende Trennzeichen werden angezeigt, wenn strsep() gibt einen Zeiger auf ein Nullzeichen zurück (also ein Zeichen mit dem Wert '\0').

Das strsep() Ändern Sie sowohl die Eingabezeichenfolge als auch den Zeiger dessen Adresse als erstes Argument übergeben wurde strsep().

Zweiter Unterschied ist, strtok() stützt sich auf eine statische Variable, um die aktuelle Parsing-Position innerhalb einer Zeichenfolge zu verfolgen. Diese Implementierung erfordert Analysieren Sie eine Zeichenfolge vollständig, bevor Sie eine zweite Zeichenfolge beginnen. Aber das ist bei nicht der Fall strsep().

Berufung strtok() wenn ein anderer strtok() ist noch nicht fertig:

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

void another_function_callng_strtok(void)
{
    char str[] ="ttt -vvvv";
    char* delims = " -";
    char* token;

    printf ("Original String: %s\n", str);
    token = strtok (str, delims);
    while (token != NULL) {
        printf ("%s\n", token);
        token = strtok (NULL, delims);
    }
    printf ("another_function_callng_strtok: I am done.\n");
}

void function_callng_strtok ()
{
    char str[] ="aaa --bbb-ccc";
    char* delims = " -";
    char* token;

    printf ("Original String: %s\n", str);
    token = strtok (str, delims);
    while (token != NULL)
    {
        printf ("%s\n",token);
        another_function_callng_strtok();
        token = strtok (NULL, delims);
    }
}

int main(void) {
    function_callng_strtok();
    return 0;
}

Ausgabe:

# ./example2_strtok
Original String: aaa --bbb-ccc
aaa
Original String: ttt -vvvv
ttt
vvvv
another_function_callng_strtok: I am done.

Die Funktion function_callng_strtok() nur Token drucken "aaa" und druckt nicht den Rest der Tokens der Eingabezeichenfolge, weil es aufruft another_function_callng_strtok() die wiederum anrufen strtok() und es setzt den statischen Zeiger von strtok() zu NULL wenn es mit dem Extrahieren aller Token fertig ist. Die Steuerung kommt zurück function_callng_strtok() while Schleife, strtok() kehrt zurück NULL aufgrund des statischen Zeigers, der auf zeigt NULL und die die Schleifenbedingung bilden false und Schleifenausgänge.

Berufung strsep() wenn ein anderer strsep() ist noch nicht fertig:

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

void another_function_callng_strsep(void)
{
    char str[] ="ttt -vvvv";
    const char* delims = " -";
    char* token;
    char* ptr = str;

    printf ("Original String: %s\n", str);
    while ((token = strsep(&ptr, delims)) != NULL) {
        if (*token == '\0') {
            token = "<empty>";
        }
        printf("%s\n", token);
    }
    printf ("another_function_callng_strsep: I am done.\n");
}

void function_callng_strsep ()
{
    char str[] ="aaa --bbb-ccc";
    const char* delims = " -";
    char* token;
    char* ptr = str;

    printf ("Original String: %s\n", str);
    while ((token = strsep(&ptr, delims)) != NULL) {
        if (*token == '\0') {
            token = "<empty>";
        }
        printf("%s\n", token);
        another_function_callng_strsep();
    }
}

int main(void) {
    function_callng_strsep();
    return 0;
}

Ausgabe:

# ./example2_strsep
Original String: aaa --bbb-ccc
aaa
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
<empty>
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
<empty>
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
bbb
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
ccc
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.

Hier sehen Sie, rufend strsep() bevor eine Zeichenfolge vollständig analysiert wird, macht dies keinen Unterschied.

Also der Nachteil strtok() und strsep() ist, dass beide die Eingabezeichenfolge aber ändern strsep() hat einige Vorteile gegenüber strtok() wie oben dargestellt.

Aus Strep:

Die Funktion strsep() ist als Ersatz für die Funktion strtok() gedacht. Obwohl die Funktion strtok() aus Portabilitätsgründen bevorzugt werden sollte (sie entspricht ISO/IEC 9899:1990 (“ISO C90”)), ist sie nicht in der Lage, leere Felder zu verarbeiten, dh Felder zu erkennen, die durch zwei benachbarte Trennzeichen getrennt sind, oder für mehr als eine einzelne Saite gleichzeitig verwendet werden. Die Funktion strsep() erschien erstmals in 4.4BSD.


Als Referenz:

1381270cookie-checkWas sind die Unterschiede zwischen strtok und strsep in C

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

Privacy policy