Twitter und Facebook-Anbindung
X
Tweet Follow @twitterapi
!!! Anbindung an twitter und facebook öffnen !!!

Wenn Ihnen mein Online-Buch gefällt,
dann bedanken Sie sich doch mit einer kleinen Spende...

13.2 Textdateien

13.2 Textdateien

So, das war erst einmal genügend Theorie. Fangen wir also an. Am besten dafür sind die Textdateien, da man hier schnell seine Ergebnisse kontrollieren kann. Eines sei aber vorher noch erwähnt. Wenn Sie Pfadangaben in Strings, statisch als Konstante, implementieren, dann denken Sie daran, ein doppeltes Backslash zum abtrennen von Datei und Verzeichnissen zu nehmen, da ein einzelnes Backslash ein Escapezeichen einleiten würde.

Zum Beispiel: C:\\TestVerzeichnis\\Unterverzeichnis\\Testdatei.txt

Zum Seitenanfang
Zum Inhaltsverzeichnis

13.2.3 Die Funktionen fgets() und fputs()

13.2.3 Die Funktionen fgets() und fputs()

Ein kleiner Nachteil ergibt sich durch die zwei eben genannten Funktionen. Im Falle von Strings, wird immer nur ein Wort eingelesen bzw. geschrieben. So muss man extra für Leerzeichen sorgen. Des Weiteren hat man keine Chance, Zeilenumbrüche zu erkennen.

Abhilfe schaffen die Funktionen "fgets", zum einlesen ganzer Zeilen und "fputs", zum schreiben ganzer Zeilen. Folgendes Beispiel öffnet wieder eine Datei und holt, dieses Mal Zeile für Zeile, den Inhalt heraus und packt ihn in eine neue Datei. Anschließend sieht die Zieldatei exakt so aus, wie die Quelldatei.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
					
char*	pcstrFileNameSrc	= "C:\\Testdatei_Quelle.txt";
char*	pcstrFileNameDst	= "C:\\Testdatei_Ziel.txt";

FILE*	hFileSrc		= fopen(pcstrFileNameSrc, "r");
FILE*	hFileDst		= fopen(pcstrFileNameDst, "w+");

// Wenn die Dateien geöffnet werden konnten
if (hFileSrc != NULL) {
	if (hFileDst != NULL) {
		try {
			char astrTemp[1000];
			bool bCouldRead	= true;

			// Solange das Dateiende noch nicht erreicht ist
			while (bCouldRead) {
				fgets(astrTemp, 999, hFileSrc);

				// Wenn etwas eingelesen werden konnte
				if (bCouldRead = !feof(hFileSrc)) {
					fputs(astrTemp, hFileDst);
				} // end of if
			} // end of while
		} catch (...) {
			printf("Es ist ein Fehler aufgetreten!\n");
		} // end of try
		
		fclose(hFileSrc);
	} // end of if
	
	fclose(hFileDst);
} // end of if
					
Zum Seitenanfang
Zum Inhaltsverzeichnis

13.2.1 Die Funktionen fopen(), feof() und fclose()

13.2.1 Die Funktionen fopen(), feof() und fclose()

Die Funktion "fopen" veranlasst das Betriebssystem, eine Datei für einen Zugriff zu öffnen. Dabei werden der Pfad auf die gewünschte Datei und ein s.g. Modus, mit übergeben. Zurück erhält man das bereits erwähnte Datei-Handle. Der Modus gibt an, für welchen Zugriff man die Datei öffnen will (z.B. lesen oder schreiben) und was geschehen soll, wenn die Datei noch nicht vorhanden ist. Optional kann noch angegeben werden, mit welchem Zeichensatz die Datei ausgelesen bzw. beschrieben werden soll. Folgende wichtigen Modi gibt es.

