2D char array befüllen (ziemlicher Anfänger)
-
Guten Abend,
ich habe ein Problem mit einem 2D char array. Folgender Code:
int ReadMeshFile(char * filename, TQuad **ppQuad, TTri **ppTri, TBar **ppBar, TNode **ppNode, TFamily **ppFamily, unsigned long * nQuad, unsigned long * nTri, unsigned long * nBar, unsigned long * nNode, unsigned long * nFamily, unsigned int * nBarFamily, unsigned int * nShellFamily){ //Setting internal variables unsigned int N = 81, fc=0, FN=1; unsigned long FileSize, nLines, fi=0, fj=0, maxNodeNumber=0, AllocatedRAM=0, fnQuad=0, fnTri=0, fnBar=0, fnNode=0, fnBarFamily=0, fnShellFamily=0, * fpNodeNumberExtInt, NodeCounter=0; unsigned long OldQuadMaxNumber = 0, OldQuadMinNumber = 4e9, OldBarMaxNumber = 0, OldBarMinNumber = 4e9, OldTriMaxNumber = 0, OldTriMinNumber = 4e9; char **Puffer, line[N], StrNr[N]; double xmin=0, xmax=0, ymin=0, ymax=0, zmin=0, zmax=0; //Setting internal userdefined variables TNode * fpNode, * fpNodeStart, gNode; TQuad * fpQuad, * fpQuadStart, gQuad; TTri * fpTri, * fpTriStart, gTri; TBar * fpBar, * fpBarStart, gBar; TFamily * fpBarFamily, * fpShellFamily, * fpBarFamilyStart, * fpShellFamilyStart, gFamily; //Checking, if file exists if (FileExist(filename)!=0){ printf("Unable to open file %s...\nPlease press any key...\n\n", filename); WaitForUserInput; return 1; } struct stat FileStatus; //Getting Size of Meshfile stat(filename, &FileStatus); nLines = FileStatus.st_size/(81*sizeof(char)); printf("Size of %s: %i byte.\n", filename, FileStatus.st_size); printf("Expect %i lines\n\n", nLines); //Allocating space to save meshfile in RAM printf("Temporary allocating %3.3f kByte for character\n\n", ByteDivisor*81*nLines*sizeof(char)); Puffer = (char **)calloc(nLines, sizeof(char *)); do{ Puffer[fi] = (char *)calloc(N, sizeof(char)); }while(fi++<nLines); fi=0; AllocatedRAM += nLines*sizeof(char[81]); FILE *Mf = fopen(filename, "r"); //Reading file while(fgets(line, N, Mf) != NULL){ //Inhalt wird korrekt ausgegeben! Puffer[fj] = line; printf("%s\n", Puffer[fj++]); if (strncmp(line, "GRID",4)==0) { fnNode++; for (fi=9; fi<=15;fi++) { StrNr[fc++] = line[fi]; } fc=0; if (atoi(StrNr) > maxNodeNumber){maxNodeNumber = atoi(StrNr);} } if (strncmp(line, "CTRIA3",6)==0) {fnTri++;} if (strncmp(line, "CQUAD",5)==0) {fnQuad++;} if (strncmp(line, "CBAR",4)==0) {fnBar++;} if (strncmp(line, "$ Bar element data for family ", 39)==0) {fnBarFamily++;} if (strncmp(line, "$ Shell element data for family ", 41)==0) {fnShellFamily++;} } fi=0; fj=0; printf("Allocating space for mesh:\n"); if (fnNode>0){ printf(" - %3.3f kByte for %lu nodes\n", ByteDivisor*fnNode*sizeof(TNode), fnNode); AllocatedRAM += fnNode*sizeof(TNode); AllocatedRAM += maxNodeNumber*sizeof(unsigned long); fpNode = (TNode *)calloc(fnNode, sizeof(TNode)); fpNodeStart = fpNode; fpNodeNumberExtInt = (unsigned long *)calloc(maxNodeNumber, sizeof(unsigned long)); } if (fnQuad>0){ printf(" - %3.3f kByte for %lu quad elements\n", ByteDivisor*fnQuad*sizeof(TQuad), fnQuad); AllocatedRAM += fnQuad*sizeof(TQuad); fpQuad = (TQuad *)calloc(fnQuad, sizeof(TQuad)); fpQuadStart = fpQuad; } if (fnTri>0){ printf(" - %3.3f kByte for %lu triangle elements\n", ByteDivisor*fnTri*sizeof(TTri), fnTri); AllocatedRAM += fnTri*sizeof(TTri); fpTri = (TTri *)calloc(fnTri, sizeof(TTri)); fpTriStart = fpTri; } if (fnBar>0){ printf(" - %3.3f kByte for %lu bar elements\n", ByteDivisor*fnBar*sizeof(TBar), fnBar); AllocatedRAM += fnBar*sizeof(TBar); fpBar = (TBar *)calloc(fnBar, sizeof(TBar)); fpBarStart = fpBar; } if ((fnBarFamily + fnShellFamily)>0){ printf(" - %3.3f kByte for %lu families\n", ByteDivisor*fnBarFamily+fnShellFamily*sizeof(TFamily), (fnBarFamily + fnShellFamily)); AllocatedRAM += (fnBarFamily+fnShellFamily)*sizeof(TFamily); fpBarFamily = (TFamily *)calloc(fnBarFamily, sizeof(TFamily)); fpBarFamilyStart = fpBarFamily; fpShellFamily = (TFamily *)calloc(fnShellFamily, sizeof(TFamily)); fpShellFamilyStart = fpShellFamily; } printf(" Total current allocated RAM: %3.3f kByte\n\n", ByteDivisor*AllocatedRAM); do{ //Index wird ausgegeben - nach "Puffer:" erscheint nichts mehr. printf("Index: %lu, Puffer: %s\n", fi, Puffer[fi]); }while(++fi<nLines);fi=0; fclose(Mf); do{ my_free(Puffer[fi]); }while(++fi<nLines);fi=0; my_free(*Puffer); return 0; }
my_free ist eine Definition, die wie folgt aussieht:
#define my_free(x) free(x); x = NULL //Free memory of pointer
Wirklich interessant ist nur Zeitel 47 bis 51, 57 bis 60 und 118 bis 121.
Wie die Kommentare schon sagen, wird der Inhalt von Puffer in Zeile 60 noch korrekt ausgegeben - in Zeile 120 jedoch ist er leer. Sieht jemand den Fehler?
Vielen Dank für die Hilfe,
CJens.
-
Sorry, ich meinte natürlich, dass die Ausgabe in Zeile 106, nicht 120 nicht funktioniert.
-
Hallo,
ich habe jetzt einen Code gebastelt, der halbwegs funktioniert - wenngleich ich nicht genau weis, wieso:
#include <stdio.h> #include <stdlib.h> int main(){ //Variable declaration char **Puffer, * filename = "abc.txt"; FILE *Mf = fopen(filename, "r"); int NC=81, NR=175, i=0; char line[NC]; Puffer = (char **)calloc(NR, sizeof(char *)); do{ Puffer[i] = (char *)calloc(NC, sizeof(char)); }while(++i<NC); i=0; while(fgets(line, NC, Mf) != NULL){ if (line[0] != '\n'){ if (i>=NR){break;} Puffer[i++] = line; printf("A Index: %i, Puffer: %s\n",i,Puffer[i++]); } } i=0; do{ printf("B Index: %i, Puffer: %s\n",i,Puffer[i]); }while(++i<NR);i=0; system("PAUSE"); return 0; }
Gut, bei der Ausgabe "B Index" wird immer abwechselnd einmal (null) und dann die nächste Zeile korrekte ausgegeben.
Grüße,
CJens
-
...gut, ich konnte mir jetzt erklären, wieso immer einmal (null) und dann einmal der korrekte Eintrag ausgegeben wird - weil ich sowohl bei
Puffer[i++] = line;
also auch bei
printf("A Index: %i, Puffer: %s\n",i,Puffer[i++]);
ein i++ stehen habe. Wenn ich das i++ einfach bei Puffer wegnehme - funktioniert garnichts mehr - die komplette Ausgabe von Zeile 26 ist dann leer...
-
Also, ich denke ich weiß jetzt, wo der Fehler liegt. Und zwar speichere ich ja lediglich den Pointer auf das char-array line. Was in diesem Array liegt, wird jedoch mit jedem Schleifendurchgang überschrieben.
Deshalb funktioniert es bei printf("A ...\n") aber nicht mehr bei printf("B...\n").Hat jemand eine Idee, wie man das lösen kann, ohne dass ich jedes einzelne Zeichen mit einer ineinander geschachtelten Schleife übergebe?
Die Datei kann bis zu 4 GB groß werden und der Code soll schnell laufen - oder macht die Funktion fgets bzw. strcpy nichts anderes?Grüße,
CJens
-
Da ich weiß, dass jede Zeile 81 Einträge hat und ich langsam ins Bett gehen möchte, habe ich mir eine Notlösung zusammengebastelt. Diese anbei:
#include <stdio.h> #include <stdlib.h> int main(){ //Variable declaration char *Puffer, * filename = "abc.txt"; FILE *Mf = fopen(filename, "r"); int NC=81, NR=300, i=0, j=0, Counter=0, Lines=0; char line[NC]; Puffer = (char *)calloc(NR*NC, sizeof(char)); while(fgets(line, NC, Mf) != NULL){ if (line[0] != '\n'){ for (j=0;j<NC;j++){Puffer[Counter++] = line[j];} Lines++; } } Counter=0; do{ do{ printf("%c",Puffer[Counter++]); }while(++i<NC);i=0; printf("\n"); }while(j++<Lines); j=0; system("PAUSE"); return 0; }
Es wäre aber super, wenn jemand dazu einen elegantere Lösung parat hätte - eine, in der ich über den Index durch die Zeilen im Array springen kann. Das ist zwar für meine aktuelle Aufgabe nicht notwendig, aber ich will C ja auch lernen...
Grüße und gute Nacht,
CJens
-
Du hast ja selber gemerkt. dass
char **Puffer, line[N] ... Puffer[fi] = (char *)calloc(N, sizeof(char)); ... fgets(line, N, Mf) Puffer[fj] = line;
nicht funktioniert, da du in der letzten Zeile immer auf deine Eingabezeile line zeigst.
Zudem geht der Verweis auf den dynamischen Speicher verloren und du erzeigst Speicherlecks.Das eine Zuweisung eines Arrays in C so nicht funktioniert steht aber gleich am Anfang des Kapitels über Arrays.
Eine Möglichkeit wäre strcpy zu nutzen:
strcpy(Puffer[fj], line);
Was ja auch in C extra dafür eingebaut wurde.
Oder du liest gleich in deinen Puffer ein, ohne line.
fgets(Puffer[fj], N, Mf)
Das "normale" Verfahren wäre, ein Zeile in line einzulesen (das dann aber durchaus größer sein darf).
Dann ermittelst du den wirklichen Speicherbedarf deiner Eingabe und besorgst entsprechend Speicher.
Dann kopierst du den Inhalt von line nach Puffer[xyz].
-
deine funktion ReadMeshFile besteht aus spaghetticode.
da würd ich noch einiges dran verbessern:
- reservierung des ram auslagern
- benutzer-eingabe auslagern
- datei-einlesen auslagern
- ausgabe auslagern
- fehlerbehandlung einbauen ( was passiert wenn nLines 0 ist, wenn fgets NULL liefert(wenigstens ne meldung anzeigen ), etc)
-
Hallo.
Also, die Routine ist wirklich sehr lang... das gefällt mir auch nicht so gut, hat aber einen Grund:
Ich habe eine Datei, in der X Knoten mit Koordinaten, Y Dreiecke mit Knotenummern und Z Vierecke definiert sind. X,Y und Z liegen typischerweise bei 10 bis 20 Mio.Bevor ich diese einlesen kann muss ich die Datei erst einmal durchkämmen und zählen, wieviele Knoten, Vierecke und Dreiecke die Datei enthält. Dann reserviere ich den Speicher für die Felder. Anschließend muss ich die Datei nochmal durchgehen um die Werte auszulesen. Mein Gedanke war, dass ein Zugriff auf den Hauptspeicher um einiges schneller wäre, als ein Zugriff auf die Datei. Nachdem ich die Datei schon einmal durchgehen muss und jedes Mal den String durch fgets erhalte habe ich mir gedacht, ich kann den doch zwischenspeichern, so dass ich die Datei nicht nochmal durchlaufen muss sondern schneller den Hauptspeicher durchlaufen kann. Wenn dem nicht so ist, kann ich natürlich Unterroutinen definieren, die das alles machen.
Mir geht es hier wirklich um Geschwindigkeit...
-
Hallo Dirk,
vielen Dank. Der Code funktioniert jetzt so:
#include <stdio.h> #include <stdlib.h> #define InputFileName "abc.txt" int main(){ char **Puffer, line[21]; unsigned long nLines=3, N=21, i=0, j=0; FILE * Mf = fopen(InputFileName, "r"); Puffer = (char **)calloc(nLines, sizeof(char *)); do{ Puffer[i] = (char *)calloc(N, sizeof(char)); }while(++i<N);i=0; while(fgets(Puffer[i], N, Mf) != NULL){if (*Puffer[i]!='\n'){i++;}} fclose(Mf); for (j=0;j<i;j++){printf("Zeile %lu: %s\n", j, Puffer[j]);} return 0; }
-
Schau dir mal realloc an.
Du kannst erstmal Speicher für deine Knoten und n-Ecke reservieren*.
Wenn der nicht reicht machst du ein realloc (z.B. mit dem doppelten Speicherplatz).
Ganz am Ende machst du nochmal ein realloc mit den richtigen WertenDann kanns du gleich beim ersten Mal die Daten einlesen und auswerten, ohne den Riesenpuffer
Und verzichte dann auf die meisten printf. Jeder 1000 Wert reicht auch. IO ist langsam
*Evtl. gibt es ja einen Erfahrungswert den du aus der Dateigröße ermitteln kannst.
-
Hi.
Also, das wäre natürlich auch eine Möglichkeit. Dann würde ich also Speicher für 10 000 Knoten, Dreiecke und Vierecke reservieren und wenn diese Zahl überschritten ist, den einfach um weitere 10 000 erweitern.
Die printf's sind ja nur jetzt in der Entwicklungsphase drinnen - die kommen dann raus. 10 Mio. Ausgaben wären auch etwas viel
-
Bei 10-20 Millionen Elementen würde ich gleich mal 1 bis 5 Millionen am Stück nehmen und dann entsprechend erweitern.
Oder du erweiterst immer um die Speichergröße die du gerade hast.
100000 > 200000 > 400000 > 800000 ...Oder du spendierst deinen structs am Anfang noch einen Eintrag
int id;
zur Identifikation welche struct das ist.
Dann kannst du Speicher reservieren (die Anzahl der Zeilen kennst du ja) und dann einfach einlesen und später noch sortieren.
-
CJens schrieb:
Also, die Routine ist wirklich sehr lang... das gefällt mir auch nicht so gut, hat aber einen Grund:
Ich habe eine Datei, in der X Knoten mit Koordinaten, Y Dreiecke mit Knotenummern und Z Vierecke definiert sind. X,Y und Z liegen typischerweise bei 10 bis 20 Mio.das ist kein grund spaghetticode zu fabrizieren.
btw. erzeugst du die datei selbst? dann kannst du die nötigen infos zur reservierung des speichers in der datei unterbringen.wenn das nicht geht und wenn deine datei bis zu 20 mio knoten hat, was solls
dann reserviere speicher für 30 mio und erweitere den speicher wenn nötig zur
laufzeit.