# [C] Zahlen aus Datei lesen



## n1smo (17. September 2007)

Hi, es gibt zwar schon etliche Beiträge zudem Thema hab aber leider nichts passendes gefunden was mir weiterhelfen könnte.

Ich hab folgendes Problem.
Ich hab ne Datei in der Zahlen wie folgt drin stehen könnten, als Beispiel:

2,394,34,95553,233949586
29,595,4353
3450,534,2,4,67,6,7,8,9

Also alles variabel, die Zahlen selber, die Zahlen pro Zeile als auch die Anzahl der Zeilen.

Ich such jetzt einen gescheiten weg diese Zahlen einzulesen.
Das Programm soll alle Zahlen durchgehen und die grösste in einer Variabel speichern.

Der einzige Weg der mir einfällt dies zu machen ist sich jedes Zeichen einzeln per fgetc zu holen, die vorher gespeicherte Zahl mit 10 zu multiplizieren und die aktuell eingelesene zu addieren. So ungefähr:

int nummer, c;
while( (c=fgetc(datei)) != EOF)
if (c>='0' && c<='9'){
nummer=nummer*10 + c;
}
Das ganze wird halt immer so lange gemacht bis ein komma bzw new line zeichen kommt.

Ist natürlich alles andere als elegant die Methode, daher wollt ich mich mal informieren wie eine bessere implementierung aussehen könnte.


----------



## MCoder (17. September 2007)

Hallo,

nach deinem Beispiel sieht es so aus, dass in der Datei Integer-Zahlen stehen, die mit Komma getrennt sind.
Da könntest du folgendes machen:
- Zeile aus Datei einlesen
- mit "strtok" die Zeile zerlegen, so dass man die einzelnen Strings mit den Zahlen erhält
- mit "atoi" den String in einen Integer konvertieren

Gruß
MCoder


----------



## n1smo (17. September 2007)

Hab noch nie mit strtok gearbeitet, das könnte evtl das Problem des einlesens lösen, müsste ich mir mal genau angucken. Werd das wenn ich Zeit finde mal probieren, sollte jemand irgendwelche Code schnipsel haben wäre ich auch dankbar.

Danke erstmal


----------



## n1smo (18. September 2007)

Also hat alles gut geklappt, danke, nun hab ich aber noch eine Frage wenn ich die Zeilen mit fgets einlese, muss ich ja eine maximale zeilenlänge angeben. Kann ich das irgendwie umgehen, so dass die Zeile beliebig lang sein darf?


----------



## MCoder (18. September 2007)

Hallo,

die Funktion "fgets()" liest bis einschließlich des Zeilenumbruch-Zeichens oder eben nur soviel, wie der bereitgestellte Speicher hergibt. Du könntest also schauen, ob das letzte eingelesene Zeichen ein Zeilenumbruch ist. Wenn ja, hast du die Zeile komplett eingelesen, ansonsten muss du solange weiter einlesen, bis der Zeilenumbruch kommt und dir dann die Zeile zusammenstückeln.

Muss es eigentlich unbedingt C sein? Mit C++ wäre es nämlich einfacher.

Gruß
MCoder


----------



## n1smo (18. September 2007)

Jo muss leider C sein, und wie genau prüfe ich das letzte eingelesene Zeichen?
Und nehmen wir mal an der Speicher ist voll aber das war noch nicht das letzte Zeichen, was kann man dann machen?


----------



## MCoder (19. September 2007)

n1smo hat gesagt.:


> Jo muss leider C sein, und wie genau prüfe ich das letzte eingelesene Zeichen?


Einfach testen, ob das letzte Zeichen ein '\n' ist:

```
char buffer[] = "abcdefg\n";

if( buffer[strlen(buffer) - 1] == '\n' )
{
    // Zeilenende erkannt
}
else
{
    // ...
}
```



n1smo hat gesagt.:


> Und nehmen wir mal an der Speicher ist voll aber das war noch nicht das letzte Zeichen, was kann man dann machen?


Genau das ist die Stelle, wo es bei C etwas umständlich wird. Du musst mit dynamisch alloziertem Speicher arbeiten, den du dem Bedarf entsprechend anpassen musst (Verwendung von malloc, realloc und Co.).

Gruß
MCoder


----------



## n1smo (19. September 2007)

Alles klar, danke
Hab das schon befürchtet, naja mal schauen wie ich das regel


----------