Mit der Funktion "feof" kann man testen, ob man am Dateiende angekommen ist (end of file). Wie Sie sich denken können, benötigt die Funktion lediglich das Datei-Handle als Übergabeparameter. Im Gegensatz zu anderen Sprachen, ist diese Funktion aber mit Vorsicht zu genießen, da sie erst "true" zurückgibt, wenn einmal erfolglos versucht wurde, aus einer Datei zu lesen bzw. in sie zu schreiben. Somit ließt man einmal umsonst aus einer Datei.

Die Funktion "fclose" beendet den Dateizugriff und gibt die Datei für den Zugriff durch andere Benutzer und oder Programme, frei. Auch ihr wird lediglich der entsprechende Datei-Handle übergeben. Vergisst man die Freigabe, kann die Datei bis zum nächsten Neustart des Computers, nicht mehr verwendet werden! Das Schließen einer nicht geöffneten Datei, führt zu einem Fehler bzw. zu einer Exception.

Zum Seitenanfang
Zum Inhaltsverzeichnis

13.2.2 Die Funktionen fscanf() und fprintf()

13.2.2 Die Funktionen fscanf() und fprintf()

Wie Sie sich vielleicht denken könnt, stehen die Funktionen für "File-ScanF" und "File-PrintF". Sie funktionieren wie die bekannten Funktionen "scanf" und "printf", nur mit dem Unterschied, dass man beim Aufruf noch das Datei-Handle mit übergibt und die Ein - bzw. Ausgabe, nichts mit der Konsole, sondern mit Dateien zu tun hat.

Im folgenden Beispiel, werde ich eine Textdatei öffnen, den Inhalt Stück für Stück auslesen und ihn in eine andere Datei schreiben. Damit wird ein (langsames) Kopieren einer Datei, realisiert.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
					
char*	pcstrFileNameSrc	= "C:\\Testdatei_Quelle.txt";
char*	pcstrFileNameDst	= "C:\\Testdatei_Ziel.txt";

FILE*	hFileSrc		= fopen(pcstrFileNameSrc, "r");
FILE*	hFileDst		= fopen(pcstrFileNameDst, "w+");

// Wenn die Dateien geöffnet werden konnten
if (hFileSrc != NULL) {
	if (hFileDst != NULL) {
		try {
			char astrTemp[100];
			bool bCouldRead	= true;

			// Solange das Dateiende noch nicht erreicht ist
			while (bCouldRead) {
				fscanf(hFileSrc, "%s", astrTemp);

				// Wenn das Einlesen funktioniert hat
				if (bCouldRead = !feof(hFileSrc)) {
					fprintf(hFileDst, "%s ", astrTemp);
				} // end of if
			} // end of while
		} catch (...) {
			printf("Es ist ein Fehler aufgetreten!\n");
		} // end of try
	
		fclose(hFileSrc);
	} // end of if

	fclose(hFileDst);
} // end of if
					

Genauso wie man das von der Funktion "scanf" kennt, kann man auch Integer und andere Typen, gezielt einlesen. Allerdings muss man dann darauf gefasst sein, dass der gelesene Inhalt nicht dem entspricht, was man erwartet (jemand könnte die Datei manipuliert haben). Deshalb ist es stets am klügsten, erst einmal Strings einzulesen und jene, nach entsprechender Prüfung, in das gewünschte Format zu Konvertieren. Wie Sie in Zeile 19 sehen, prüfe ich vor dem Schreiben, ob das Einlesen funktioniert hat. Würde ich dies nicht tun, würde in der Zieldatei, der letzte Eintrag der Quelldatei doppelt vorkommen, da "fscanf" im Fehlerfall die übergebene Variable für den Inhalt, nicht anfasst und somit noch der letzte Wert drin steht.

Alternativ, stehen auch die sicheren Funktionen "fscanf_s" und "fprintf_s" zur Verfügung, mit welchen Sie besser auf evtl. auftretende Fehler reagieren können. Mehr zu diesen Funktionen findet Sie in der Hilfe oder im MSDN.

Zum Seitenanfang
Zum Inhaltsverzeichnis

13.2.4 Testdateien mit Streams

13.2.4 Testdateien mit Streams

