fread ==> Array der Zeilen



  • Hallo Leute,

    ich muss eine sehr große Textdatei einlesen und verarbeiten. Ich hatte mir das wie folgt gedacht:

    Erstmal das Textfile in den Speicher laden, um dann schnell darauf zugreifen zu können (Funktioniert wurnderbar):

    char* GetMeshBuffer(char* FileName, unsigned long* FileSize){
    	unsigned long i = 0, j = 0;
    	char* Dummy = NULL;
    	struct stat FileStatus;
    	FILE* MeshFile = NULL;
    	unsigned long fFileSize = 0;
    
    	/*Getting size of meshfile*/
    	stat(FileName, &FileStatus);
    	fFileSize = FileStatus.st_size;
    
    	/*Allocating memory for meshfile - one more, because of access in
              in function SplitMeshBufferToLines*/
    	Dummy = (char*)malloc((fFileSize + 1) * sizeof(char));
    
    	if (Dummy == NULL){
    		printf("ERROR\nUnable to allocate memory for meshfile\n\n");
    		return 1;
    	}
    
    	MeshFile = fopen(FileName, "r");
    
    	if (MeshFile == NULL){
    		printf("ERROR\nUnable to open %s for reading\n\n", FileName);
    		return 1;
    	}
    
    	/*Copying meshfile to local memory*/
    	fread(Dummy, fFileSize, 1, MeshFile);
    
    	/*Closing mesh file*/
    	fclose(MeshFile);
    
    	*FileSize = fFileSize;
    	return Dummy;
    }
    

    Anschließend zähle ich die Zeilenumbrüche um die Anzahl der Zeilen zu bestimmen (Funktioniert):

    unsigned long NumberBufferLines(char* MeshBuffer, unsigned long FileSize){
    	unsigned long i = 0, j = 0, k = 0;
    
    	for (i = 0; i < FileSize; i++){
    		if (MeshBuffer[i] == 10){
    			j++;
    		}
    	}
    
    	return ++j;
    }
    

    Jetzt möchte ich alle Zeilenumbrücke (ASCII Code Nummer = 10) durch das Ende eines Strings ersetzen, also durch \0 und gleichzeitig einen Zeiger auf den Zeilenanfang setzen (Funktioniert auch):

    char** SplitMeshBufferToLines(char* MeshBuffer, unsigned long FileSize, unsigned long nLine){
    	unsigned long i = 0, j = 0, k = 0;
    	char* LinePointer = MeshBuffer;
    	char** BufferLines = NULL;
    
    	BufferLines = (char**)malloc(nLine*sizeof(char*));
    
    	for (i = 0; i < FileSize; i++){
    		if (MeshBuffer[i] == 10){
    			MeshBuffer[i] = '\0';
    			BufferLines[j++] = LinePointer;
    			LinePointer = &MeshBuffer[i + 1];
    		}
    	}
            MeshBuffer[i + 1] = '\0';
    
    	return BufferLines;
    }
    

    Wenn ich jetzt auf das String Array zugreife funktioniert das nur bis nLine-1

    Also, folgendes funktioniert:

    for (i = 0; i < nLine - 1; i++){printf("%lu: %s\n", i, LineBuffer[i]);}
    

    Das funktioniert nicht, weil der letzte Zugriff eine Fehler beim Lesen an der letzten Adresse verursacht:

    for (i = 0; i < nLine; i++){printf("%lu: %s\n", i, LineBuffer[i]);}
    

    Der Code steigt bei i = 75259 aus, aber nLine ist 75261 - also der letzte Output lautet:

    (75259 of 75261): Text....

    Ich weiß, dass in C ein Array beim Index 0 beginnt und ich kann den Code zum Laufen bringen, indem ich in der for-Schleife einfach bis nLine-1 zähle, denn dort ist der letzte Eintrag... aber ich will das nicht machen ohne zu wissen, warum das so ist!
    Selbst wenn die letzte Zeile bei 75259 liegt - das char**-Array muss doch bis zum Index 75260 funktionieren?!?



  • - nur Deppen benutzen den malloc-Cast
    - ich bezweifle, dass es sehr große Dateien sind (malloc würde scheitern)
    - verwende "rb" statt "r"
    - prüfe bei stat
    - prüfe bei fread
    - ersetze 10 durch '\n'
    - separiere unterschiedliche Aufgabe in unterschiedliche Funktionen (malloc hat nichts mit fread u.ä. zu tun)
    - was du verwendest ist kein String-Array

    • MeshBuffer[i + 1] = '\0' ist falsch und muss hier MeshBuffer[i] = '\0' heißen


  • char* GetMeshBuffer(char* FileName, unsigned long* FileSize){
        // ...
        if (Dummy == NULL){
            printf("ERROR\nUnable to allocate memory for meshfile\n\n");
            return 1; // <-- das passt nicht zum rueckgabetyp
        }
    


  • Wutz schrieb:

    - nur Deppen benutzen den malloc-Cast

    Danke...

    Wutz schrieb:

    - ich bezweifle, dass es sehr große Dateien sind (malloc würde scheitern)
    - verwende "rb" statt "r"
    - prüfe bei stat
    - prüfe bei fread
    - ersetze 10 durch '\n'
    - separiere unterschiedliche Aufgabe in unterschiedliche Funktionen (malloc hat nichts mit fread u.ä. zu tun)
    - was du verwendest ist kein String-Array

    • MeshBuffer[i + 1] = '\0' ist falsch und muss hier MeshBuffer[i] = '\0' heißen

    Warum sollte malloc bei grossen daten scheitern? Es geht um dateien > 1Gb. Malloc reserviert doch a.d. Heap.

    Ich kann gerne alles prüfen, aber der code funktioniert ja - nur weis ich nicht, wieso er bei i=nLine-1 probleme hat - ihr scheinbar auch nicht. Warum "rb" - es handelt sich um ein ASCII-File



  • Naivling.
    Große Dateien sind größer als der verfügbare Hauptspeicher und bei 32Bit scheitert malloc schon bei >4GB.
    Dein Verfahren "lade alles in den Hauptspeicher" ist prinzipiell suboptimal, lasse dein Programm doch mal mit einer Datei >4TB laufen.
    stat liefert die Binärgröße der Datei, dann im Textmodus weiterzumachen, ist Anfänger-Müll.

    MeshBuffer[i + 1] = '\0'
    

    ist UB und sichert nicht das Ende der Datei ab, wie du es beabsichtigst.



  • Wutz schrieb:

    Naivling.
    Große Dateien sind größer als der verfügbare Hauptspeicher und bei 32Bit scheitert malloc schon bei >4GB.
    Dein Verfahren "lade alles in den Hauptspeicher" ist prinzipiell suboptimal, lasse dein Programm doch mal mit einer Datei >4TB laufen.
    stat liefert die Binärgröße der Datei, dann im Textmodus weiterzumachen, ist Anfänger-Müll.

    MeshBuffer[i + 1] = '\0'
    

    ist UB und sichert nicht das Ende der Datei ab, wie du es beabsichtigst.

    Du wirst ja immer freudlicher...

    Malloc versagt bei über 4gb? Kein problem, kann man prüfen und mehrfach mit malloc den speicher reservieren.

    4tb? So gross sind die dateien auch nicht. Meine platte hat gerade 2tb.

    Aber ok, werde den ansatz überdenken. Ich will eben vermeiden, die datei zweimal zu loopen, weil der plattenzugriff doch deutlich langsamer ist...

    Zum thema "größer als der hauptspeicher": eine datei mit 1gb liefert ein gleichungssystem, welches später an die 20 gb hauptspeicher benötigen wird. Also, das ist kein problem. Die datei enthält nämlich ein gitternetz auf welchem dann partielle Differentialgleichungen diskretisiert werden.



  • Wenn malloc wegen Hauptspeichermangel scheitert, nützt dir auch dein mehrfaches Aufrufen nichts, du begreifst es einfach nicht.
    Bei >4GB kann auch stat scheitern, auch das hast du nicht berücksichtigt.



  • Wutz schrieb:

    Wenn malloc wegen Hauptspeichermangel scheitert, nützt dir auch dein mehrfaches Aufrufen nichts, du begreifst es einfach nicht.
    Bei >4GB kann auch stat scheitern, auch das hast du nicht berücksichtigt.

    Achso, das weis ich, dass eine anwendung in 32 bit max 4gb allocieren kann - das ist aber keine limitierung von malloc, sondern vom betriebssystem, oder?.
    Es geht hier um ein wenig hpc computing - die datei soll möglichst schnell verarbeitet werden. Das programm wird auf einem 64 bit system mit 128gb arbeitsspeicher ausgeführt.



  • Wutz schrieb:

    MeshBuffer[i + 1] = '\0'
    

    ist UB und sichert nicht das Ende der Datei ab, wie du es beabsichtigst.

    Soll es ja nicht. Ich lese die komplette datei in den speicher. Dann gehe ich die datei durch und ersetze alle zeilenumbrüche durch \0 (das ende eines strings), und merke mir die adresse des letzten adressfeldes mit \0 + 1 Damit kann ich dann als string auf die einzelnen zeilen zugreifen.


Log in to reply