Forum: Mikrocontroller und Digitale Elektronik [C] Textdatei in String speichern


von Freak (Gast)


Lesenswert?

Guten Abend,

ich versuche im Moment ein Programm für die PlayStation Portable zu zu 
schreiben. Dabei möchte ich eine ganze Textdatei, ohne die 
Formatierungszeichen (EOF und Zeilenumbruch) in einen String speichern 
um ihn später weiterverarbeiten zu können.

Leider hängt sich die PlayStation sofort nach dem starten des Programms 
auf. Habt Ihr eine Idee woran es liegen könnte?

Hier mein Code
1
int main() {
2
    
3
   SetupCallbacks();
4
   pspDebugScreenInit();
5
  
6
   int Zeichenanzahl = 0;                            
7
   char Zeichen;
8
   char *Inhalt;
9
  
10
   FILE *tf;
11
   tf = fopen("test.txt","rb");
12
   if(tf==NULL) {
13
                 pspDebugScreenPrintf("Laden der Datei fehlgeschlagen\n"); }
14
                 else{ pspDebugScreenPrintf("Laden der Datei war erfolgreich\n");}
15
                
16
   while(!feof(tf))
17
   //while(Zeichen!=EOF)
18
   {
19
   Zeichen = fgetc(tf);
20
  
21
   if(Zeichen!='\n')
22
        {
23
        Zeichenanzahl++;
24
        Inhalt = (char *) malloc(Zeichenanzahl);
25
        strcat(Inhalt,Zeichen);
26
        }          
27
   }
28
  
29
   fclose(tf);
30
   pspDebugScreenPrintf("Inhalt der Datei lautet:\n\n%s",Inhalt);
31
  
32
   sceKernelSleepThread();
33
   return 0;
34
}

Das gesammte Projekt lässt sich problemlos compilieren. Keine Warnungen 
oder sonstiges. Auf dem Bildschirm wird aber auch keine Ausgabe 
getätigt. Daher vermute ich, das es ein proböem beim Laden der Datei 
gibt. In etlichen vorherigen Projekten gab es aber keine Probleme.

MfG

von test (Gast)


Lesenswert?

>if(tf==NULL) {
>pspDebugScreenPrintf("Laden der Datei fehlgeschlagen\n"); }
Da sollte es dann auch nicht weitergehen, also exit() einfügen oder 
etwas ähnliches, ansonsten knallt es weiter unten.

Auch sehe ich malloc aber kein free, lass das nicht den Karl-Heinz 
sehen!

von Martin (Gast)


Lesenswert?

Du allozierst für jedes Zeichen den ganzen Puffer, jeweils eine Stelle 
mehr.

Wenn da also 100 Zeichen kommen, allozierst du 100+99+98... Stück. Das 
ist auf alle Fälle schonmal Banane.

von Patrick (Gast)


Lesenswert?

Naja, zumindest muss Dein Code ja "Laden [...] erfolgreich" oder "... 
fehlgeschlagen" ausgeben; daran siehst Du ja, ob die Datei erfolgreich 
geöffnet wurde.

Diese zwei Zeilen:
1
Inhalt = (char *) malloc(Zeichenanzahl);
2
strcat(Inhalt,Zeichen);

an der gezeigten Stelle sind natürlich völliger Blödsinn, da nach jedem 
gelesenen Zeichen ein neuer Speicherbereich allokiert wird und das neue 
Zeichen an den Anfang dieses Bereichs kopiert wird.

Ich ahne ungefähr, was Du vorhast; in diesem Falle wäre realloc() 
angebracht.
Effizienter wäre es allerdings, den Speicher nicht byteweise zu 
reallokieren, sondern blockweise (z. B. in 4K-Blöcken).

von Freak (Gast)


Lesenswert?

So, das Problem liegt wie Ihr alle auch schon sagtet, bei
1
Inhalt = (char *) malloc(Zeichenanzahl);

Wenn ich es so mache, läuft es ohne Probleme. Nur habe ich jetzt eine 
feste Größe definiert. Ich möchte es aber dynamisch haben.

1
char Inhalt[5000]; 
2
3
   ...[...]...
4
5
   while(!feof(tf))
6
   //while(Zeichen!=EOF)
7
   {
8
   Zeichen = fgetc(tf);
9
   
10
   if(Zeichen!='\n')
11
        {
12
        Zeichenanzahl++;
13
        Inhalt[Zeichenanzahl]=Zeichen;
14
        }          
15
   }
16
   
17
   fclose(tf);

Also muss ich mir nur noch einmal anschauen, wie man einen String 
dynamisch vergrößern kann. Habe sowas seit Ebigkeiten nicht mehr 
gemacht.
Danke ann alle.

MfG

von Klaus W. (mfgkw)