Eine elegantere Variante mit Dateien zu arbeiten, sind die Dateistreams. Sie nehmen Ihnen ein wenig Arbeit ab, da sie selbst Objekte sind, die viele Dinge kapseln, also im Hintergrund ausführen, um die Sie sich nicht mehr kümmern müssen. Eigentlich ist dieses Kapitel an der falschen Stelle, da ich Objekte erst im nächsten großen Abschnitt bespreche, aber auf der anderen Seite, gehört es der Vollständigkeit halber, hier her und bietet zudem eine schöne Einleitung zum Thema objektorientierte Programmierung.

Im folgenden Quelltext werde ich genau das gleiche machen, wie eben, also eine Textdatei händisch kopieren, nur dass ich jetzt Dateistreams benutzen werde. Nebenbei sei noch erwähnt, dass ich dafür die Header-Datei "fstream" benutze.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
					
char*		pcstrFileNameSrc	= "C:\\Testdatei_Quelle.txt";
char*		pcstrFileNameDst	= "C:\\Testdatei_Ziel.txt";

std::ifstream	oSrcFileStream(pcstrFileNameSrc);
std::ofstream	oDstFileStream(pcstrFileNameDst);

// Wenn die Dateien ge�ffnet werden konnten
if (oSrcFileStream.is_open() && oDstFileStream.is_open()) {
	try {
		char cValue;
		bool bCouldRead		= true;

		// Solange gelesen werden kann
		while (bCouldRead) {
			oSrcFileStream >> cValue;
		
			// Wenn das Einlesen funktioniert hat
			if (bCouldRead = !oSrcFileStream.eof()) {
				oDstFileStream << cValue;
			} // end of if
		} // end of while
	} catch (...) {
		printf("Es ist ein Fehler aufgetreten!\n");
	} // end of try
} // end of if
					

In Zeile 4 und 5 sehen Sie, dass bereits das öffnen einer Datei mit Streams, anders aussieht. Dies liegt daran, dass man die Datei nicht selbst öffnet, sondern dies der Kontrolle der Objekte überlässt. In den beiden Zeilen werden also lediglich zwei Streamobjekte erzeugt und es wird ihnen mitgeteilt, welche Dateien sie benutzen sollen. Normalerweise muss man auch hier noch einen Modus mit angeben, allerdings gibt es für jene Parameter entsprechende Defaultwerte, welche genau so definiert sind, wie ich sie brauche, um in Textdateien zu lesen, bzw. zu schreiben.

Einen weiteren Unterschied zu eben sehen Sie in Zeile 8. Vorhin habe ich getrennt geprüft, ob die Dateien geöffnet werden konnten, damit ich sie auch nur in diesem Fall schließe. Auch hier kommen Ihnen die Streamobjekte entgegen. Da sie, wie erwähnt, den kompletten Dateizugriff Kapseln (auch das Schließen der Dateien), brauche ich keine getrennten Prüfungen und spare mir damit ein paar Zeilen Quelltext.

In Zeile 15 wird nun aus der Datei bzw. aus dem Stream, Zeichenweise gelesen. Entsprechend ähnlich verhält sich die Sache in Zeile 19, nur mit dem Unterschied, dass hier das Schreiben in die Datei realisiert wird.

Leider sind Streams keine Allzweckwaffe. Zum einen muss man trotzdem immer prüfen, ob das Einlesen funktioniert hat und zum anderen sieht das Ergebnis, also die Zieldatei, nicht so aus wie gewünscht. Es fehlen wieder sämtliche Leerzeichen und auch alle Zeilenumbrüche. Wie man dies beheben kann, sehen Sie dann, wenn ich zeige, wie man Binärdateien mit Streams bearbeitet. Abschließend sei nochmals erwähnt, dass man die Dateien nicht wieder freigeben braucht, da dies geschieht, sobald man den Gültigkeitsbereich der Objekte verlässt und sie im Zuge dessen, freigegeben werden (es passiert im Destruktor der Streams).

Zum Seitenanfang
Zum Inhaltsverzeichnis

© Copyright by Thomas Weiß, 2009 - 2012