2008-11-03 19 views
8

Rozważmy następujący prosty program w C, który odczytać plik do bufora i wyświetlaczy, które bufor konsoli:Dlaczego mój prosty program C wyświetla śmieci na standardowe wyjście?

#include<stdio.h> 

main() 
{ 
    FILE *file; 
    char *buffer; 
    unsigned long fileLen; 
    //Open file 
    file = fopen("HelloWorld.txt", "rb"); 
    if (!file) 
    { 
     fprintf(stderr, "Unable to open file %s", "HelloWorld.txt"); 
     return; 
    } 
    //Get file length 
    fseek(file, 0, SEEK_END); 
    fileLen=ftell(file); 
    fseek(file, 0, SEEK_SET); 
    //Allocate memory 
    buffer=(char *)malloc(fileLen+1); 
    if (!buffer) 
    { 
     fprintf(stderr, "Memory error!"); 
     fclose(file); 
     return; 
    } 
    //Read file contents into buffer 
    fread(buffer, fileLen, 1, file); 
    //Send buffer contents to stdout 
    printf("%s\n",buffer);  
    fclose(file); 
} 

Plik będzie czytać po prostu zawiera:

Hello World!

Wyjście jest:

Hello World ²²²²▌▌▌▌▌▌▌↔☺

Minęło trochę czasu odkąd zrobił coś znaczącego w języku C/C++, ale zwykle zakładam, że bufor był przydzielany większy niż to konieczne, ale nie wydaje się, żeby tak było.

FileLen kończy się 12, które jest dokładne.

myślę teraz, że muszę być po prostu wyświetlając bufor źle, ale nie jestem pewien, co robię źle.

może ktoś mi wskazówką do tego, co robię źle?

Odpowiedz

38

Musisz NUL-zakończyć ciąg znaków. Przed wydrukowaniem dodaj

buffer[fileLen] = 0; 

.

+1

Wyjmij -1 i masz rację! – GEOCHET

+0

Próbowałem to zrobić w pewnym momencie, ale z jakiegoś powodu wygląda na to, że miałem mózgowy pierdziel i użyłem \ n zamiast \ 0 z jakiegoś głupiego powodu. Powiedziałem, że jestem zardzewiały! – GEOCHET

+0

Brakowało mi, że bufor był fileLen + 1 bajt długo ... – JesperE

8

JesperE jest poprawny w odniesieniu do problemu z terminem zerowania w twoim przykładzie, dodam tylko, że jeśli przetwarzasz pliki tekstowe, lepiej jest użyć fgets() lub czegoś podobnego, ponieważ poprawnie obsłuży sekwencje nowej linii przez różne platform i zawsze będzie dla ciebie kończył łańcuch. Jeśli naprawdę pracujesz z danymi binarnymi, nie chcesz używać printf() do wyprowadzania danych, ponieważ funkcje printf oczekują łańcuchów, a bajt zerowy w danych spowoduje obcięcie danych wyjściowych.

+0

Dzięki za poradę. To jest plik binarny (jego części). Używam właśnie printf() w tej chwili, aby uzyskać moje łożyska i "debugować". – GEOCHET

28

podejście JesperE będzie działać, ale może być zainteresowany, aby wiedzieć, że istnieje alternatywny sposób obsługi tego.

Zawsze można drukować ciąg znanej długości, nawet gdy nie ma NUL-terminator, dostarczając długość do printf jako precyzją polu Parametry:

printf("%.*s\n", fileLen, buffer); 

To pozwala drukować ciąg bez modyfikowanie bufora.

0

calloc można użyć zamiast malloc przydzielić pamięci, który jest już zainicjowane. calloc przyjmuje dodatkowy argument. Przydaje się do przydzielania tablic; pierwszy parametr calloc wskazuje liczbę elementów w tablicy, dla których chcesz przydzielić pamięć, a drugi argument to rozmiar każdego elementu. Ponieważ wielkość char jest zawsze 1, możemy po prostu przekazać 1 jako drugi argument:

buffer = calloc (fileLen + 1, 1); 

W C, nie ma potrzeby, aby rzucić wartość zwracaną malloc lub calloc. Powyższe zapewnia, że ​​łańcuch zostanie zakończony zerem, nawet jeśli odczyt pliku zakończy się przedwcześnie z jakiegokolwiek powodu.calloc trwa dłużej niż malloc, ponieważ musi wyzerować całą pamięć, o którą prosiłeś, zanim ci ją przekaże.

+0

@downvoter: jak miło mi wyjaśnić – dreamlax

2

Twoje podejście do określenia rozmiaru pliku poprzez dążenie do końca pliku, a następnie za pomocą ftell() jest źle:

  • Jeśli jest to plik tekstowy, otwierany bez "b" w drugim parametrze na wezwanie fopen(), wtedy ftell() może nie podać liczby znaków, które można odczytać z pliku. Na przykład, system Windows używa dwóch bajtów dla końca wiersza, ale gdy jest czytany, jest to jeden char. W rzeczywistości wartość zwracana ftell() dla strumieni otwartych w trybie tekstowym jest przydatna tylko w przypadku wywołań do fseek(), a nie w celu określenia rozmiaru pliku.
  • Jeśli jest to plik binarny, otwierane "b" w drugim parametrze do fopen(), wówczas średnia C ma do powiedzenia:

    Ustawianie wskaźnik pozycji pliku do EOF, jak z fseek(file, 0, SEEK_END), ma niezdefiniowane zachowanie dla strumienia binarnego (ze względu na możliwe końcowe znaki zerowe) lub dla dowolnego strumienia z zależnym od stanu kodowaniem, które nie kończy się z pewnością w początkowym stanie zmiany.

Więc, co robisz, nie jest koniecznie będzie działać w standardzie C Najprościej jest użyć fread() czytać, a jeśli zdarzy się potrzeba więcej pamięci, użyj realloc(). Twój system może dostarczyć mmap() lub może zagwarantować ustawienie wskaźnika pozycji pliku na końcu pliku dla strumieni binarnych —, ale poleganie na tych nie jest przenośne.

Zobacz także ten C-FAQ: What's the difference between text and binary I/O?.

Powiązane problemy