Lesenswert?

realloc()

von Freak (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> realloc()

Das ist mein Problem. Bei realloc hängt sie sich jedes mal auf. Ich 
schau mir schon verzweifelt die Referenzen an. Irgendwo muss ich was 
übersehen haben.

von Andreas B. (andreasb)


Lesenswert?

Hallo Freak

Statt realloc() kannst du einfach einen genug grossen Speicher am Anfang 
reservieren.

mit fseek() springs du ans ende der Datei, dann fragst du mit ftell() 
die Position ab, somit weisst du wie gross die Datei ist.

Danach reserviertst du size + 1 mit malloc, das ist auf jeden Fall genug 
Speicher.

Danach kannst du Zeichen für Zeichen einlesen (wenn auch nicht besonders 
performant) und schreibst als letztes ein 0 ('\0') in das Array, und 
somit hast du einen String.

Das du zufiel Speicher reserviert hast ist nicht so schlimm, sofern die 
der Speicher nicht ausgeht, du könntest dann mit strlen() und strncpy() 
das in einen neuen Speicher kopieren, der die exakte Grösse hat.

FILE * fp = fopen(...);
fseek(fp, 0, SEEK_END); // ans ende
long size = ftell(fp);
fseek(fp, 0, SEEK_SET); // wider an den Anfang

char * tmp = malloc(size + 1);

// hier einlesen

int len = strlen(tmp);
char * str = malloc(len + 1);
strncpy(str, tmp, len);

free(tmp); // Speicher freigeben!

Auch str muss mit free freigegeben werden, wenn nicht mehr gebraucht.

Alle angaben ohne Gewähr, nicht getestet...

mfg Andreas

von test (Gast)


Lesenswert?

Freak schrieb:
> Klaus Wachtler schrieb:
>> realloc()
>
> Das ist mein Problem. Bei realloc hängt sie sich jedes mal auf. Ich
> schau mir schon verzweifelt die Referenzen an. Irgendwo muss ich was
> übersehen haben.

realloc ist eigentlich problemlos, man muss nur darauf achten dass keine 
Speicherlecks entstehen wenn die Funktion fehlschlägt:
1
int *ptr;
2
3
ptr=alloc(42);
4
if(ptr==NULL)
5
  exit(1);
6
ptr=realloc(ptr,84); <---- Wenn das fehlschlägt geht die Referenz auf den ersten Speicherblock verloren (wird mit NULL überschrieben) --> Speicherleck.
7
if(ptr==NULL)
8
  exit(2);

Lösung: ptr_tmp und nach dem Test auf 0 wieder ptr zuweisen.

von Freak (Gast)


Lesenswert?

Ich versteh es langsam nicht mehr.
1
   int Zeichenanzahl = 0;  
2
3
   [...]  
4
5
   while(!feof(tf))
6
   {
7
   Zeichen = fgetc(tf);
8
   
9
   if(Zeichen!='\n')
10
        {
11
        //Zeichenanzahl++;
12
        Zeichenanzahl=Zeichenanzahl+1;
13
        //Inhalt = (char *) malloc(Zeichenanzahl+1); //+1 da Terminierungszeichen
14
        Inhalt = (char *) realloc(Inhalt,Zeichenanzahl+1);
15
        Inhalt[Zeichenanzahl-1]=Zeichen;  //Da Array bei 0 beginnt
16
        }          
17
   }

Ich öffne die Datei und lese das erste Zeichen. Damit wird Zeichenanzahl 
auf 1 erhöht. Anschließend erweitere ich den String auf 2 
(Zeichenanzahl+1), da das Terminierungszeichen am Ende dazukommt. 
Schlussendlich schreibe ich an Position Zeichenanzahl-1 (da Arrays mit 0 
beginnen) das ausgelesene Zeichen.

Aber warum klappt es nicht?

von DirkB (Gast)


Lesenswert?

Initialisierst du auch Inhalt mit NULL?

Probier mal lieber das Beispiel von Andreas B.

Du kannst ja ganz am Ende nochmal ein realloc machen, wenn du meinst du 
hast zuviel Speicher belegt.

von Klaus W. (mfgkw)


Lesenswert?

Wer keine Rückgabewert anschaut, wird nicht lange programmieren.
Sollen wir raten, was realloc bei dir liefert?
Oder was in den Programmteilen steht, die keiner sieht?

von Andreas B. (andreasb)


Lesenswert?

Freak schrieb:
> Ich versteh es langsam nicht mehr.
>...
> Aber warum klappt es nicht?

WAS heisst "klappt nicht"?

Der Code den du gepostet hat hat keinen Offensichtlichen Fehler din.

Bendenke aber, normalerweise vergrössert realloc den Speicher nicht, 
sondern alloziiert einen neuen Speicherbereich und kopiert alles vom 
alten Speicher in den neuen Speicher, und gibt dann den alten Speicher 
frei.

Das ist extrem langsam. (Komplexität O(n^2) in etwa, falls dir das was 
sagt)

Wenn dann machst du ein realloc alle 1024 Zeichen oder so, oder eben 
ftell.


mfg Andreas

von Klaus W. (mfgkw)


Lesenswert?

Andreas B. schrieb:
> Das ist extrem langsam. (Komplexität O(n^2) in etwa, falls dir das was
> sagt)

evtl. langsam ja, aber wieso O(n^2)? Bei einem realloc(): nein oder bei 
allen zusammengenommen: notfalls ja?

von Klaus W. (mfgkw)


Lesenswert?

Andreas B. schrieb:
> Der Code den du gepostet hat hat keinen Offensichtlichen Fehler din.

Das würde ich so nicht sagen.
Den Rückgabewert von realloc() zu ignorieren, ist nicht nett.

von Andreas B. (andreasb)


Lesenswert?

Klaus Wachtler schrieb:
> Andreas B. schrieb:
>> Das ist extrem langsam. (Komplexität O(n^2) in etwa, falls dir das was
>> sagt)
>
> evtl. langsam ja, aber wieso O(n^2)? Bei einem realloc(): nein oder bei
> allen zusammengenommen: notfalls ja?

Ja sorry, hättes besser weggelassen.

Ich meinte alles Zusammen O(n^2) weil realloc oftmals kopieren muss.

Wird statt realloc am Anfang genug Speicher reserviert läuft das ganze 
mit O(n).

Beweisen kann ich die Behauptung nicht, da ich mich nicht mit der PSP 
auskenne...

Aber wahrscheinlich ist es schneller ohne realloc...



mfg Andreas

von Klaus W. (mfgkw)


Lesenswert?

Wie du schon sagtest: man muß ja nicht byteweise verlängern.
In so einem Fall bietet es sich an, entweder erst die Dateilänge zu 
bestimmen, oder bei Bedarf immer zu verdoppeln o.s.ä..

von Freak (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Oder was in den Programmteilen steht, die keiner sieht?

Da steht folgendes. Nichts relevantes für das Problem.
1
int main() {
2
    
3
   SetupCallbacks();
4
   pspDebugScreenInit();
5
   
6
   int Zeichenanzahl = 0;                            
7
   char Zeichen;
8
   char *Inhalt;
9
   //char Inhalt[5000]; 
10
                 
11
   FILE *tf;
12
   tf = fopen("test.txt","rb");
13
   if(tf==NULL) { 
14
                 pspDebugScreenPrintf("Laden der Datei fehlgeschlagen\n"); 
15
                 sceKernelExitGame();
16
                 }
17
                 else{ pspDebugScreenPrintf("Laden der Datei war erfolgreich\n");}

Andreas B. schrieb:
> Der Code den du gepostet hat hat keinen Offensichtlichen Fehler din.
So sehe ich das auch und trotzdem stimmt was nicht. Ich gehe aber nun 
davon aus, dass es systembedingt ist.

Nun bleibt noch die Möglichkeit - wie auch schon gesagt wurde - vorher 
die Dateigröße/Zeichenanzahl zu bestimmen. Nur wollte ich mir die 
Rechenzeit dafür sparen. Wenn es aber nicht anders geht, dann eben so. 
Ich danke allen die mir bei der Problembeseitigung geholfen haben.

Einen guten Rutsch. :)
MfG

von Klaus W. (mfgkw)


Lesenswert?

Dann ist Inhalt nicht initialisiert?
Und du wunderst dich, wenn realloc() abstürzt?


Gut, daß dieser Programmteil nicht wichtig ist.

von Klaus W. (mfgkw)


Lesenswert?

Die bloße Erwähnung von Callbacks macht mich übrigens auch schon nervös, 
wenn es um Abstürze geht.

von DirkB (Gast)


Lesenswert?

Freak schrieb:
> Nur wollte ich mir die
> Rechenzeit dafür sparen. Wenn es aber nicht anders geht, dann eben so.
> Ich danke allen die mir bei der Problembeseitigung geholfen haben.

Und du meinst, das ständige realloc mit umkopieren ist schneller?

Und realloc muss beim ersten Aufruf einen NULL-Zeiger bekommen.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Man könnte so ein Programm natürlich auch mal auf dem PC ausprobieren 
(ohne PSP Spezifika) und dort debuggen... nur so eine Idee...

Und wer wenn nix läuft beginnt auf einzelne bytes und ein paar 
millisekunden für das bestimmen der Dateigröße hin optimiert dem ist eh 
nicht mehr zu helfen ;)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.