## MCoder (19. September 2007)

So wie dich verstanden habe, kommt es ja nicht so sehr darauf an, die Daten zeilenweise zu verabeiten, sondern wirklich nur die Zahlen aus der gesamten Datei herauszuziehen.
Falls das letzte Zeichen ein Zeilenumbruch oder ein Komma ist, ist die Sache erledigt. Ansonsten musst du dir nur die letzte (Teil)Zahl merken und die erste (Teil)Zahl des nächsten fgets()-Aufrufes da dranhängen.
Für diesen Fall entsprechend (statischen) Speicher vorzuhalten, sollte nicht das Problem sein.

Gruß
MCoder


----------



## n1smo (21. September 2007)

Ja sry, das muss ich noch berichtigen ich dachte auch dass ich erst alles auslesen muss, aber hat sich herausgestellt dass ich doch jede Zeile einzeln einlesen muss, und sie dann erstmal zu verarbeiten.
Im moment steig ich aber echt nicht dahinter wie ich das genau mit der Speicherverwaltung machen soll damit er bei zu langen Zahlen keine Fehler ausspuckt, theoretisch könnte ich einfach die max laenge vom puffer enorm hoch stellen, aber kein plan wie sich das auf die performance ausschlägt, bestimmt nicht so gut...

Zudem ist mir noch eine Frage aufgekommen:

Wie kann ich die gleiche Zeile per strtok 2-mal durchlaufen ohne das Puffer Array vorher kopieren zu müssen, quasi den Lesezeiger wieder an den Anfang setzen.

Hier mal ein Beispiel dazu:
char* Token
char puffer[ZEILENLAENGE];

while(fgets(puffer, ZEILENLAENGE, datei)){

			Token = strtok(puffer, ",\n");

			while(Token != NULL) {

			// mach irgendwas...

				Token = strtok(NULL, ",\n");
			}

// jetzt soll die gleiche Zeile nochmal durchlaufen werden

			Token = strtok(puffer, ",\n");

			while(Token != NULL) {

			// mach irgendwas...

				Token = strtok(NULL, ",\n");
			}

// funktioniert so aber nicht...

}

So funktionierts leider nicht, da dann direkt am ende weitermacht wird wo er vorher aufgehört hat also quasi bei NULL...
Im moment umgehe ich das Problem indem ich dem Puffer vorher kopiere und den zweiten durchlauf dann mit dem kopierten Puffer starte, aber das geht doch bestimmt auch besser.


----------



## derm35 (21. September 2007)

hi!
habe im Moment ein ähnl. Problem.
Ich möchte Zahlen aus einer Datei einlesen, die je durch ein return zeichen getrennt sind.
Habe jedoch probleme den richtigen Variablen Typ für mich zu finden.
Es sind Zahlen mit dem Format xxxx.xxxx
Die eingelesenen Zahlen müssten gespeichert werden (matrix?) und dann ALLE möglichen 2er Pärchen der Zahlen nacheinander in mein Unterprogramm geschickt werden.

Kann mir da wer helfen?
Das ganze ist etwas zu komplex für mich...
Danke!

mfG


----------



## MCoder (21. September 2007)

n1smo hat gesagt.:


> Im moment steig ich aber echt nicht dahinter wie ich das genau mit der Speicherverwaltung machen soll damit er bei zu langen Zahlen keine Fehler ausspuckt


Kurz vor dem Wochenende mal eine, zugegeben nicht sehr elegante, aber dafür einfache Methode.
Du ermittelst einfach die Länge der gesamten Datei und reservierst den Speicher für diese Länge. Dann ist garantiert, dass jede Zeile hereinpasst:  

```
#include <sys\stat.h>

// ...

FILE *fp = fopen("c:\\test.txt", "r");

if( fp )
{
    struct stat buf;

    if( fstat(fileno(fp), &buf) == 0  )
    {
        char *pBuffer = (char *)malloc(buf.st_size + 1);

        // ... zeilenweise einlesen und verarbeiten

        free(pBuffer);
    }
}
```



n1smo hat gesagt.:


> Wie kann ich die gleiche Zeile per strtok 2-mal durchlaufen ohne das Puffer Array vorher kopieren zu müssen, quasi den Lesezeiger wieder an den Anfang setzen.


Nimm nicht den Lesepuffer, sondern arbeite mit einem Zeiger darauf, den strtok() dann beliebig verstellen kann:

