2016-07-25 22 views
0

Ich habe versucht, einen vollständigen Line-Eingang in C zu nehmen Anfänge tat ich,Wie nehme ich eine Zeileneingabe in C?

char line[100] // assume no line is longer than 100 letters. 
scanf("%s", line); 

Ignorieren Sicherheitslücken und Buffer Overflows, wusste ich, das nie mehr als ein Worteingabe übernehmen könnte. Ich modifizierte es wieder,

scanf("[^\n]", line); 

Dies konnte natürlich nicht mehr als eine Zeile der Eingabe nehmen. Der folgende Code ausgeführt wurde jedoch in Endlosschleife,

while(fscanf(stdin, "%[^\n]", line) != EOF) 
{ 
    printf("%s\n", line); 
} 

Das lag daran wurde die \n nie verbraucht, und an der gleichen Stelle wiederholt stoppen würde und hatte den gleichen Wert in line. So schrieb ich den Code wie

while(fscanf(stdin, "%[^\n]\n", line) != EOF) 
{ 
    printf("%s\n", line); 
} 

Dieser Code arbeitete tadellos (oder so dachte ich), für die Eingabe aus einer Datei. Aber für die Eingabe von stdin erzeugte dies kryptisches, seltsames, unartikuliertes Verhalten. Erst nachdem die zweite Zeile eingegeben wurde, würde die erste Zeile gedruckt werden. Ich kann nicht verstehen, was wirklich passiert.

Alles, was ich tue, ist dies. Notieren Sie sich die Zeichenfolge, bis Sie auf eine \n stoßen, speichern Sie sie in line, und verwenden Sie dann die \n aus dem Eingabepuffer. Drucken Sie jetzt diese line und machen Sie sich bereit für die nächste Zeile von der Eingabe. Oder werde ich in die Irre geführt?

Zu der Zeit jedoch, diese Frage der Entsendung, fand ich eine bessere Alternative,

while(fscanf(stdin, "%[^\n]%*c", line) != EOF) 
{ 
    printf("%s\n", line); 
} 

Dieses einwandfrei für alle Fälle funktioniert. Aber meine Frage bleibt noch. Wie dieser Code kommen,

while(fscanf(stdin, "%[^\n]\n", line) != EOF) 
{ 
    printf("%s\n", line); 
} 

arbeiteten für Eingaben aus einer Datei, sondern verursachen Probleme für die Eingabe von der Standardeingabe?

+10

Verwenden Sie fgets(), nicht fscanf(). – FredK

+0

könnte ich sehr sicher tun. Aber das ist wie vor dem Problem weglaufen. Dies ist zu verstehen, das Verhalten von 'scanf' –

+1

Ich werde für' fgets' auch gehen - nicht vergessen, es behält jede nachfolgende 'newline'. Wie für die 'scanf'-Familie, überprüfe immer die korrekte * Anzahl der konvertierten Elemente *. Umgewandelte '0'-Elemente werden nicht durch' EOF'-Test erkannt, aber 'EOF' wird angezeigt, wenn die korrekte Anzahl von Elementen nicht konvertiert wurde. –

Antwort

3

Verwenden Sie fgets(). @FredK

char buf[N]; 
while (fgets(buf, sizeof buf, stdin)) { 
    // crop potential \n if desired. 
    buf[strcspn(buf, "\n")] = '\0'; 
    ... 
} 

Es gibt zu viele Fragen versuchen scanf() für Benutzereingaben zu verwenden, die es anfällig für falschen Gebrauch oder Code-Attacken machen.

// Leaves trailing \n in stdin 
scanf("%[^\n]", line) 

// Does nothing if line begins with \n. \n remains in stdin 
// As return value not checked, use of line may be UB. 
// If some text read, consumes \n and then all following whitespace: ' ' \n \t etc. 
// Then does not return until a non-white-space is entered. 
// As stdin is usually buffered, this implies 2 lines of user input. 
// Fails to limit input. 
scanf("%[^\n]\n", line) 

// Does nothing if line begins with \n. \n remains in stdin 
// Consumes 1 char after `line`, even if next character is not a \n 
scanf("%99[^\n]%*c", line) 

gilt das EOF ist üblich, die falsch überprüfen. @Weather Vane Wenn zuerst eingegeben wird, wird 0 zurückgegeben, da line nicht aufgefüllt ist. Als 0 != EOF geht Code weiter, um einen nicht initialisierten line zu verwenden, der zu UB führt.

while(fscanf(stdin, "%[^\n]%*c", line) != EOF) 

Sie können "1234 \ n" wie folgt eingeben. Wahrscheinlich Endlosschleife als erste fscanf() lesen "123", wirft die "4" und der nächste fscanf() Anruf wird auf \ n festgefahren.

