2010-03-23 6 views
8

Ich bekomme "Bus Error" versucht stdin in eine char* Variable zu lesen. Ich möchte nur ganze Sachen über stdin lesen und zuerst in eine Variable schreiben, dann weiter an der Variablen arbeiten.Wie liest man die Standardeingabe in eine Stringvariable bis EOF in C?

My-Code ist wie folgt:

char* content; 
char* c; 
while(scanf("%c", c)) { 
strcat(content, c); 
} 

fprintf(stdout, "Size: %d", strlen(content)); 

Aber irgendwie immer "Busfehler" kehrte ich erhalten, indem cat test.txt | myapp Aufruf, wo myapp den kompilierten Code oben ist.

Meine Frage ist, wie lese ich stdin bis EOF in eine Variable? Wie Sie im Code sehen, möchte ich nur die Größe der Eingabe über Stdin drucken, in diesem Fall sollte es gleich der Größe der Datei sein test.txt.

Ich dachte, nur mit scanf wäre genug, vielleicht gepufferte Art zu lesen stdin?

+0

Warum nicht einfach verwenden stat() die bekommen Dateigröße, dann verwenden Sie die Dateigröße + 1, um (versuchen Sie) malloc den Puffer und dann lesen() in den Puffer? – technosaurus

Antwort

16

Zuerst übergeben Sie nicht initialisierte Zeiger, was bedeutet, dass scanf und strcat Speicher schreiben, die Sie nicht besitzen. Zweitens erwartet strcat zwei nullterminierte Strings, während c nur ein Zeichen ist. Dies wird wiederum dazu führen, dass es Speicher liest, den Sie nicht besitzen. Sie brauchen scanf nicht, weil Sie keine echte Verarbeitung durchführen. Schließlich ist das Lesen eines Buchstabens unnötig langsam. Hier ist der Beginn einer Lösung, einen veränderbaren Puffer für die endgültige Zeichenfolge verwendet wird, und ein fester Puffer für die fgets nennt

#define BUF_SIZE 1024 
char buffer[BUF_SIZE]; 
size_t contentSize = 1; // includes NULL 
/* Preallocate space. We could just allocate one char here, 
but that wouldn't be efficient. */ 
char *content = malloc(sizeof(char) * BUF_SIZE); 
if(content == NULL) 
{ 
    perror("Failed to allocate content"); 
    exit(1); 
} 
content[0] = '\0'; // make null-terminated 
while(fgets(buffer, BUF_SIZE, stdin)) 
{ 
    char *old = content; 
    contentSize += strlen(buffer); 
    content = realloc(content, contentSize); 
    if(content == NULL) 
    { 
     perror("Failed to reallocate content"); 
     free(old); 
     exit(2); 
    } 
    strcat(content, buffer); 
} 

if(ferror(stdin)) 
{ 
    free(content); 
    perror("Error reading from stdin."); 
    exit(3); 
} 

EDIT: Wie Wolfer zu anspielte, ein NULL in Ihrem Eingang bewirkt, dass die Zeichenfolge beendet werden vorzeitig bei der Verwendung von Fgets. getline ist eine bessere Wahl, wenn verfügbar, da es Speicherzuweisung verarbeitet und keine Probleme mit NUL-Eingabe hat.

+0

Verursacht 'ungültige Konvertierung von 'void *' nach 'char *' [- fpermissive]' auf 'char * content = malloc (sizeof (char) * BUF_SIZE);' und 'content = realloc (content, contentSize);' mit gcc 4.8.1 ... – Wolfer

+0

Auch es schlägt fehl, wenn mit '0x00' in stdin dargestellt. – Wolfer

+1

