Entfernen des nachgestellten Newline-Zeichens aus der fgets()-Eingabe

Lesezeit: 10 Minuten

Benutzeravatar von sfactor
Faktor

Ich versuche, einige Daten vom Benutzer zu erhalten und sie an eine andere Funktion in gcc zu senden. Der Code ist ungefähr so.

printf("Enter your Name: ");
if (!(fgets(Name, sizeof Name, stdin) != NULL)) {
    fprintf(stderr, "Error reading Name.\n");
    exit(1);
}

Ich finde jedoch, dass es einen Zeilenumbruch hat \n Charakter am Ende. Also wenn ich eintrete John es endet mit dem Senden John\n. Wie entferne ich das \n und sende einen richtigen String.

  • if (!fgets(Name, sizeof Name, stdin)) (Verwenden Sie zumindest nicht zwei Negationen, ! und !=)

    Roger Pate

    22. April 2010 um 20:05 Uhr

  • @ Roger Pate “verwende keine zwei Negationen” –> hmmm, wenn wir tief graben, sind “nicht” und “Verneinung” beides Negationen. ;-). Vielleicht „Verwenden if (fgets(Name, sizeof Name, stdin)) {.

    – chux – Wiedereinsetzung von Monica

    24. August 2016 um 14:53 Uhr


  • @chux, ich bin sicher, Sie meinten if (fgets(Name, sizeof Name, stdin) == NULL ) {

    – R Sahu

    5. April 2018 um 18:50 Uhr

  • @RSahu True: lästig !:

    – chux – Wiedereinsetzung von Monica

    5. April 2018 um 19:30 Uhr

Die vielleicht einfachste Lösung verwendet eine meiner wenig bekannten Lieblingsfunktionen, strcspn():

buffer[strcspn(buffer, "\n")] = 0;

Wenn Sie es auch handhaben möchten '\r' (sagen wir, wenn der Stream binär ist):

buffer[strcspn(buffer, "\r\n")] = 0; // works for LF, CR, CRLF, LFCR, ...

Die Funktion zählt die Anzahl der Zeichen, bis sie auf a trifft '\r' oder ein '\n' (mit anderen Worten, es findet die erste '\r' oder '\n'). Wenn es nichts trifft, stoppt es am '\0' (Rückgabe der Länge der Zeichenfolge).

Beachten Sie, dass dies auch ohne Zeilenumbruch funktioniert, weil strcspn stoppt bei a '\0'. In diesem Fall wird einfach die gesamte Leitung ausgetauscht '\0' mit '\0'.

  • Dies bewältigt sogar das Seltene buffer als beginnt mit '\0'etwas, das Kummer für die verursacht buffer[strlen(buffer) - 1] = '\0'; sich nähern.

    – chux – Wiedereinsetzung von Monica

    11. Februar 2015 um 19:39 Uhr


  • @chux: Ja, ich wünschte, mehr Leute hätten davon gewusst strcspn(). Eine der nützlicheren Funktionen in der Bibliothek, IMO. Ich habe beschlossen, heute ein paar gängige C-Hacks wie diesen zu schreiben und zu veröffentlichen; a strtok_r Umsetzung mit strcspn und strspn war einer der ersten: codepad.org/2lBkZk0w (Warnung: Ich kann nicht garantieren, dass es fehlerfrei ist; es wurde hastig geschrieben und hat wahrscheinlich ein paar). Ich weiß zwar noch nicht, wo ich sie veröffentlichen werde, aber ich beabsichtige, sie im Geiste der berühmten “Bit Twiddling Hacks” zu machen.

    – Tim Čas

    11. Februar 2015 um 19:44 Uhr


  • Wege gesucht robust trimmen fgets(). Dies strcspn() scheint das zu sein nur richtiger Einzeiler. strlen ist schneller – wenn auch nicht so einfach.

    – chux – Wiedereinsetzung von Monica

    11. Februar 2015 um 20:04 Uhr


  • @sidbushes: Die Frage, sowohl im Titel als auch im Inhalt, fragt nach dem abschließenden Zeilenumbruch aus fgets() Eingang. Das ist immer auch der erste Zeilenumbruch.

    – Tim Čas

    9. April 2017 um 13:44 Uhr

  • @sidbushes: Ich verstehe, woher Sie kommen, aber ich kann nicht für Google-Suchergebnisse für bestimmte Begriffe verantwortlich gemacht werden. Sprechen Sie mit Google, nicht mit mir.

    – Tim Čas

    8. Mai 2017 um 12:08 Uhr

Benutzeravatar von Jerry Coffin
Jerry Sarg

Der elegante Weg:

Name[strcspn(Name, "\n")] = 0;

Der etwas hässliche Weg:

char *pos;
if ((pos=strchr(Name, '\n')) != NULL)
    *pos="\0";
else
    /* input too long for buffer, flag error */

Der etwas seltsame Weg:

strtok(Name, "\n");

Notiere dass der strtok Die Funktion funktioniert nicht wie erwartet, wenn der Benutzer eine leere Zeichenfolge eingibt (dh nur die Eingabetaste drückt). Es verlässt die \n Charakter intakt.

Es gibt natürlich auch andere.

  • Jede C-Laufzeitbibliothek, die Thread-fähig ist (d. h. die meisten, die auf eine Multithread-Plattform abzielen), strtok() wird Thread-sicher sein (es wird lokalen Thread-Speicher für den ‘Inter-Call’-Zustand verwenden). Trotzdem ist es im Allgemeinen immer noch besser, den nicht standardmäßigen (aber häufig genug) zu verwenden. strtok_r() Variante.

    – Michael Burr

    22. April 2010 um 21:36 Uhr

  • Siehe meine Antwort für eine vollständig Thread-sichere und wiedereintrittsfähige Variante, ähnlich Ihrer strtok Ansatz (und es funktioniert mit leeren Eingängen). In der Tat ein guter Weg zur Umsetzung strtok ist zu verwenden strcspn und strspn.

    – Tim Čas

    11. Februar 2015 um 19:09 Uhr


  • Es ist wichtig, den Sonst-Fall zu handhaben, wenn Sie sich in einer Umgebung befinden, in der das Risiko überlanger Leitungen besteht. Das unbemerkte Abschneiden von Eingaben kann sehr schädliche Fehler verursachen.

    – Malcolm McLean

    8. Januar 2017 um 18:52 Uhr

  • Wenn Sie Einzeiler mögen und glibc verwenden, versuchen Sie es *strchrnul(Name, '\n') = '\0';.

    – Zweibit

    17. August 2017 um 8:55 Uhr

  • Wann strchr(Name, '\n') == NULLdann gibt es neben “Eingabe zu lang für Puffer, Flagfehler” noch andere Möglichkeiten: Letzter Text in stdin endete nicht mit a '\n' oder ein seltenes eingebettetes Nullzeichen wurde gelesen.

    – chux – Wiedereinsetzung von Monica

    6. Dezember 2017 um 22:36 Uhr

Benutzeravatar von James Morris
James Morris

size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n') 
    name[ln] = '\0';

  • Wird wahrscheinlich eine Ausnahme auslösen, wenn die Zeichenfolge leer ist, oder? Wie Index außerhalb des Bereichs.

    – Edward Olamisan

    31. Mai 2013 um 3:26 Uhr


  • @EdwardOlamisan, die Zeichenfolge wird jedoch niemals leer sein.

    – James Morris

    14. Juli 2013 um 23:27 Uhr

  • @James Morris In ungewöhnlichen Fällen fgets(buf, size, ....) –> strlen(buf) == 0. 1) fgets() liest sich als erstes char a '\0'. 2) size == 1 3) fgets() kehrt zurück NULL dann buf Inhalte können alles sein. (Der Code von OP testet jedoch auf NULL) Schlagen Sie vor: size_t ln = strlen(name); if (ln > 0 && name[ln-1] == '\n') name[--ln] = '\0';

    – chux – Wiedereinsetzung von Monica

    2. Juli 2014 um 14:00 Uhr


  • Was ist, wenn der String leer ist? ln wäre -1, abgesehen von der Tatsache size_t ist unsigniert, schreibt also in den Zufallsspeicher. Ich denke, Sie verwenden möchten ssize_t und prüfe ln ist >0.

    – Licht

    16. März 2015 um 22:38 Uhr

  • @legends2k: Eine Suche nach einem Kompilierzeitwert (insbesondere einem Nullwert wie in strlen) kann viel effizienter implementiert werden als eine einfache zeichenweise Suche. Aus diesem Grund würde ich diese Lösung für besser halten als a strchr oder strcspn basierte.

    – AnT steht zu Russland

    29. März 2016 um 18:28 Uhr