while(fscanf(stdin, "%3[^\n]%*c", line) != EOF) 

Wenn die Ergebnisse der *scanf() Überprüfung Check gegen das, was Sie wollen, nicht gegen einen der Werte, die Sie nicht wollen. (Aber auch folgendes hat andere Probleme)

while(fscanf(stdin, "%[^\n]%*c", line) == 1) 

Über die nächsten scanf() lesen Linie:

char buf[100]; 
buf[0] = 0; 
int cnt = scanf("%99[^\n]", buf); 
if (cnt == EOF) Handle_EndOfFile(); 
// Consume \n if next stdin char is a \n 
scanf("%*1[\n]"); 
// Use buf; 

while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
für Eingaben aus einer Datei gearbeitet , verursacht aber Probleme bei der Eingabe von Standard i nput?

Posting Beispielcode und Eingabe/Datendatei wäre nützlich. Mit mäßiger Menge an Code veröffentlicht, einige mögliche Gründe.

line Überschreitung ist UB
Eingang mit \n beginnt zu UB führenden
Datei oder stdin beide nicht in demselben Modus geöffnet. \r nicht in einem übersetzt.


Hinweis: Folgendes schlägt fehl, wenn eine Zeile 100 Zeichen enthält. So führt die Annahme der cal noch immer zu UB.

char line[100] // assume no line is longer than 100 letters. 
scanf("%s", line); 
0

Persönlich denke ich fgets() ist schlecht entworfen. Wenn ich eine Zeile lese, möchte ich sie insgesamt lesen, unabhängig von ihrer Länge (außer dem Auffüllen des gesamten RAM). fgets() kann das nicht auf einmal machen. Wenn eine lange Zeile vorhanden ist, müssen Sie sie mehrmals manuell ausführen, bis sie den Zeilenumbruch erreicht. Die Glibc-spezifische getline() ist in dieser Hinsicht bequemer. Hier ist eine Funktion, die GNU getline nachahmt():

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

long my_getline(char **buf, long *m_buf, FILE *fp) 
{ 
    long tot = 0, max = 0; 
    char *p; 
    if (*m_buf == 0) { // empty buffer; allocate 
     *m_buf = 16; // initial size; could be larger 
     *buf = (char*)malloc(*m_buf); // FIXME: check NULL 
    } 
    for (p = *buf, max = *m_buf;;) { 
     long l, old_m; 
     if (fgets(p, max, fp) == NULL) 
      return tot? tot : EOF; // reach end-of-file 
     for (l = 0; l < max; ++l) 
      if (p[l] == '\n') break; 
     if (l < max) { // a complete line 
      tot += l, p[l] = 0; 
      break; 
     } 
     old_m = *m_buf; 
     *m_buf <<= 1; // incomplete line; double the buffer 
     *buf = (char*)realloc(*buf, *m_buf); // check NULL 
     max = (*m_buf) - old_m; 
     p = (*buf) + old_m - 1; // point to the end of partial line 
    } 
    return tot; 
} 

int main(int argc, char *argv[]) 
{ 
    long l, m_buf = 0; 
    char *buf = 0; 
    while ((l = my_getline(&buf, &m_buf, stdin)) != EOF) 
     puts(buf); 
    free(buf); 
    return 0; 
} 

ich in der Regel meine eigene Readline-() Funktion verwenden. Ich schrieb dieses my_getline() vor einem Moment. Es wurde nicht gründlich getestet. Bitte verwenden Sie mit Vorsicht.

+0

Beachten Sie, dass * nix' ssize_t getline (char ** lineptr, size_t * n , FILE * stream) 'verwendet' size_t * ', nicht' long * '. Gibt es einen besonderen Grund für die Änderung dieses Codes? BTW: Code hat undicht: Sollte 'free (* buf)' vor '* buf = malloc (* m_buf);' 'l = strlen (p); if (p [l-1] ... 'ist ein Hacker-Exploit, da das erste von' fgets() 'zurückgegebene Zeichen ein Nullzeichen sein kann. – chux

+0

Es gibt kein Leck. Wenn' buf 'vorbelegt ist, sollte '* m_buf' Sei positiv, dann kommst du nie in "malloc". Was "size_t" usw. betrifft, kannst du das ändern. Das obige zeigt nur eine Skizze. Für eine korrekte Bibliotheksfunktion müssen wir mindestens '* alloc()' überprüfen. Der erste char 'fgets()' returns sollte kein NULL sein (oh, warte, außer '* m_buf' ist gleich 1). – user172818

+0

' * buf' darf nicht-'NULL' sein und' * m_buf' darf 0 sein. ' lang m_buf = 0; char * buf = malloc (m_buf); my_getline (& buf, & m_buf, stdin); 'IAC, es ist ein Eckfall. nur ein FYI – chux

Verwandte Themen