# Datei Zeile für Zeile auslesen



## Trendy Andy (13. Mai 2004)

Hallo,

Ich möchte eine Datei mit Gleitkommazahlen in jeder Zeile auslesen. Jedoch finde ich keinen geeigneten Befehl, um im die nächste Zeile der Datei zu springen und die nächste Zahl einzulesen. Kann mir da jemand helfen?

PS: Sorry wenn das schonmal gefragt wurde. Hab nix passendes in der Hilfe gefunden.


----------



## Gawayn (13. Mai 2004)

Du hast nix Passendes in der Hilfe gefunden? MSDN listet doch alle Funktionen zu diesem Thema feinsäuberlich auf, inklusive fgets(), was meine erste Assoziation zu diesem Thema wäre. Oder habe ich was falsch verstanden?


```
FILE *f = fopen( "test.txt", "r" ) ;
while( !feof( f ) )
{
  fgets( buffer, sizeof( buffer ), f ) ;
  // hier den String verarbeiten
}
```

Gawayn


----------



## Trendy Andy (13. Mai 2004)

Ja mit fgets les ich die Zeile ein. Aber wie komm ich jetzt in die nächste?


----------



## Gawayn (13. Mai 2004)

Wie du an die nächste Zeile kommst, siehst du doch in meinem kleinen Beispielprogramm. fgets() liest eine Zeile ein. Somit befindet sich der Dateizeiger HINTER der soeben eingelesenen Zeile. Wenn du nun fgets() noch einmal aufrufst, wird die nächste Zeile eingelesen, und immer so weiter.

Gawayn


----------



## Trendy Andy (13. Mai 2004)

Ach so. Alles klar. Nur kommt dann gleich das nächste Problem. 
fgets liest ja nen char ein. Nun müsste ich ja ne Typkonvertierung von char->float machen. Hast du dafür ne Lösung?


----------



## Mark Leutloff (13. Mai 2004)

Such mal nach der Funktion stdtod(...) das müsste das sein  was du suchst.


----------



## Trendy Andy (13. Mai 2004)

Die Funktion gibt es nicht. Schreibfehler?


----------



## Gawayn (13. Mai 2004)

fgets() liest keinen char ein, sondern eine variable Anzahl von chars, also ein char[] (sprich: char-Array). Wieviele Zeichen von fgets() eingelesen werden, hängt einerseits vom count-Parameter ab (in meinem Beispiel das sizeof), und andererseits von der Länge der aktuellen Zeile in der Datei.

Wenn du mit fgets() eine Zeile in einen String eingelesen hast, musst du ihn zu einem float (oder einem double) konvertieren. (MERKE: Wir sprechen hier von Textdateien, denn nur in Textdateien macht fgets() Sinn. Bei Binärdateien wäre die Vorgehensweise eine andere.) Es gibt zwei Funktionen, die den Job für dich übernehmen: atof() und strtod().


```
double atof( const char *string ) ;
double strtod( const char *nptr, char **endptr ) ;
```

Wie diese beiden Funktionen arbeiten, müsste in deiner Hilfe stehen. Ansonsten bei Problemen: fragen.

Gawayn


----------



## frager (13. Mai 2004)

Probier mal das aus:

```
#include <stdio.h>

int main(int arvc, char **argv)
{
	FILE *pf;
	float f = 0;

	pf = fopen("test.txt", "r");
	if(!pf) {
		printf("Konnte die Datei nicht öffnen\n");
		return 1;
	}

	while(!feof(pf)) {
		if(fscanf(pf, "%f", &f) == EOF) {
			break;
		} else {
			printf("%f\n", f);
		}
	}

	fclose(pf);
	getchar();
	return 0;
}
```


----------



## Trendy Andy (13. Mai 2004)

Irgendwie klappt beides nicht richtig. Bei fscanf liest er irgendwelche zahlen ein und bei der anderen möglichkeit kommt bei mir nen Fehler (kein Compilierfehler).

Ich poste einfach mal meinen Quelltext:

