Wie soll ich in C eine Textdatei lesen und alle Zeichenfolgen drucken?

89

Ich habe eine Textdatei mit dem Namen test.txt

Ich möchte ein C-Programm schreiben, das diese Datei lesen und den Inhalt auf der Konsole drucken kann (vorausgesetzt, die Datei enthält nur ASCII-Text).

Ich weiß nicht, wie ich die Größe meiner Zeichenfolgenvariablen ermitteln soll. So was:

char str[999];
FILE * file;
file = fopen( "test.txt" , "r");
if (file) {
    while (fscanf(file, "%s", str)!=EOF)
        printf("%s",str);
    fclose(file);
}

Die Größe 999funktioniert nicht, da die von zurückgegebene Zeichenfolge fscanfgrößer sein kann. Wie kann ich das lösen?

Richard
quelle

Antworten:

129

Am einfachsten ist es, ein Zeichen zu lesen und direkt nach dem Lesen auszudrucken:

int c;
FILE *file;
file = fopen("test.txt", "r");
if (file) {
    while ((c = getc(file)) != EOF)
        putchar(c);
    fclose(file);
}

cist intoben, da EOFes sich um eine negative Zahl handelt und eine Ebene sein charkann unsigned.

Wenn Sie die Datei in Blöcken lesen möchten, jedoch ohne dynamische Speicherzuweisung, haben Sie folgende Möglichkeiten:

#define CHUNK 1024 /* read 1024 bytes at a time */
char buf[CHUNK];
FILE *file;
size_t nread;

file = fopen("test.txt", "r");
if (file) {
    while ((nread = fread(buf, 1, sizeof buf, file)) > 0)
        fwrite(buf, 1, nread, stdout);
    if (ferror(file)) {
        /* deal with error */
    }
    fclose(file);
}

Die zweite Methode oben ist im Wesentlichen, wie Sie eine Datei mit einem dynamisch zugewiesenen Array lesen:

char *buf = malloc(chunk);

if (buf == NULL) {
    /* deal with malloc() failure */
}

/* otherwise do this.  Note 'chunk' instead of 'sizeof buf' */
while ((nread = fread(buf, 1, chunk, file)) > 0) {
    /* as above */
}

Ihre Methode fscanf()mit %sals Format verliert Informationen über Leerzeichen in der Datei, sodass eine Datei nicht genau in diese Datei kopiert wird stdout.

Alok Singhal
quelle
Es ist möglich, Daten aus einer Datei zu lesen, ohne diese Datei in c / c ++ zu öffnen?
Sagar Patel
Was ist, wenn die Textdatei durch Kommas getrennte Ganzzahlwerte enthält? als was wäre der Code können Sie Ihre Antwort damit auch darin bearbeiten.
Mohsin
Das Obige funktioniert für jede Art von Textdatei. Wenn Sie die Zahlen aus einer CSV-Datei analysieren möchten, ist dies ein anderes Problem.
Alok Singhal
1
@overexchange Bei der Frage geht es nicht um Zeilen, sondern darum, eine Datei zu lesen und ihren Inhalt zu kopieren stdout.
Alok Singhal
1
@shjeff Eine Datei darf kein EOF-Zeichen enthalten. Beachten Sie, dass dies cint ist und C garantiert, dass EOFes keinem gültigen Zeichen entspricht.
Alok Singhal
58

Es gibt hier viele gute Antworten zum Lesen in Stücken. Ich zeige Ihnen nur einen kleinen Trick, der den gesamten Inhalt auf einmal in einen Puffer liest und druckt.

Ich sage nicht, dass es besser ist. Es ist nicht so und wie Ricardo manchmal kann es schlecht sein, aber ich finde es eine gute Lösung für die einfachen Fälle.

Ich habe es mit Kommentaren bestreut, weil viel los ist.

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

char* ReadFile(char *filename)
{
   char *buffer = NULL;
   int string_size, read_size;
   FILE *handler = fopen(filename, "r");

   if (handler)
   {
       // Seek the last byte of the file
       fseek(handler, 0, SEEK_END);
       // Offset from the first to the last byte, or in other words, filesize
       string_size = ftell(handler);
       // go back to the start of the file
       rewind(handler);

       // Allocate a string that can hold it all
       buffer = (char*) malloc(sizeof(char) * (string_size + 1) );

       // Read it all in one operation
       read_size = fread(buffer, sizeof(char), string_size, handler);

       // fread doesn't set it so put a \0 in the last position
       // and buffer is now officially a string
       buffer[string_size] = '\0';

       if (string_size != read_size)
       {
           // Something went wrong, throw away the memory and set
           // the buffer to NULL
           free(buffer);
           buffer = NULL;
       }

       // Always remember to close the file.
       fclose(handler);
    }

    return buffer;
}

int main()
{
    char *string = ReadFile("yourfile.txt");
    if (string)
    {
        puts(string);
        free(string);
    }

    return 0;
}

Lass es mich wissen, wenn es nützlich ist oder du etwas daraus lernen könntest :)

