2010-03-28 9 views
26

Chcę czytać wiersz po wierszu, nie znając długości linii przed. Oto co mam do tej pory:Odczytaj wiersz z pliku, nie znając długości linii

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. 

teraz mogę dowiedzieć się długość linii, ale tylko na liniach, które są krótsze niż 4095 znaków, plus dwa char tablice wydawać się niewygodne sposób robienia zadania. Czy istnieje lepszy sposób to zrobić (już użyłem fgets(), ale powiedziano mi, że nie był najlepszy sposób)?

--Ry

Odpowiedz

14

Możesz zacząć od jakiegoś odpowiedniego rozmiaru swojego wyboru, a następnie użyć realloc połowie drogi, jeśli potrzebujesz więcej miejsca, jak:

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); 

musisz sprawdzić błędy alokacji po wywołań malloc i realloc.

+1

To 'realloc' nie' relloc'. –

+0

Podobnie jak notatka, czytanie znaków po znaku jest bardzo powolne. Powinieneś przeczytać to w dużych kawałkach (4-16k). – Blindy

+4

@Bindy: przedwczesna optymalizacja ... –

1

Jesteś blisko. Zasadniczo chcesz przeczytać fragmenty danych i sprawdzić je pod kątem znaków \n. Jeśli znajdziesz jeden, dobry, masz koniec linii. Jeśli nie, musisz zwiększyć bufor (np. Przydzielić nowy bufor dwukrotnie większy od pierwszego i skopiować dane z pierwszego w nowym, a następnie usunąć stary bufor i zmienić nazwę nowego bufora jako stary - lub po prostu realloc, jeśli jesteś w C), to czytaj dalej, aż znajdziesz zakończenie.

Gdy skończysz, tekst od początku bufora do postaci \n jest twoją linią. Skopiuj go do bufora lub pracuj na nim w miejscu, do Ciebie.

Po przygotowaniu do następnej linii można skopiować "resztę" wejścia przez bieżącą linię (zasadniczo lewą zmianę) i wypełnić resztę bufora danymi z wejścia. Następnie udaj się ponownie, aż zabraknie danych.

Oczywiście można to zoptymalizować za pomocą np. Kołowego bufora, ale powinno to być więcej niż wystarczające dla każdego rozsądnego algorytmu związanego z io.

5

Możesz zajrzeć do Chuck B. Falconer's public domain ggets library. Jeśli korzystasz z systemu z glibc, prawdopodobnie masz dostępną (niestandardową) funkcję getline.

+0

Nice! Wierzę, że mogę zaufać większości systemów typu UNIX, aby zainstalować glibc, więc jest to z pewnością świetny sposób na czytanie w liniach. – ryyst

+0

Co więcej, 'getline' został dołączony do najnowszego standardu POSIX, więc teraz * jest * standardem w systemie Unix. Nadal nie ma gwarancji, że jest on dołączony do c * per se *. – dmckee

1

Tak to zrobiłem dla stdin, jeśli nazywasz to jak readLine(NULL, 0) funkcja przydziela bufor dla ciebie o wielkości 1024 i pozwala mu rosnąć w krokach 1024. Jeśli wywołasz funkcję z readLine(NULL, 10) otrzymasz bufor z krokiem 10. Jeśli masz bufor, możesz podać mu jego rozmiar.

#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; 
} 
Powiązane problemy