```
#include <stdlib.h>
#include <stdio.h>		

const int ARRAY_GR = 10;	//Konstante für die Größe des Feldes

FILE *stream;
char *string[10];

void readfile(double *x, int y)
{
	stream=fopen("minmax.txt","r");

	if(!stream)
		printf("Konnte die Datei nicht öffnen\n");

	while( !feof( stream ) )
	{
		for(int i=0;i<y;i++)			
		{	
			fscanf(stream, "%f", &x[i]);
			printf("%4.2f\n", x[i]);
			
			//fgets(*string, 8, stream ) ;
			//x[i] = atof(*string);
		}
		
	}
	fclose(stream);
}

double getmin(double *x, int y)
{
	double dummy =x[0];		
	for(int i=1;i<y;i++)	
		if(dummy>x[i])	
			dummy = x[i];
	return dummy;		
}

double getmid(double *x, int y)
{
	double sum=0;
	for(int i=0;i<y;i++)
		sum=sum+x[i];
	double mid=sum/y;
	return mid;
}

double getmax(double *x, int y)
{
	double dummy =x[0];		
	for(int i=1;i<y;i++)	
		if(dummy<x[i])
			dummy = x[i];
	return dummy;
}

void ausgabe(double *x, int y)
{
	printf("\nAusgabe des Feldes: \n");		
	for(int i=0;i<y;i++)
		printf("%4.2f\n",x[i]);
}

void main()
{
	double feld[ARRAY_GR];				
	readfile(&feld[0], ARRAY_GR);			
	printf("Minimum: %4.2f\n", getmin(&feld[0], ARRAY_GR));
	printf("Maximum: %4.2f\n", getmax(&feld[0], ARRAY_GR));
	printf("Mittelwert: %4.2f\n", getmid(&feld[0], ARRAY_GR));
	ausgabe(&feld[0], ARRAY_GR);					
}
```

Das Programm soll halt Zahlen einlesen und davon dann das Maximum, Minimum und Mittelwert ausgeben. Ihr müsst halt noch ne minmax.txt mit Gleitkommazahlen erstellen.


----------



## Gawayn (13. Mai 2004)

Ich habe das Programm nicht ausprobiert, sondern sage erstmal einige Fehler, die mir auf den ersten Blick ins Auge stechen.

Du definierst:


```
char *string[10];
```

Das ist kein String, sondern ein Array von 10 Zeigern auf Strings. Ein String ist das hier:


```
char *string ;
```

Desweiteren dereferenzierst du die Variable überall, Beispiel:


```
//fgets(*string, 8, stream ) ;
```

Der Ausdruck *string zeigt auf nicht initialisierten Speicher. fgets() kann nicht mit einem Zeiger auf einen String funktionieren, der im Speicher überhaupt nicht existiert. Das bedeutet: Wenn man einen String als char* definiert, ist zu diesem Zeitpunkt noch kein Speicher für diesen String reserviert. Dies geschieht erst bei einer Zuweisung. fgets() aber reserviert nicht selbst Speicher, sondern erwartet einen Zeiger auf einen bereits reservierten Speicherbereich. Übergibt man ihm einen char*, so schreibt fgets() seine Daten an irgendeine zufällige Stelle im Speicher, auf die der char* gerade zeigt, was einen Zugriffsfehler verursacht. Folgendermaßen wäre es korrekt:


```
char string[1024] ;
// blablabla
fgets( string, sizeof( string ), stream ) ;
```

Der fscanf-Befehl wird zwar korrekt angewandt, allerdings ist x ein Zeiger auf einen double, der fscanf-Befehl möchte jedoch gerne einen float. 

Versuch mal, die genannten Fehler da rauszukriegen.

Gawayn


----------



## Trendy Andy (13. Mai 2004)

Danke Gawayn für deine schnelle und ausführliche Hilfe!
Der Fehler lag wirklich an der falschen Deklaration des strings! Die Parameterübergabe bei den Funktionen klappt so wie ichs habe!


----------



## Trendy Andy (16. Mai 2004)