chux – Stellt Monicas Benutzeravatar wieder her
Chux – Wiedereinsetzung von Monica

Unten ist ein schneller Ansatz, um ein Potenzial zu entfernen '\n' aus einer Zeichenfolge, die von gespeichert wurde fgets().
Es verwendet strlen()mit 2 Tests.

char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) != NULL) {

  size_t len = strlen(buffer);
  if (len > 0 && buffer[len-1] == '\n') {
    buffer[--len] = '\0';
  }

Jetzt benutzen buffer und len wie benötigt.

Diese Methode hat den Nebennutzen a len Wert für nachfolgenden Code. Es kann leicht schneller sein als strchr(Name, '\n'). Ref YMMV, aber beide Methoden funktionieren.


buffervom Original fgets() enthält nicht in "\n" unter Umständen:
A) Die Schlange war zu lang für buffer also nur char vor dem '\n' eingespart wird buffer. Die ungelesenen Zeichen verbleiben im Stream.
B) Die letzte Zeile in der Datei endete nicht mit a '\n'.

Wenn die Eingabe Nullzeichen eingebettet hat '\0' irgendwo drin steht die länge von gemeldet strlen() wird die nicht enthalten '\n' Lage.


Einige andere Antworten Probleme:

  1. strtok(buffer, "\n"); kann die nicht entfernen '\n' Wenn buffer ist "\n". Aus dieser Antwort – nach dieser Antwort geändert, um vor dieser Einschränkung zu warnen.

  2. Das Folgende schlägt in seltenen Fällen beim ersten fehl char gelesen von fgets() ist '\0'. Dies geschieht, wenn die Eingabe mit einem eingebetteten beginnt '\0'. Dann buffer[len -1] wird buffer[SIZE_MAX] Zugriff auf Speicher sicherlich außerhalb des legitimen Bereichs von buffer. Etwas, das ein Hacker ausprobieren oder finden könnte, wenn er UTF16-Textdateien dummerweise liest. Dies war der Stand einer Antwort, als diese Antwort geschrieben wurde. Später hat ein Nicht-OP es bearbeitet, um Code wie die Überprüfung dieser Antwort einzuschließen "".

    size_t len = strlen(buffer);
    if (buffer[len - 1] == '\n') {  // FAILS when len == 0
      buffer[len -1] = '\0';
    }
    
  3. sprintf(buffer,"%s",buffer); ist undefiniertes Verhalten: Ref. Außerdem werden keine führenden, trennenden oder nachgestellten Leerzeichen gespeichert. Jetzt gelöscht.

  4. [Edit due to good later answer] Mit dem 1 Liner gibt es keine Probleme buffer[strcspn(buffer, "\n")] = 0; andere als die Leistung im Vergleich zu der strlen() sich nähern. Die Leistung beim Trimmen ist normalerweise kein Problem, da der Code E / A ausführt – ein schwarzes Loch der CPU-Zeit. Sollte der folgende Code die Länge der Zeichenfolge benötigen oder sehr leistungsbewusst sein, verwenden Sie dies strlen() sich nähern. Sonst die strcspn() ist eine gute Alternative.

