2010-03-28 16 views
26

Ich möchte Zeile für Zeile in eine Datei einlesen, ohne vorher die Zeilenlänge zu kennen. Hier ist, was ich bisher habe:Zeile aus Datei lesen, ohne die Zeilenlänge zu kennen

int ch = getc(file); 
int length = 0; 
char buffer[4095]; 

while (ch != '\n' && ch != EOF) { 
    ch = getc(file); 
    buffer[length] = ch; 
    length++; 
} 

printf("Line length: %d characters.", length); 

char newbuffer[length + 1]; 

for (int i = 0; i < length; i++) 
    newbuffer[i] = buffer[i]; 

newbuffer[length] = '\0'; // newbuffer now contains the line. 

ich jetzt die Zeilenlänge herausfinden kann, aber nur für Linien, die kürzer als 4095 Zeichen, plus der zwei char-Arrays scheint, wie eine unangenehme Art und Weise die Aufgabe, zu tun. Gibt es einen besseren Weg, dies zu tun (Ich habe bereits fgets() verwendet, aber mir wurde gesagt, dass es nicht der beste Weg ist)?

--Ry

Antwort

14

Sie können mit einer geeigneten Größe Ihrer Wahl beginnen und dann realloc Mitte verwenden, wenn Sie mehr Speicherplatz benötigen als:

int CUR_MAX = 4095; 
char *buffer = (char*) malloc(sizeof(char) * CUR_MAX); // allocate buffer. 
int length = 0; 

while ((ch != '\n') && (ch != EOF)) { 
    if(length ==CUR_MAX) { // time to expand ? 
     CUR_MAX *= 2; // expand to double the current size of anything similar. 
     buffer = realloc(buffer, CUR_MAX); // re allocate memory. 
    } 
    ch = getc(file); // read from stream. 
    buffer[length] = ch; // stuff in buffer. 
    length++; 
} 
. 
. 
free(buffer); 

Sie werden für Zuordnungsfehler nach Anrufen zu malloc und realloc haben zu überprüfen.

+1

Es ist 'realloc' nicht' relloc'. –

+0

Nur als eine Anmerkung, Zeichen-zu-Zeichen-Lesen ist extrem langsam. Sie sollten es in großen Stücken lesen (4-16k). – Blindy

+4

@Blindy: vorzeitige Optimierung ... –

1

Sie sind in der Nähe. Grundsätzlich möchten Sie Datenblöcke lesen und sie auf \n Zeichen prüfen. Wenn Sie einen finden, gut, haben Sie ein Ende der Linie. Wenn Sie dies nicht tun, müssen Sie Ihren Puffer erhöhen (z. B. einen neuen Puffer doppelt so groß wie der erste zuweisen und die Daten vom ersten in den neuen kopieren, dann den alten Puffer löschen und Ihren neuen Puffer als alt - oder einfach realloc wenn du in C bist, dann lies noch mehr, bis du ein Ende findest.

Sobald Sie Ihre Endung haben, ist der Text vom Anfang des Puffers zum \n Zeichen Ihre Zeile. Kopieren Sie es in einen Puffer oder arbeiten Sie an Ort und Stelle, bis zu Ihnen.

Nachdem Sie für die nächste Zeile bereit sind, können Sie den "Rest" der Eingabe über die aktuelle Zeile (im Grunde eine Verschiebung nach links) kopieren und den Rest des Puffers mit Daten aus dem Eingang füllen. Sie gehen dann wieder, bis Sie keine Daten mehr haben.

Dies kann natürlich optimiert werden, zum Beispiel mit einem zirkularen Puffer, aber dies sollte mehr als ausreichend für jeden vernünftigen io-gebundenen Algorithmus sein.

5

Vielleicht möchten Sie in Chuck B. Falconer's public domain ggets library suchen. Wenn Sie sich auf einem System mit glibc befinden, steht Ihnen wahrscheinlich eine (nicht standardmäßige) getline-Funktion zur Verfügung.

+0

Schön! Ich glaube, ich kann den meisten UNIX-ähnlichen Systemen vertrauen, glibc installiert zu haben, also ist dies definitiv eine gute Art, Zeilen zu lesen. – ryyst

+0

Darüber hinaus wurde "getline" in den letzten POSIX-Standard aufgenommen, so dass es nun Standard unter Unix ist. Trotzdem keine Garantie, dass es in c * per se * enthalten ist. – dmckee

1

Das ist, wie ich es für stdin gemacht habe, wenn Sie es wie readLine(NULL, 0) nennen, weist die Funktion einen Puffer für Sie mit der Größe von 1024 zu und lassen Sie es in Schritten von 1024 wachsen. Wenn Sie die Funktion mit readLine(NULL, 10) aufrufen, erhalten Sie puffern mit Schritten von 10. Wenn Sie einen Puffer haben, können Sie ihn mit seiner Größe liefern.

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

char *readLine(char **line, size_t *length) 
{ 
    assert(line != NULL); 
    assert(length != NULL); 

    size_t count = 0; 

    *length = *length > 0 ? *length : 1024; 

    if (!*line) 
    { 
     *line = calloc(*length, sizeof(**line)); 
     if (!*line) 
     { 
      return NULL; 
     } 
    } 
    else 
    { 
     memset(*line, 0, *length); 
    } 

    for (int ch = getc(stdin); ch != '\n' && ch != EOF; ch = getc(stdin)) 
    { 
     if (count == *length) 
     { 
      *length += 2; 
      *line = realloc(*line, *length); 
      if (!*line) 
      { 
       return NULL; 
      } 
     } 

     (*line)[count] = (char)ch; 

     ++count; 
    } 

    return *line; 
} 
Verwandte Themen