Ich hab mal noch ne Frage: Wie kann ich mein Programm geschickt umändern, damit ich keine Array mit ner speziellen Größe verwenden muss, sondern dass alles variabel ist?


----------



## Gawayn (16. Mai 2004)

Du hast 2 Möglichkeiten:  a) Du verwendest eine verkettete Liste, oder b) du stellst als erstes die Größe der Datei fest und allokierst dann soviel Speicher, wie du brauchst.

Eine verkettete Liste ist z.B. folgende Struktur:


```
struct ENTRY {
  int data ;
  ENTRY *next ;
} ;
```

Du siehst, die Struktur ENTRY enthält einen Zeiger, der auf eine weitere ENTRY-Struktur zeigt. Indem man nun eine solche Struktur dynamisch allokiert und sich deren Adresse merkt, kann man nun anhand der next-Zeiger immer mehr solcher Strukturen erzeugen. So entsteht eine Liste, deren Daten im Speicher über die next-Zeiger verkettet sind.

Einfacher wäre es, wenn du am Beginn des Programms die Größe der Datei feststellst und dann einen Speicherbereich allokierst, der groß genug ist, die Daten aufzunehmen. Diesen Speicherbereich sprichst du anschließend als Array an.

Gawayn


----------



## Trendy Andy (16. Mai 2004)

zu a)
Das mit der verketteten Struktur hat mir schon ein Kumpel geraten! Kannst du mir ein kurzes Beispiel geben? Ich blick da noch nicht so ganz durch.

zu b)
Die Größe der Datei hab ich schon ausgelesen. Ich müsste doch dann  einfach noch mit malloc Speicher allocieren und damit die Feldgröße bestimmen, oder?

PS: Die Lösung mit der verketteten Struktur wäre mir lieber.


----------



## Gawayn (16. Mai 2004)

Wenn du ein Array allokieren willst, musst du die Anzahl der Zeilen in der Textdatei ermitteln, weil ja in jeder Zeile genau eine Zahl steht. Das ergibt dann die Größe deines Arrays. Dazu muss man nicht malloc nehmen, der new-Operator tuts auch:

```
float *a ;
a = new float[ANZAHL] ;
a[0] = 1.9 ;
// und so weiter
```
Eine verkettete Liste kann man nicht "kurz" programmieren, da diese Struktur schon etwas mehr Hirnarbeit erfordert, will heißen, da kommt etwas mehr Code zusammen. Schließlich muss die Liste "verwaltet" werden. Es genügt nicht, der Liste Einträge hinzuzufügen. Man muss sie auch abrufen (suchen) und löschen können. Deswegen wird der Code länger. Der folgende Code erstellt drei Einträge. Der Wert NULL im next-Zeiger markiert das Ende der Liste.

```
ENTRY *start = new ENTRY ;

start->data = blablabla ;
start->next = new ENTRY ;
start->next->data = blublublu ;
start->next->next = new ENTRY ;
start->next->next->data = blobloblo ;
start->next->next->next = NULL ;
```
Um das Ganze nun dynamisch im Programm zu verwalten, am besten noch in einer eigenen Klasse, musst du mit einem Laufzeiger, dem du am Anfang den Wert von start zuweist, durch die Liste rutschen. So kannst du Einträge suchen oder löschen. Mach was draus 

Gawayn


----------



## Trendy Andy (16. Mai 2004)

Dank dir.

Habs trotzdem erstmal mit dem allokierten Feld gemacht! Ging recht fix!
Das mit der verketteten Liste muss ich mir nochmal anschauen.

MFG

Andy


----------



## Daniel Toplak (17. Mai 2004)

Zum Thema einfach und doppelt verkettete Listen gibt es hier im Forum einige Threads, einfach mal die Suche benutzen.

Gruß Homer


----------



## oglimmer (17. Mai 2004)

Wenn auch C++ geht, verwende ich es immer.

Daher:


```
std::ifstream ifs;
ifs.open("foo.txt");

while ( ! ifs.eof() ) {
	std::string aLine;
	std::getline(ifs, aLine);
	std::istringstream is(aLine);
	double d;
	is >> d;

	// hier ist das double aus der n-ten Zeile fertig...

}

ifs.close();
```


