Archiv verlassen und diese Seite im Standarddesign anzeigen : C, beliebig lange Zeilen lesen
Ich möchte beliebig lange Zeilen in C einlesen, jedoch scheitere ich am allokieren des nötigen Speichers:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *inputStream = fopen("/dev/urandom","r");
FILE *outputStream = fopen("trash","w");
int i;
char buffer[10000000];
for (i=0; i < 100000; i++) {
fgets(buffer,sizeof(buffer),inputStream);
fputs(buffer,outputStream);
}
fclose(inputStream);
fclose(outputStream);
return 0;
}
Die Lösung ist hässlich, weil ich Speicher allokiere, ohne zu wissen, wie lang die Zeile am Ende sein wird, ausserdem kann ich gar nicht genug allokieren, weil die Länge einer Zeile die Größe des Datentyps long überschreitet. Im Ergebnis ist die Datei nur halb so groß wie bei allen Sprachen mit automatischer Speicherverwaltung.
Irgendwelche Ideen?
Hallo,
schau dir mal malloc und Co. an:
http://www.poplog.org/docs/popdocs/pop11/unix/malloc
Die GNU C Bibliothek bietet hierfür die Funktion getline() (vgl. getline(3)).
Danke für den Hinweis. Leider liefert mir folgendes Programm einen Segmentation Fault.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *inputStream = fopen("/dev/urandom","r");
FILE *outputStream = fopen("trash","w");
int i;
char **buffer;
size_t *n;
for (i=0; i < 100000; i++) {
getline(buffer,n,inputStream);
fputs(*buffer,outputStream);
}
fclose(inputStream);
fclose(outputStream);
return 0;
}
Freekazonid
09.11.05, 22:16
1. allocates(dt wort? oO) getline denn speicher? wenn nein musst du das selber fuer deinen buffer machen. manpage sagt
*lineptr can contain a pointer to a
malloc()-allocated buffer *n bytes in size.
ich gehe davon aus das getline das nicht macht, ergo vorher malloc aufrufen
2. kann man zeilenweise aus /dev/urandom lesen? denke nicht dass das sinnvoll ist ._O
Naja, irgendwann wird mal ein newline kommen, besonders sinnvoll für den täglichen Gebrauch soll das Programm ja auch nicht werden. ;)
Ich habe es jetzt folgendermaßen zum Laufen gebracht:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *inputStream = fopen("/dev/urandom","r");
FILE *outputStream = fopen("trash","w");
int i;
char *buffer = NULL;
size_t n = 0;
for (i=0; i < 100000; i++) {
getline(&buffer,&n,inputStream);
fprintf(outputStream,"%s",buffer);
}
if (buffer)
free (buffer);
fclose(inputStream);
fclose(outputStream);
return 0;
}
Was mich nur stutzig macht ist die Tatsache, dass die trash-Datei in C 13 MB gross wird und in allen anderen getesteten Sprachen (Perl, Python, C++) 25 MB. Woran könnte das liegen?
Wie wäre es, wenn du einen Puffer anlegst, der nur eine begrenzte Größe hat, sagen wir 4096 Byte.
Dann liest du maximal diese 4096 Byte ein.
Diese sicherst du separat und liest die nächsten 4096 Byte ein.
Mit realloc() kannst du neuen Speicher allozieren und die neuen Daten an die zuerst gelesenen anhängen.
...uswusf...
Sonst wird Dir gerne auch bei mrunix.de geholfen, dem Schwesterforum von lf.de:
http://mrunix.de/forums/forumdisplay.php?f=24
Samsara
.
Hi,
Ich habe es jetzt folgendermaßen zum Laufen gebracht:
du darfst dich nicht der Illusion hingeben, dass C Programme richtig sind, nur weil sie nicht abstürzen. Dein Programm erzeugt undefiniertes Verhalten, weil:
- Du keinen Speicher allozierst, das heisst du schreibst in einen Speicherbereich, der dir nicht gehört
- Du free aufrufst, obwohl eben keine malloc aufgerufen wurde
Das kann einmal funktionieren, und beim nächsten Mal nicht. Dann suchst du dich dumm und dämlich. Wenn du dich ernsthaft für die C Programmierung interessierst, kommst du um ein Buch nicht herum.
@carstenj: das stimmt nicht, getline(char **lineptr, size_t *n, FILE *stream) alloziert einen Puffer ausreichend großer Länge, falls lineptr auf einen NULL-Zeiger verweist. Siehe getline(3).
Hi,
ok, wusste ich nicht. Thx.
@carstenj: das stimmt nicht, getline(char **lineptr, size_t *n, FILE *stream) alloziert einen Puffer ausreichend großer Länge, falls lineptr auf einen NULL-Zeiger verweist.
Das ist aber doch nur beim ersten Schleifendurchlauf so, oder irre ich mich?
Gruß
fuffy
@fuffy: Du hast Recht, ich hätte mir den Quelltext genauer ansehen sollen; richtig würde die Schleife wohl so aussehen:
for (i=0; i < 100000; i++) {
buffer = NULL;
n = 0;
getline(&buffer, &n, inputStream);
if (buffer != NULL) {
fprintf(outputStream, "%s", buffer);
free(buffer);
}
}
aus reiner neugier, muss ich mal nachfragen
Alternatively, before calling getline(), *lineptr can contain a pointer
to a malloc()-allocated buffer *n bytes in size. If the buffer is not
large enough to hold the line read in, getline() resizes the buffer to
fit with realloc(), updating *lineptr and *n as necessary.
in dem beispiel hier wird doch beim ersten aufruf von getline der buffer angelegt und beim naechsten aufruf durch den neuen wert ueberschrieben
und der man page zufolge wuerde getline falls der buffer zu klein ist mit realloc() den buffer anpassen
es steht zwar auch, das getline das nur macht wenn der buffer vorher mit malloc() angelegt wurde, was hier in dem bsp nicht der fall ist, aber getline legt den buffer sicher nicht viel anders an falls buffer = NULL
oder?
nur meine idee, ich koennte auch falsch liegen.
Die Funktion hat verschiedene Betriebsmodi. Falls *lineptr NULL ist, alloziert getline() mit malloc() einen ausreichend großen Speicherbereich und aktualisiert danach *lineptr und *n; falls *lineptr nicht NULL ist, prüft getline() anhand von *n, ob ausreichend Speicher alloziert wurde, ansonsten wird realloc() bemüht und sowohl *lineptr als auch *n aktualisiert.
In dem Beispiel der getline() Handbuchseite tritt beim ersten Durchlauf der erste Fall ein, danach der zweite; dies entspricht Thaomirs Programm. Das "richtig" in meinem letzten Beitrag ist irreführend und bezog sich lediglich auf das Verhalten, daß ich in meinem vorletzten Beitrag genannt hatte.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.