Überlegen wir uns jetzt noch, wie wir eine Datei tatsächlich in unserem Programm speichern können. Im letzten Kapitel haben wir einzelne Zeilen in Strings eingelesen und dann ausgegeben. Hier wollen wir also mehrere Strings nacheinander speichern. Wir brauchen also eine Liste von Strings.
char **zeilen;
Der Ablauf ist wie folgt: 1. Zeile in BUFF einlesen 2. Platz für den neuen Pointer in Liste allozieren 3. Platz für den neuen String in Liste allozieren 4. String kopieren
Für Schritt 1 können wir unseren Code aus dem letzten Kapitel übernehmen.
while(!feof(my_file)) {
if ( !fgets(BUFF, 500, my_file) ) {
printf("Dateiende!\n");
}
printf("%s", BUFF);
}
Für Schritt 2 müssen wir uns die Funktionsweise von Pointerlisten in Erinnerung rufen.
Wir legen eine Pointervariable mit dem Namen zeilen
an. Dieser Pointer zeigt auf einen Speicherbereich mit reserviertem Speicher. In diesem Speicherbereich liegen Objekte mit dem Typ *zeilen
, also *(**char)
. Diese Objekte sind somit auch Pointer. Da Strings lediglich Arrays von chars sind, macht das auch Sinn.
Wenn wir eine neue Zeile speichern wollen, müssen wir also zuerst Platz für einen neuen Pointer im Array zeilen
erstellen.
char **zeilen = NULL;
int num_zeilen = 0;
while(!feof(my_file)) {
if ( !fgets(BUFF, 500, my_file) ) {
printf("Dateiende!\n");
}
num_zeilen += 1;
zeilen = realloc(zeilen, num_zeilen * sizeof(*zeilen));
}
Statt sizeof(*zeilen)
könnten wir hier genauso sizeof(*char)
schreiben.
In Schritt 3 wollen wir den benötigten Platz für den Text der letzten Zeile reservieren. Hier stoßen wir auf ein Problem: wir wissen bis jetzt gar nicht wie lange die Zeile eigentlich ist. Erinnern wir uns dazu: ein String in C endet immer mit einem Nullchar \0
. Um die Länge des Strings zu finden, müssen wir also nur nach dem ersten Auftreten dieses Zeichens in BUFF
suchen.
int len_str = 0;
while ( BUFF[len_str] != 0 ) {
len_str += 1;
}
Jetzt haben wir die Länge des Strings ohne den Nullchar. Wir müssen also Platz für len_str + 1
chars allozieren.
zeilen[num_zeilen] = malloc( len_str + 1 );
Im letzten Schritt wollen wir nun noch alle Zeichen in unseren String kopieren. Dazu iterieren wir über BUFF
und übertragen jedes Zeichen in zeilen[num_zeilen-1]
.
for ( int i=0; i < str_len+1; i += 1 ) {
zeilen[num_zeilen][i] = BUFF[i];
}
Damit haben wir alle Zeilen in unsere Liste kopiert! Überprüfen können wir das, indem wir alle Zeilen nochmal ausgeben.
for ( int i = 0; i < num_zeilen; i += 1 ) {
printf("%s", zeilen[i]);
}
Das fertige Programm sieht also wie folgt aus.
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* my_file = fopen("test.txt", "r");
char BUFF[500];
char **zeilen = NULL;
int num_zeilen = 0;
while( 1 ) {
fgets(BUFF, 500, my_file);
if ( feof(my_file) ) {
break;
}
num_zeilen += 1;
zeilen = realloc(zeilen, num_zeilen*sizeof(*zeilen));
int str_len = 0;
for ( int i = 0; BUFF[i] != 0; i+=1 ) {
str_len += 1;
}
zeilen[num_zeilen-1] = malloc(str_len+1);
for ( int i = 0; i < str_len+1; i += 1 ) {
zeilen[num_zeilen-1][i] = BUFF[i];
}
}
for ( int i=0; i < num_zeilen; i += 1 ) {
printf("%s", zeilen[i]);
}
fclose(my_file);
}