----------



## derSchorsch (27. Mai 2006)

Hi,
das, was hier für strings in double beschrieben wurde, hätte ichgerne für eine selbstdefinierte Klasse:

```
class Vector
{
public:
	Vector(); 																		
	Vector(GLfloat x1,GLfloat y1,GLfloat z1): x(x1),y(y1),z(z1) {}; 
	~Vector();
	 
	
	void SetComp(GLfloat x_, GLfloat y_, GLfloat z_)
    {
		x=x_;
		y=y_; 
		z=z_; 
    }
	GLfloat x, y, z;
};

Vector::Vector(): 
	x(0),y(0),z(0) {}

Vector::~Vector() {}
```

Ich will die Vektoren in einer datei speichern und später auslesen...
Dazu hatte ich folgende Idee: ich speichere die Addressen der Zeiger, die ich mit 

```
Vector** Punkt = new Vector*[zeilen_anzahl];		//Zeiger auf Arrays von Zeigern [auf Vector], auf heap

		for (int i = 0; i< zeilen_anzahl; i++)
			Punkt[i] = new Vector[spalten_anzahl];
```
deklariert habe:

```
for (int i=0; i<=maxSchritt; i++)
			for (int j=0; j<=maxSchritt; j++)
			{
				Punkt[i][j].x = j*15.0/(maxSchritt+1)-2.5; //xWerte berechnen
				Punkt[i][j].z = i*15.0/(maxSchritt+1)-5.0;
				f.write((char*) &Punkt[i][j],  sizeof Punkt[i][j]);
				f.write("\n", 2);
			}
```
Also ich meine zumindest, dass der mit (char*) &Punkt[i][j] das macht.
Ich habe allerdings keine ahnung, wie ich die Vektoren wieder auslese,
am liebsten wär mir eben, dass ich aus der Datei die Addressen des Punktarrays auf dem heap auslese...

mfg Schorsch


----------



## deepthroat (29. Mai 2006)

Hi.

Was bringt es dir denn die Adressen der Zeiger zu speichern? Die tatsächlichen Objekte sind doch dann nicht gespeichert und werden sich auch nicht unbedingt wieder an der gleichen Stelle/Adresse befinden wie vorher falls sie irgendwie anders generiert werden.

Du mußt schon die Objekte selbst speichern. Dazu mußt du den kompletten Zustand der einzelnen Objekte (sprich die 3 GLfloat Membervariablen) speichern und danach wieder auslesen.

Gruß


----------



## KlaDi (16. August 2006)

Hallo,
irgendwie schwirrt mir gerade ziemlich der Kopf.
Ich möchte eine Datei Zeilenweise einlesen und in Variablen speichern.
Aber wo ist denn der Unterschied zwischen fstream, istream und ifstream? Ich hab jetzt schon viele Möglichkeiten gefunden eine Dateil Zeilenweise einzulesen, aber leider funktioniert keine davon bei mir.

Gruß KlaDi.


----------



## deepthroat (16. August 2006)

Hi.





			
				KlaDi hat gesagt.:
			
		

> Hallo,
> irgendwie schwirrt mir gerade ziemlich der Kopf.
> Ich möchte eine Datei Zeilenweise einlesen und in Variablen speichern.
> Aber wo ist denn der Unterschied zwischen fstream, istream und ifstream?


fstream ist ein Datei-Stream, istream ein Eingabe-Stream und ifstream ist ein Eingabe-Datei-Stream.


			
				KlaDi hat gesagt.:
			
		

> Ich hab jetzt schon viele Möglichkeiten gefunden eine Dateil Zeilenweise einzulesen, aber leider funktioniert keine davon bei mir.


Welche Programmiersprache verwendest du denn?

Gruß


----------



## KlaDi (16. August 2006)

Ah, da erschließt ja schonmal ein wenig.
Also ich verwende C++ und hab nen Borland-Builder zur Verfügung.

Gruß KlaDi.


----------



## deepthroat (16. August 2006)