Direkt zum Entfernen des ‘\n’ aus der fgets-Ausgabe, wenn jede Zeile ‘\n’ enthält

line[strlen(line) - 1] = '\0';

Andernfalls:

void remove_newline_ch(char *line)
{
    int new_line = strlen(line) -1;
    if (line[new_line] == '\n')
        line[new_line] = '\0';
}

  • Beachten Sie, dass die Verwendung sicherer wäre strnlen Anstatt von strlen.

    – Mike Mertsock

    30. Juni 2013 um 1:37 Uhr

  • Ein Kommentar zur ersten Antwort in der verknüpften Frage lautet: “Beachten Sie, dass strlen(), strcmp() und strdup() sicher sind. Die ‘n’-Alternativen bieten Ihnen zusätzliche Funktionalität.”

    – Etienne

    19. November 2013 um 23:12 Uhr

  • @esker nein, würde es nicht. Einfügen eines n erhöht die Sicherheit nicht auf magische Weise, in diesem Fall würde es den Code sogar gefährlicher machen. Ähnlich mit strncpy, eine furchtbar unsichere Funktion. Der Beitrag, auf den Sie verlinkt haben, ist ein schlechter Rat.

    – MM

    28. Oktober 2015 um 20:55 Uhr

  • Dies scheitert kläglich für eine leere Zeichenfolge (""). Ebenfalls strlen() kehrt zurück size_t nicht int.

    – alk

    17. Juni 2017 um 9:55 Uhr

  • Dies ist für eine leere Zeichenfolge unsicher, es wird bei Index -1 geschrieben. Verwenden Sie dies nicht.

    – Jean-Francois Fabre

    14. Juni 2018 um 19:38 Uhr