lfzawacki
quelle
2
Sollte es nicht buffer[string_size] = '\0';statt lesen string_size+1? Afaik die eigentliche Saite geht von 0bis string_size-1und der \0Charakter muss also sein string_size, oder?
Aepsil0n
4
Das Verwenden ftellund Ermittelnfseek der Größe einer Datei ist unsicher: Securecoding.cert.org/confluence/display/seccode/…
Joakim
1
Dieser Code enthält einen Speicherverlust, Sie schließen die Datei nie. Es fehltfclose(handle)
Joakim
1
Es gibt einen Tippfehler, bei dem Sie fclose (Handle) aufrufen, es sollte fclose (Handler) sein
Eduardo Cobuci
3
Sie können das Setzen des Null-Terminators verwenden, calloc(2)anstatt malloc(1)zu überspringen.
14

Drucken Sie die Zeichen stattdessen direkt auf die Konsole, da die Textdatei möglicherweise sehr groß ist und Sie möglicherweise viel Speicher benötigen.

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

int main() {

    FILE *f;
    char c;
    f=fopen("test.txt","rt");

    while((c=fgetc(f))!=EOF){
        printf("%c",c);
    }

    fclose(f);
    return 0;
}
Sagar Shah
quelle
6

Verwenden Sie "read ()" anstelle von fscanf:

ssize_t read(int fildes, void *buf, size_t nbyte);

BESCHREIBUNG

Die Funktion read () soll versuchen, nbyteBytes aus der Datei, die dem offenen Dateideskriptor zugeordnet ist fildes, in den Puffer zu lesen , auf den durch verwiesen wird buf.

Hier ist ein Beispiel:

http://cmagical.blogspot.com/2010/01/c-programming-on-unix-implementing-cat.html

Arbeitsteil aus diesem Beispiel:

f=open(argv[1],O_RDONLY);
while ((n=read(f,l,80)) > 0)
    write(1,l,n);

Ein alternativer Ansatz ist die Verwendung getc/ putczu einer Zeit zum Lesen / Schreiben 1 Zeichen. Viel weniger effizient. Ein gutes Beispiel: http://www.eskimo.com/~scs/cclass/notes/sx13.html

DVK
quelle
readlässt Sie eine bestimmte Anzahl von Zeichen einlesen. Lesen Sie genug ein, um Ihren Puffer zu füllen, und speichern Sie ihn dann auf dem Bildschirm, löschen Sie ihn und wiederholen Sie den Vorgang, bis Sie das Ende der Datei erreicht haben.
Bis zum
1

Zwei Ansätze fallen mir ein.

Erstens nicht verwenden scanf. Verwenden Sie fgets()diesen Parameter, um die Puffergröße anzugeben und alle Zeilenumbrüche intakt zu lassen. Eine einfache Schleife über die Datei, die den Pufferinhalt druckt, sollte die Datei natürlich intakt kopieren.

Zweitens verwenden fread()oder die gemeinsame C-Sprache mit fgetc(). Diese würden die Datei in Blöcken fester Größe oder jeweils einem einzelnen Zeichen verarbeiten.

Wenn Sie die Datei über durch Leerzeichen getrennte Zeichenfolgen verarbeiten müssen, verwenden Sie entweder fgetsoder fread, um die Datei zu lesen, und strtokteilen Sie den Puffer in Leerzeichen. Vergessen Sie nicht, den Übergang von einem Puffer zum nächsten zu handhaben, da Ihre Zielzeichenfolgen wahrscheinlich die Puffergrenze überschreiten.

Wenn scanffür das Lesen eine externe Anforderung erforderlich ist, begrenzen Sie die Länge der Zeichenfolge, die möglicherweise gelesen wird, mit einem Genauigkeitsfeld im Formatbezeichner. Sagen Sie in Ihrem Fall mit einem 999-Byte-Puffer, scanf("%998s", str);der höchstens 998 Zeichen in den Puffer schreibt, und lassen Platz für den Nullterminator. Wenn einzelne Zeichenfolgen zulässig sind, die länger als Ihr Puffer sind, müssen Sie sie in zwei Teilen verarbeiten. Wenn nicht, haben Sie die Möglichkeit, den Benutzer höflich über einen Fehler zu informieren, ohne eine Sicherheitslücke für den Pufferüberlauf zu erstellen.

Überprüfen Sie unabhängig davon immer die Rückgabewerte und überlegen Sie, wie Sie mit fehlerhaften, böswilligen oder nur fehlerhaften Eingaben umgehen sollen.

RBerteig
quelle
1

Sie können fgetsdie Größe der gelesenen Zeichenfolge verwenden und begrenzen.

char *fgets(char *str, int num, FILE *stream);

Sie können das whilein Ihrem Code ändern in:

while (fgets(str, 100, file)) /* printf("%s", str) */;
Edu
quelle
0

Sie könnten die gesamte Datei mit dynamischer Speicherzuordnung lesen, dies ist jedoch keine gute Idee, da bei zu großer Datei Speicherprobleme auftreten können.

Lesen Sie also besser kurze Teile der Datei und drucken Sie sie aus.

#include <stdio.h>
#define BLOCK   1000

int main() {
    FILE *f=fopen("teste.txt","r");
    int size;
    char buffer[BLOCK];
    // ...
    while((size=fread(buffer,BLOCK,sizeof(char),f)>0)
            fwrite(buffer,size,sizeof(char),stdout);
    fclose(f);
    // ...
    return 0;
}
Rigon
quelle