Aha.

Die Standardmethode um in C++ Dateien zeilenweise einzulesen ist folgende:
	
	
	



```
#include <fstream>
#include <string>

using namespace std;
...
string zeile;
ifstream datei("c:/temp/xyz.txt");

while (getline(datei, zeile)) {
  ...
}
```
Übrigens, wenn etwas nicht funktioniert, dann solltest du dazuschreiben warum es nicht funktioniert. (Fehlermeldungen, Ausgaben, deinen Programmtext) Hellsehen ist immer ein bißchen schlecht.

Gruß


----------



## KlaDi (16. August 2006)

Ja, hast ja recht, bin halt gerade irgendwie komplett verwirrt...Sorry.
Also ich hab jetzt mal Deinen Code eingegeben.
Da bekomme ich den Fehler "Could not find a march for 'std::getline(ifstream,std::basic_string<char,std::string_char_traits<char>,std::allocator<char>>)'".
Der Fehler tritt in der Zeile mit dem while auf.


----------



## KlaDi (17. August 2006)

Kann keiner was mit dem Fehler anfangen?
Bin ich vielleicht der einzige der den hat? Oder liegt das vielleicht am Borland Compiler?


----------



## deepthroat (17. August 2006)

Welche Version vom Borland Builder hast du denn? Evtl. ist der zu alt?

Die getline(istream, string) Funktion sollte mit dem <string> Header verfügbar sein.

Gruß

/edit: Hab grad bei mir im Borland BCC55 Verzeichnis nachgeschaut und es scheint, das Borland die Funktion nicht implementiert. Allerdings implementieren sie die getline(istream, string, char) Funktion. Dann mußt du halt die while Bedingung oben ändern in:
	
	
	



```
while(getline(datei, zeile, '\n')) {
```


----------



## KlaDi (17. August 2006)

Ich hab die Version 5.01.
Ich hab mal selbst noch nen bisschen was probiert und wenn ich es so mache:

```
#include<string>
#include<conio.h>
#include<stdio.h>
#include<fstream>

using namespace std;

void main()
{
     char* buffer;
     string word;
     FILE *f = fopen("D:\\testdatei.txt", "r");
     while(!feof(f))
     {
          word = fgets(buffer, sizeof(buffer), f);
          cout << word << endl;
     }
     fclose(f);
     getch()
}
```
dann bekomme ich den Inhalt meiner Datei, aber leider immer nur 3 Zeichen am Stück. hab schon versucht die irgendwie zusammenzusetzen, aber leider nicht hinbekommen.

Dank und Gruß KlaDi.


----------



## deepthroat (17. August 2006)

Naja, 5.01 ist ja schon ziemlich angestaubt. Das unterstützt mit Sicherheit nicht den C++ Standard. Funktioniert denn das getline mit den 3 Parametern nicht?

Gruß

/edit: Dein Code ist übrigens sehr fehlerhaft.


----------



## KlaDi (17. August 2006)

Doch das funktioniert, hatte nur schon geantwortet, als Du noch nicht editiert hattest.
Aber eine Frage habe ich noch, was muss ich denn machen um eine sinnvolle Ausgabe in die Konsole zu bekommen?
wenn ich:

```
cout << getline(datei, zeile, '\n');
```

mache, dann bekomme ich Ausgaben wie: 0x0012ff44, das sind glaube ich Speicheradressen oder liege ich da gerade völlig auf dem Holzweg?


----------



## deepthroat (17. August 2006)

Die Zeile wird natürlich in die Variable mit dem Namen "zeile" eingelesen. Also:
	
	
	



```
cout << zeile << endl;
```
Gruß

/edit: Das getline muß in der while Bedingung stehenbleiben, denn es wird da der Dateistream selbst zurückgegeben und dann implizit in einen boolschen Wert umgewandelt der angibt ob die Operation erfolgreich war.


----------



## KlaDi (17. August 2006)

Natürlich, jetzt wo du es sagst....Ich glaube ich sollte mich doch nochmal intensiver mit C++ auseinandersetzen...

Danke!


----------