Für einzelnes ‘\n’-Trimmen,

void remove_new_line(char* string)
{
    size_t length = strlen(string);
    if((length > 0) && (string[length-1] == '\n'))
    {
        string[length-1] ='\0';
    }
}

für mehrfaches ‘\n’-Trimmen,

void remove_multi_new_line(char* string)
{
  size_t length = strlen(string);
  while((length>0) && (string[length-1] == '\n'))
  {
      --length;
      string[length] ='\0';
  }
}

  • Beachten Sie, dass die Verwendung sicherer wäre strnlen Anstatt von strlen.

    – Mike Mertsock

    30. Juni 2013 um 1:37 Uhr

  • Ein Kommentar zur ersten Antwort in der verknüpften Frage lautet: “Beachten Sie, dass strlen(), strcmp() und strdup() sicher sind. Die ‘n’-Alternativen bieten Ihnen zusätzliche Funktionalität.”

    – Etienne

    19. November 2013 um 23:12 Uhr

  • @esker nein, würde es nicht. Einfügen eines n erhöht die Sicherheit nicht auf magische Weise, in diesem Fall würde es den Code sogar gefährlicher machen. Ähnlich mit strncpy, eine furchtbar unsichere Funktion. Der Beitrag, auf den Sie verlinkt haben, ist ein schlechter Rat.

    – MM

    28. Oktober 2015 um 20:55 Uhr

  • Dies scheitert kläglich für eine leere Zeichenfolge (""). Ebenfalls strlen() kehrt zurück size_t nicht int.

    – alk

    17. Juni 2017 um 9:55 Uhr

  • Dies ist für eine leere Zeichenfolge unsicher, es wird bei Index -1 geschrieben. Verwenden Sie dies nicht.

    – Jean-Francois Fabre

    14. Juni 2018 um 19:38 Uhr


Mein Newbie-Weg 😉 Bitte lassen Sie mich wissen, ob das richtig ist. Es scheint für alle meine Fälle zu funktionieren:

#define IPT_SIZE 5

int findNULL(char* arr)
{
    for (int i = 0; i < strlen(arr); i++)
    {
        if (*(arr+i) == '\n')
        {
            return i;
        }
    }
    return 0;
}

int main()
{
    char *input = malloc(IPT_SIZE + 1 * sizeof(char)), buff;
    int counter = 0;

    //prompt user for the input:
    printf("input string no longer than %i characters: ", IPT_SIZE);
    do
    {
        fgets(input, 1000, stdin);
        *(input + findNULL(input)) = '\0';
        if (strlen(input) > IPT_SIZE)
        {
            printf("error! the given string is too large. try again...\n");
            counter++;
        }
        //if the counter exceeds 3, exit the program (custom function):
        errorMsgExit(counter, 3); 
    }
    while (strlen(input) > IPT_SIZE);

//rest of the program follows

free(input)
return 0;
}

1427310cookie-checkEntfernen des nachgestellten Newline-Zeichens aus der fgets()-Eingabe

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

Privacy policy