PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C, beliebig lange Zeilen lesen



Thaomir
09.11.05, 09:49
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?

carstenj
09.11.05, 09:56
Hallo,

schau dir mal malloc und Co. an:
http://www.poplog.org/docs/popdocs/pop11/unix/malloc

zander
09.11.05, 13:09
Die GNU C Bibliothek bietet hierfür die Funktion getline() (vgl. getline(3)).

Thaomir
09.11.05, 22:03
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

Thaomir
09.11.05, 22:19
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?

L00NIX
09.11.05, 22:21
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...

Samsara
10.11.05, 01:03
Sonst wird Dir gerne auch bei mrunix.de geholfen, dem Schwesterforum von lf.de:
http://mrunix.de/forums/forumdisplay.php?f=24

Samsara
.

carstenj
10.11.05, 09:37
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.

zander
10.11.05, 11:19
@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).

carstenj
10.11.05, 11:37
Hi,

ok, wusste ich nicht. Thx.

fuffy
11.11.05, 07:49
@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

zander
11.11.05, 20:45
@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);
}
}

kratz00
12.11.05, 15:25
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.

zander
13.11.05, 19:02
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.