@Wolfer, gcc * C * Compiler [hat keine] (http://linux.die.net/man/1/gcc) -fpermissive.Vielleicht kompilieren Sie es als C++; Das würde den Fehler erklären? Die Tags sagen C. Du hast Recht, dass fgets mit NUL-Charakteren nicht gut spielt. Es verursacht kein undefiniertes Verhalten, aber Sie werden es nicht wissen und die Zeichenfolge wird vorzeitig beendet. Ich werde eine Notiz über getline zu der Frage hinzufügen. –

7

Ihr Problem ist, dass Sie nie c und content zugeteilt haben, also zeigen sie nicht irgendwo definiert - sie zeigen wahrscheinlich auf nicht zugeordneten Speicher oder etwas, das gar nicht existiert. Und dann fügst du Daten in sie ein. Sie müssen sie zuerst zuweisen. (Das ist, was ein Busfehler der Regel bedeutet;. Sie haben versucht, einen Speicherzugriff zu tun, die nicht gültig ist)

(Alternativ, da c ist immer nur ein einzelnes Zeichen zu halten, können Sie es als char c erklären und übergeben &c zu scanf.Nicht nötig, eine Zeichenkette zu deklarieren, wenn man es tut.)

Sobald Sie das tun, werden Sie das Problem von sicherstellen, dass content lang genug ist, um alle Eingabe zu halten. Entweder müssen Sie schätzen, wie viel Input Sie erwarten, und es mindestens so lange zuweisen (und dann einen Fehler, wenn Sie das überschreiten), oder Sie benötigen eine Strategie, um es in einer größeren Größe neu zuzuordnen, wenn es nicht lang genug ist.

Oh, und Sie werden auch auf das Problem stoßen, dass strcat erwartet eine Zeichenfolge, kein einzelnes Zeichen. Auch wenn Sie c als char* verlassen, macht der Aufruf scanf keinen String. Eine Einzelzeichenfolge ist (im Speicher) ein Zeichen gefolgt von einem Nullzeichen, um das Ende der Zeichenfolge anzuzeigen. scanf, wird beim Scannen nach einem einzelnen Zeichen nicht das Nullzeichen nach dem Zeichen eingefügt. Als Ergebnis wird strcpy nicht wissen, wo das Ende der Zeichenfolge ist, und wird durch den Speicher wandern, auf der Suche nach dem Nullzeichen.

+5

Sie sind nicht NULL, sie haben undefinierte Werte. –

+0

+1 @Matthew, es sei denn, sie sind Globals. –

+1

@Brooks, nur machen 'c' ein' char' und '' '' ist nicht genug, da 'strcat()' eine Null-terminierte Zeichenkette benötigt. –

6

Da Sie sich nicht um den eigentlichen Inhalt kümmern, warum sollten Sie eine Zeichenfolge erstellen? Ich würde auch getchar() verwenden:

int c; 
size_t s = 0; 

while ((c = getchar()) != EOF) 
{ 
    s++; 
} 

printf("Size: %z\n", s); 

Dieser Code wird korrekt Fälle behandeln, in denen die Dateien '\0' Zeichen in ihm hat.

+0

Das OP sagte: "Ich möchte nur ganze Sachen lesen, die über Stdin kommen und es zuerst in eine Variable schreiben, dann weiter an der Variablen arbeiten." Ich denke, der Aufruf von länge() ist nur ein Beispiel. Aber Ihr Kommentar über die Datei mit '\ 0' Zeichen ist eine wichtige - ihre ganze Idee, es in eine einzige Variable zu setzen, kann ein wenig fehlerhaft sein. –

+3

'c' sollte als' int' deklariert werden, um den Bereich von 'char' plus' EOF' einzuschließen. –

+0

@ Jon Yup, guten Ruf. Tippen ohne nachzudenken. –

1

Das Problem hierbei ist, dass Sie eine Zeigervariable verweisen, dass kein Speicher über malloc zugewiesen, daher würden die Ergebnisse nicht definiert, und nicht allein, dass durch strcat auf einem undefinierten Zeiger verwenden, die auf etwas zeigen konnte, Sie beendet up mit einem Busfehler!

Dies wäre der festgelegte Code erforderlich ....

 
char* content = malloc (100 * sizeof(char)); 
char c; 
if (content != NULL){ 
    content[0] = '\0'; // Thanks David! 
    while ((c = getchar()) != EOF) 
    { 
     if (strlen(content) < 100){ 
      strcat(content, c); 
      content[strlen(content)-1] = '\0'; 
     } 
    } 
} 
/* When done with the variable */ 
free(content); 

Der Code unterstreicht die Verantwortung des Programmierers, den Speicher zu verwalten - für jeden malloc a gibt es free wenn nicht, Sie zu einem Speicherverlust haben!

Edit: Dank David Gelhar für seinen Punkt-out an meinem Glitch! Ich habe den Code oben repariert, um die Korrekturen wiederzugeben ... natürlich könnte in einer realen Situation vielleicht der feste Wert von 100 zu vielleicht einem #define geändert werden, um es einfach zu machen, den Puffer zu erweitern, indem man den Betrag von verdoppelt Speicher über realloc und trimmen Sie es auf Größe ...

+1

Dieser Code hat einen Fehler: das erste Mal durch die Schleife rufen Sie "strlen" auf nicht initialisierten Daten. (und natürlich sollten Sie nach Pufferüberläufen suchen, wenn mehr als 100 Zeichen der Eingabe vorhanden sind) –

+0

Wie Jon Purdy in einem der anderen Beiträge notierte, müssen Sie c als int deklarieren, nicht char, so dass das mögliche EOF resultiert wird in Reichweite sein. Andernfalls gehen Sie in eine Endlosschleife. Außerdem erwartet Ihr strcat-Aufruf zwei nullterminierte Strings, keine Zeichenfolge und kein einzelnes Zeichen. Wie geschrieben, wird es nicht kompilieren. –

1

Unter der Annahme, dass Sie (kürzer als MAXL-1 Zeichen) Strings erhalten möchten und nicht die Dateien char von char zu verarbeiten, ich habe wie folgt:

#include <stdio.h> 
#include <string.h> 
#define MAXL 256 

main(){ 
    char s[MAXL]; 
    s[0]=0; 
    scanf("%s",s); 
    while(strlen(s)>0){ 
    printf("Size of %s : %d\n",s,strlen(s)); 
    s[0]=0; 
    scanf("%s",s); 
    }; 
} 
Verwandte Themen