```
char* Token
char puffer[ZEILENLAENGE];

while(fgets(puffer, ZEILENLAENGE, datei))
{
    char *pBufferPtr = puffer;
    Token = strtok(pBufferPtr, ",\n");

    // ...
            
}
```

@derm35    
Wenn jede Zahl in einer Zeile steht, ist das nicht so problematisch.
- Zeilenweise lesen.
- Das Format schaut aus wie ein Float- oder Double-Wert, daher mit "atof()" konvertieren.
- Ich hoffe, du bist nicht auf C beschränkt, dann könntest du nämlich die Zahlen in einem Objekt der Klasse "vector" speichern.

Was mit den "möglichen 2er Pärchen" gemeint ist, weiss ich allerdings nicht.

Gruß
MCoder


----------



## derm35 (21. September 2007)

Vielen Dank schonmal!
Ich schreibe das ganze in c (visual studio).
Könntest du mir evtl. konkrete Befehle zum auslesen etc. geben?
Mit den Pärchen meine ich, dass das Prog. von allen eingelesenen Zahlen jeweils zwei herausnimmt und an das unterprogramm weitergibt. Dabei sollen alle mögl. Kombinationen der eingelesenen Zahlen nacheinander weitergegeben werden.

Danke!


----------



## n1smo (21. September 2007)

Da ich von dem ersten Teil eigentlich kaum was verstehe ausser dass dort die gesamte länge der Datei ausgelesen wird und dann dementsprechend viel Speicher reserviert bin versteh ich davon nur Bahnhof :>

Wie zB sorge ich dann dafür dass dann auch nur in diesem reservierten Speicher gearbeitet wird? Wie würde dass zB aussehen wenn man mein oben beschriebenes Beispiel in dein Code einfügen würde? Mein Wissen über dynamische Speicherverwaltung und Zeiger ist leider nocht recht beschränkt 

Und das mit strtok, krieg ich auch irgendwie nicht hin.
Ich habs unter anderem hiermit versucht, aber das klappt leider alles nicht 


```
Token = strtok(pBufferPtr, ",\n");
			while(Token != NULL) {
                       // mach etwas...
                *(pBufferPtr++);                         
				Token = strtok(NULL, ",\n");
			}
```


----------



## n1smo (21. September 2007)

```
char* Token
char puffer[ZEILENLAENGE];

while(fgets(puffer, ZEILENLAENGE, datei))
{
    char *pBufferPtr = puffer;
    Token = strtok(pBufferPtr, ",\n");

    // ...
            
}
```


Keiner eine Idee wie das genau funktionieren soll? das wäre echt wichtig für mich das zu wissen :


----------



## MCoder (21. September 2007)

@n1smo
bei deinem ersten Problem würde die Zeile "char *pBuffer = (char *)malloc(buf.st_size + 1);" deiner Zeile "char puffer[ZEILENLAENGE];" entsprechen und du fügst dann einfach deine Schleife mit "fgets()" anstelle des Kommentares ein.

Habe mir gerade noch mal die Beschreibung zu "strtok()" angesehen, Meine Idee mit der Zeigervariable taugt leider nix, weil diese Funktion den Inhalt des Puffers ändert. Sie fügt anstelle des Trennzeichens ein Nullzeichen (Stringende) ein. Um das Kopieren des Puffers kommst du daher nicht herum.

@derm35
Stöbere einfach mal im Forum: Das Lesen von Daten aus Dateien wird hier ziemlich häufig behandelt. Auch aus diesem Thread kannst du zumindest schon mal das Auslesen (Funktionen: fopen() und fgets() ) herausziehen. 
Bei dem Pärchenproblem solltest du mal versuchen, dir einen Algorithmus dafür zu überlegen, erstmal unabhängig von der konkreten Implementierung (Programmiersprache).

Gruß
MCoder


----------



## n1smo (21. September 2007)

Danke nochmal für die Hilfe, aber das ändert doch nichts daran dass ich nach wie vor eine feste maximale Zeilenlänge definieren muss


----------



## Drache2 (22. September 2007)

Sag dem doch enfach, dass wenn (z.b. die #) kommt, ist eine Zeile zuende und danach kommt ein zeichen mit dem charcode 10(Absatz). Danach beginnt die neue Zeile.


----------



## n1smo (23. September 2007)

Danke nochmal für eure Hilfe leute, hat sich jetzt erledigt


----------

