Programmieren mit C


Inhalt


Willkommen

Installation

Das Terminal

Exkurs: Scanf und Sonderzeichen

Exkurs: continue & break

Statische Arrays

Exkurs: sizeof

Dynamische Arrays

Exkurs: Speicherbereiche

Funktionen

Exkurs: Funktionen & Arrays

Kommandozeilenparameter

Structs

Dateien

Anwendung: Textdatei einlesen

Hintergrund: Unicode

Stringbibliothek

Ausblick

Dark Mode

Anwendung: Textdatei einlesen

Ü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.

Alt text: Skizze des Aufbaus von Listen von Strings

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);
}