Napisałem tę funkcję, aby odczytać wiersz z pliku:
const char *readLine(FILE *file) {
if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}
int maximumLineLength = 128;
char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);
if (lineBuffer == NULL) {
printf("Error allocating memory for line buffer.");
exit(1);
}
char ch = getc(file);
int count = 0;
while ((ch != '\n') && (ch != EOF)) {
if (count == maximumLineLength) {
maximumLineLength += 128;
lineBuffer = realloc(lineBuffer, maximumLineLength);
if (lineBuffer == NULL) {
printf("Error reallocating space for line buffer.");
exit(1);
}
}
lineBuffer[count] = ch;
count++;
ch = getc(file);
}
lineBuffer[count] = '\0';
char line[count + 1];
strncpy(line, lineBuffer, (count + 1));
free(lineBuffer);
const char *constLine = line;
return constLine;
}
Funkcja poprawnie odczytuje plik i za pomocą printf widzę, że łańcuch constLine również został poprawnie odczytany.
Jeśli jednak użyję funkcji np. Tak:
while (!feof(myFile)) {
const char *line = readLine(myFile);
printf("%s\n", line);
}
printf wydaje bełkot. Czemu?
fgets
zamiastfgetc
. Czytasz znak po znaku zamiast linii po linii.getline()
jest to część POSIX 2008. Bez niej mogą istnieć platformy podobne do POSIX, szczególnie jeśli nie obsługują reszty POSIX 2008, ale w świecie systemów POSIXgetline()
są w dzisiejszych czasach dość przenośne.Odpowiedzi:
Jeśli Twoim zadaniem nie jest wymyślenie funkcji czytania wiersz po wierszu, ale po prostu odczytanie pliku wiersz po wierszu, możesz użyć typowego fragmentu kodu obejmującego tę
getline()
funkcję (patrz strona podręcznika tutaj ):źródło
getline
jest to specyficzne dla GNU libc, tj. Dla Linuksa. Jeśli jednak intencją jest posiadanie funkcji czytania linii (w przeciwieństwie do nauki C), w Internecie dostępnych jest kilka funkcji czytania linii publicznych.if(line)
Wyboru jest zbędne. Dzwonieniefree(NULL)
to w zasadzie brak możliwości.źródło
(FILE*) fp
? Czyfp
już nie jest aFILE *
ifopen()
zwraca równieżFILE *
?getline
jest dobrą alternatywą. Zgadzam się, żeFILE *
obsada jest niepotrzebna.fp
nafilePointer
dla większej przejrzystości.W swojej
readLine
funkcji zwracasz wskaźnik doline
tablicy (Ściśle mówiąc, wskaźnik do jego pierwszego znaku, ale różnica nie ma tutaj znaczenia). Ponieważ jest to zmienna automatyczna (tzn. „Jest na stosie”), pamięć jest odzyskiwana po powrocie funkcji. Widzisz bełkot, ponieważprintf
umieścił własne rzeczy na stosie.Musisz zwrócić dynamicznie przydzielony bufor z funkcji. Już masz, to jest
lineBuffer
; wszystko, co musisz zrobić, to przyciąć go do pożądanej długości.DODANO (odpowiedź na pytanie uzupełniające w komentarzu):
readLine
zwraca wskaźnik do znaków tworzących linię. Ten wskaźnik jest tym, czego potrzebujesz do pracy z zawartością linii. Jest to również to, do czego musisz przejść,free
kiedy skończysz korzystać z pamięci zajmowanej przez te postacie. Oto jak możesz użyćreadLine
funkcji:źródło
źródło
fopen_s
uniemożliwiają importowanie kodu.printf
wyszuka specyfikatory formatu i nie wydrukuje znaków procentu oraz następujących znaków, jakimi są . Brak bajtów spowoduje zniknięcie wszystkich znaków w pozostałej części wiersza. (Nie mówcie, że bajty zerowe nie mogą się zdarzyć!)readLine()
zwraca wskaźnik do zmiennej lokalnej, co powoduje niezdefiniowane zachowanie.Aby się obejść, możesz:
readLine()
line
użyciamalloc()
- w tym przypadkuline
będzie trwałaźródło
Służy
fgets()
do odczytu linii z uchwytu pliku.źródło
Niektóre rzeczy są złe w przykładzie:
fprintf(stderr, ....
fgetc()
zamiastgetc()
.getc()
jest makrem,fgetc()
jest właściwą funkcjągetc()
zwraca aint
więcch
należy zadeklarować jakoint
. Jest to ważne, ponieważ porównanie zEOF
będzie obsługiwane poprawnie. Niektóre 8-bitowe zestawy znaków używają0xFF
jako prawidłowych znaków (na przykład ISO-LATIN-1), aEOF
które wynoszą -1, zostaną0xFF
przypisane dochar
.Istnieje potencjalne przepełnienie bufora na linii
Jeśli linia ma dokładnie 128 znaków,
count
w punkcie, który zostanie wykonany, ma 128 znaków .Jak zauważyli inni,
line
jest to tablica deklarowana lokalnie. Nie możesz zwrócić do niego wskaźnika.strncpy(count + 1)
skopiuje w większościcount + 1
znaków, ale zakończy jeśli trafi'\0'
Ponieważ zestawlineBuffer[count]
do'\0'
wiesz, że nigdy nie dostanie sięcount + 1
. Jeśli jednak tak się stanie, nie spowoduje zakończenia'\0'
, więc musisz to zrobić. Często widzisz coś takiego:jeśli masz
malloc()
wiersz do zwrócenia (zamiastchar
tablicy lokalnej ), typem zwrotu powinno byćchar*
- upuśćconst
.źródło
a co z tym?
źródło
Oto moje kilka godzin ... Czytanie całego pliku linia po linii.
źródło
fgetc
zamiastfgets
?zwróć uwagę, że zmienna „line” jest deklarowana w funkcji wywołującej, a następnie przekazywana, więc twoja
readLine
funkcja wypełnia predefiniowany bufor i po prostu zwraca go. W ten sposób działa większość bibliotek C.Są inne sposoby, o których jestem świadomy:
char line[]
jako statyczne (static char line[MAX_LINE_LENGTH]
-> zachowa jego wartość PO powrocie z funkcji). -> źle, funkcja nie jest ponownie wysyłana i może wystąpić wyścig -> jeśli wywołasz ją dwa razy z dwóch wątków, zastąpi to wynikimalloc()
wprowadzenie linii char [] i zwolnienie jej w wywoływaniu funkcji -> zbyt wiele drogichmalloc
s oraz delegowanie odpowiedzialności za zwolnienie bufora do innej funkcji (najbardziej eleganckim rozwiązaniem jest wywołaniemalloc
ifree
dowolne bufory w tej samej funkcji)btw, „jawne” rzutowanie z
char*
na naconst char*
jest zbędne.btw2, nie ma potrzeby
malloc()
lineBuffer, po prostu zdefiniuj gochar lineBuffer[128]
, więc nie musisz go zwalniaćbtw3 nie używa „dynamicznych tablic stosów” (definiujących tablicę jako
char arrayName[some_nonconstant_variable]
), jeśli nie wiesz dokładnie, co robisz, działa tylko w C99.źródło
Powinieneś używać funkcji ANSI do czytania linii, np. fgets. Po wywołaniu potrzebujesz free () w kontekście wywołania, np .:
źródło
Wdrożenie metody odczytu i pobierania zawartości z pliku (input1.txt)
Mam nadzieję, że to pomoże. Miłego kodowania!
źródło
Popełniasz błąd zwracając wskaźnik do zmiennej automatycznej. Linia zmienna jest przydzielona na stosie i trwa tylko tak długo, jak długo trwa funkcja. Nie możesz zwrócić do niego wskaźnika, ponieważ jak tylko on zwróci, pamięć zostanie podana gdzie indziej.
Aby tego uniknąć, albo zwracasz wskaźnik do pamięci, która znajduje się na stercie, np. lineBuffer, a użytkownik powinien być odpowiedzialny za wywołanie free (), kiedy to zrobi. Alternatywnie możesz poprosić użytkownika o podanie jako argumentu adresu pamięci, na którym chcesz zapisać zawartość wiersza.
źródło
Chcę kod z poziomu 0, więc zrobiłem to, aby odczytać treść słowa słownika wiersz po wierszu.
char temp_str [20]; // możesz zmienić rozmiar bufora zgodnie ze swoimi wymaganiami I długość pojedynczej linii w pliku.
Uwaga : Zainicjowałem bufor znakiem Null za każdym razem, gdy czytam wiersz. Ta funkcja może być zautomatyzowana, ale ponieważ potrzebuję proof of concept i chcę zaprojektować program Byte By Byte
źródło
int main() {
code
char temp_str [20] = {'\ 0'};code
c automatycznie wypełni każde pole zerowym terminatorem, ponieważ sposób działania deklaracji tablicowych polega na tym, że jeśli tablica zostanie zainicjowana z mniejszą liczbą elementów, które zawiera tablica, ostatni element wypełni pozostałe elementy.char temp_str[20] = {0}
również wypełnia całą tablicę znaków terminatorami zerowymi.Moje narzędzie od zera:
źródło
fgets
.Zapewnij przenośną i ogólną
getdelim
funkcję, test przeszedł przez msvc, clang, gcc.źródło
fgets
?getdelim
pozwala na dostosowanie ograniczników. Zauważam też, że nie mam limitu długości linii - w tym przypadku możesz użyć stosugetline
. (Oba opisane tutaj: man7.org/linux/man-pages/man3/getline.3.html )getdelim
igetline
zostały znormalizowane w POSIX.1-2008, ktoś inny wspomina na tej stronie).fgets
jest również standardem c, a nie specyficznym dla systemu Linux