Ich habe diese Funktion geschrieben, um eine Zeile aus einer Datei zu lesen:
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;
}
Die Funktion liest die Datei korrekt und mit printf sehe ich, dass die constLine-Zeichenfolge auch korrekt gelesen wurde.
Wenn ich jedoch die Funktion zB so benutze:
while (!feof(myFile)) {
const char *line = readLine(myFile);
printf("%s\n", line);
}
printf gibt Kauderwelsch aus. Warum?
fgets
anstelle vonfgetc
. Sie lesen Zeichen für Zeichen anstatt Zeile für Zeile.getline()
ein Teil von POSIX 2008 ist. Es kann POSIX-ähnliche Plattformen ohne POSIX 2008 geben, insbesondere wenn sie den Rest von POSIX 2008 nicht unterstützen, aber in der Welt der POSIX-Systemegetline()
heutzutage ziemlich portabel sind.Antworten:
Wenn Ihre Aufgabe nicht darin besteht, die zeilenweise Lesefunktion zu erfinden, sondern nur die Datei zeilenweise zu lesen, können Sie ein typisches Codefragment verwenden, das die
getline()
Funktion umfasst (siehe Handbuchseite hier ):quelle
getline
spezifisch für GNU libc, dh für Linux. Wenn jedoch eine Zeilenlesefunktion (im Gegensatz zum Lernen von C) vorgesehen ist, stehen im Web mehrere gemeinfreie Zeilenlesefunktionen zur Verfügung.if(line)
Scheck ist überflüssig. Anrufenfree(NULL)
ist im Wesentlichen ein No-Op.quelle
(FILE*) fp
? Ist das nichtfp
schon einFILE *
und gibt auch einfopen()
zurückFILE *
?getline
eine gute Alternative. Ich bin damit einverstanden, dass dieFILE *
Besetzung unnötig ist.fp
umfilePointer
für mehr Klarheit.In Ihrer
readLine
Funktion geben Sie einen Zeiger auf dasline
Array zurück (genau genommen einen Zeiger auf das erste Zeichen, aber der Unterschied ist hier irrelevant). Da es sich um eine automatische Variable handelt (dh sich auf dem Stapel befindet), wird der Speicher bei der Rückkehr der Funktion zurückgefordert. Sie sehen Kauderwelsch, weilprintf
er seine eigenen Sachen auf den Stapel gelegt hat.Sie müssen einen dynamisch zugewiesenen Puffer von der Funktion zurückgeben. Du hast schon einen, es ist
lineBuffer
; Alles, was Sie tun müssen, ist es auf die gewünschte Länge zu kürzen.ADDED (Antwort auf die Folgefrage im Kommentar): Gibt
readLine
einen Zeiger auf die Zeichen zurück, aus denen die Zeile besteht. Mit diesem Zeiger müssen Sie mit dem Inhalt der Zeile arbeiten. Es ist auch das, woran Sie übergeben müssen,free
wenn Sie den Speicher dieser Zeichen nicht mehr verwenden. So können Sie diereadLine
Funktion verwenden:quelle
quelle
fopen_s
Der Code ist nicht portierbar.printf
sucht nach Formatspezifizierern und druckt keine Prozentzeichen und die folgenden Zeichen wie sie sind . Null-Bytes lassen alle Zeichen in der restlichen Zeile verschwinden. (Sagen Sie mir nicht, dass keine Null-Bytes passieren können!)readLine()
Gibt den Zeiger auf die lokale Variable zurück, was zu undefiniertem Verhalten führt.Um herumzukommen, können Sie:
readLine()
line
Verwendung zumalloc()
- in diesem Fallline
bleibt er bestehenquelle
Verwenden Sie
fgets()
diese Option , um eine Zeile aus einem Dateihandle zu lesen.quelle
Einige Dinge stimmen mit dem Beispiel nicht:
fprintf(stderr, ....
fgetc()
eher als zu verwendengetc()
.getc()
ist ein Makro,fgetc()
ist eine richtige Funktiongetc()
gibt einint
so zurückch
sollte als deklariert werdenint
. Dies ist wichtig, da der Vergleich mitEOF
korrekt behandelt wird. Einige 8-Bit-Zeichensätze werden0xFF
als gültiges Zeichen verwendet (ISO-LATIN-1 wäre ein Beispiel) undEOF
das ist -1,0xFF
wenn es a zugewiesen wirdchar
.An der Leitung liegt ein potenzieller Pufferüberlauf vor
Wenn die Zeile genau 128 Zeichen lang ist,
count
ist sie an dem Punkt, an dem sie ausgeführt wird, 128.Wie andere bereits betont haben,
line
handelt es sich um ein lokal deklariertes Array. Sie können keinen Zeiger darauf zurückgeben.strncpy(count + 1)
kopiert höchstenscount + 1
Zeichen, endet jedoch, wenn es trifft.'\0'
Weil Sie eingestellt habenlineBuffer[count]
,'\0'
wissen Sie, dass es niemals erreicht wirdcount + 1
. Wenn dies jedoch der Fall wäre, würde keine Kündigung'\0'
aktiviert, sodass Sie dies tun müssen. Sie sehen oft Folgendes:Wenn Sie
malloc()
eine Zeile zurückgeben möchten (anstelle Ihres lokalenchar
Arrays), sollte Ihr Rückgabetyp sein:char*
- Löschen Sie dieconst
.quelle
was ist mit diesem?
quelle
Hier sind meine mehreren Stunden ... Lesen der gesamten Datei Zeile für Zeile.
quelle
fgetc
stattfgets
?Beachten Sie, dass die Variable 'line' in der aufrufenden Funktion deklariert und dann übergeben wird, sodass Ihre
readLine
Funktion den vordefinierten Puffer füllt und ihn nur zurückgibt. So funktionieren die meisten C-Bibliotheken.Es gibt andere Möglichkeiten, die mir bekannt sind:
char line[]
als statisch (static char line[MAX_LINE_LENGTH]
-> es behält seinen Wert NACH der Rückkehr von der Funktion). -> schlecht, die Funktion ist nicht wiedereintrittsfähig und es kann zu einer Racebedingung kommen -> Wenn Sie sie zweimal von zwei Threads aus aufrufen, werden die Ergebnisse überschriebenmalloc()
die char-Zeile [] freigeben und beim Aufrufen von Funktionen freigeben -> zu viele teuremalloc
s, und die Verantwortung für das Freigeben des Puffers an eine andere Funktion delegieren (die eleganteste Lösung besteht darin, Puffer in derselben Funktion aufzurufenmalloc
undfree
einzuschalten).Übrigens ist 'explizites' Casting von
char*
bisconst char*
überflüssig.Übrigens ist
malloc()
der lineBuffer nicht erforderlich. Definieren Sie ihn einfachchar lineBuffer[128]
, damit Sie ihn nicht freigeben müssenÜbrigens verwenden Sie keine Stapelstapel mit dynamischer Größe (definieren Sie das Array als
char arrayName[some_nonconstant_variable]
). Wenn Sie nicht genau wissen, was Sie tun, funktioniert dies nur in C99.quelle
Sie sollten die ANSI-Funktionen zum Lesen einer Zeile verwenden, z. fgets. Nach dem Aufruf benötigen Sie free () im Aufrufkontext, zB:
quelle
Implementieren Sie eine Methode zum Lesen und Abrufen von Inhalten aus einer Datei (input1.txt).
Ich hoffe das hilft. Viel Spaß beim Codieren!
quelle
Sie machen den Fehler, einen Zeiger auf eine automatische Variable zurückzugeben. Die variable Zeile wird im Stapel zugewiesen und lebt nur so lange, wie die Funktion lebt. Sie dürfen keinen Zeiger darauf zurückgeben, da der Speicher bei der Rückgabe an anderer Stelle angegeben wird.
Um dies zu vermeiden, geben Sie entweder einen Zeiger auf den Speicher zurück, der sich auf dem Heap befindet, z. lineBuffer und es sollte in der Verantwortung des Benutzers liegen, free () aufzurufen, wenn er damit fertig ist. Alternativ können Sie den Benutzer bitten, Ihnen als Argument eine Speicheradresse zu übergeben, auf die der Zeileninhalt geschrieben werden soll.
quelle
Ich möchte einen Code von Grund 0, also habe ich dies getan, um den Inhalt des Wörterbuchs Zeile für Zeile zu lesen.
char temp_str [20]; // Sie können die Puffergröße entsprechend Ihren Anforderungen und der Länge einer einzelnen Zeile in einer Datei ändern.
Hinweis Ich habe den Puffer jedes Mal, wenn ich eine Zeile lese, mit Nullzeichen initialisiert. Diese Funktion kann automatisiert werden, aber da ich einen Proof of Concept benötige und ein Programm Byte für Byte entwerfen möchte
quelle
int main() {
code
char temp_str [20] = {'\ 0'};code
c füllt automatisch jeden Slot mit einem Null-Terminator, da Array-Deklarationen so funktionieren, dass das letzte Element die verbleibenden Elemente ausfüllt, wenn ein Array mit weniger Elementen initialisiert wird, die das Array enthält.char temp_str[20] = {0}
füllt auch das gesamte Zeichenarray mit Nullterminatoren.Mein Gerät von Grund auf neu:
quelle
fgets
, die verwendet werden könnte.Stellen Sie eine tragbare und generische
getdelim
Funktion bereit, Test bestanden über msvc, clang, gcc.quelle
fgets
existiert?getdelim
ermöglicht benutzerdefinierte Trennzeichen. Außerdem fällt mir auf, dass es keine Zeilenlängenbeschränkung gibt - in diesem Fall können Sie den Stapel mit verwendengetline
. (Beide hier beschrieben: man7.org/linux/man-pages/man3/getline.3.html )getdelim
undgetline
wurde in POSIX.1-2008 standardisiert, was auf dieser Seite von einer anderen Person erwähnt wird).fgets
ist auch Standard c und nicht Linux-spezifisch