# Pascal-String einlesen



## Scal (16. November 2013)

Hallo alle zusammen,

ich hätte da mal eine Frage. Wie muss ich mir denn einen Pascal String vorstellen? 
Am Anfang der Binärdatei befindet sich noch ein Integer, welcher angibt, wie viele Pascal-Strings in der Datei enthalten sind.

Ich weiß nicht wirklich wie ich das deuten soll. Ich versuch gerade das Ganze einzulesen bzw. die einzelnen Strings in eine Textdatei zu schreiben.


lg ALex


----------



## sheel (16. November 2013)

Hi

kannst du diese Datei herzeigen (nicht reinkopieren, sondern als Anhang)?
Dann sollte klarer werden, was da gemeint ist.


----------



## wolfi1992 (16. November 2013)

Hallo sheel - das würde mich auch interessieren ... find da im Internet irgendwie nix brauchbares  .. also

ein Bsp wäre:

Datentyp 	               Beschreibung


int 	               Anzahl der in der Datei vorhandenen Pascal-Strings
int 	               length1 - Länge des ersten Pascal-Strings
char[length1] 	1. Pascal-String
int 	               length2 - Länge des zweiten Pascal-Strings
char[length2] 	2. Pascal-String 

ein Bsp. wäre

length       H               A               L               L               O
5(int)       48(char)    41(char)     4c(char)    4c(char)     4f(char)


Lg Wolfi


----------



## sheel (16. November 2013)

Also du hast keine Beipieldatei?

Dann geh ich mal von 4Byte-Littleendian-ints und einem reinen 1Byte-Zeichensatz aus,
davon, dass die int-Längen in der Datei stimmen,
und dass das Wort Pascalstring nur da ist, um es euch schwerer zu machen.
C-String triffts genau so gut.

Jedenfalls, hier etwas Code. Ungetestet.

```
#include<stdio.h>

char **einlesen(const char *filename, unsigned long *a)
{
	FILE *dat:
	char **ret;
	unsigned long b, i;
	char e = 0;

	dat = fopen(filename, "rb");
	if(dat == NULL) return NULL;

	*a = 0;
	if(4 != fread((void *)a, 4, 1, dat)) e = 1;
	if(NULL == (ret = (char **)malloc(sizeof(char *) * (*a)))) e = 1;
	if(e)
	{
		fclose(dat);
		return NULL;
	}

	for(i = 0; i < *a; i++)
	{
		b = 0;
		ret[i] = NULL;
		if(4 != fread((void *)(&b), 4, 1, dat)) e = 1;
		if(NULL == (ret[i] = (char *)malloc(b + 1))) e = 1;
		if(ret[i])
		{
			if(b != fread((void *)(ret[i]), b, 1, dat)) e = 1;
			ret[i][b] = '\0';
		}
		if(e)
		{
			if(ret[i]) i++;
			for(--i; i >= 0; i--) free(ret[i]);
			free(ret);
			fclose(dat);
			return NULL;
		}
	}

	fclose(dat);
	return s;
}

void freigeben(char **s, unsigned long a)
{
	for(--a; a >= 0; a--) free(s[a]);
	free(s);
}

...

char **s;
unsigned long count;
s = einlesen("datei.bin", &count);
if(!s) printf("Fehler\n");
else
{
	//s[...] sind Strings, insgesamt count Stück
}
...

freigeben(s, count);
s = NULL;
```


----------



## wolfi1992 (17. November 2013)

Danke dir mal für deine Hilfe .. ja als ob Programmieren lernen allein nicht schon schwer genug wäre  ...
ich hab jetzt übrigens doch eine Beispieldatei  - siehe Anhang! 

Die Aufgabe ist nun diese Datei einzulesen und den String in eine txt Datei zu schreiben. (Returnwert 0)

Es gilt dann noch folgende Fälle zu berücksichtigen. 
Wenn der Befehl nicht mit genau 1 Parameter gestartet wird  --> Returnwert 1

Falls die Binär- oder die Textdatei nicht geöffnet werden kann     
"Error: file cannot be opened.\n" --> Returnwert 2

Falls das im ersten Kommandozeilen-Argument angegebene Binärfile keine Pascal-Strings ist enthält (0 am Anfang) oder ganz leer ist 
"Error: source file is empty.\n" --> Returnwert 3


Wenn in der Binärdatei falsche Daten stehen (Länge stimmt nicht, etc.)    
"Error: file is corrupt.\n" --> Returnwert 4

Falls kein weiterer dynamischer Speicher zur Verfügung steht 
"Error: out of memory.\n" --> Returnwert 5

Ich wäre aber schon mal überglücklich wenn nur mal der Erfolgsfall klappt!

Lg Wolfi


----------



## sheel (17. November 2013)

Also, die Datei schaut zu 100% aus wie vorgestellt... 
damit ist "Pascalstring" nur eine sinnlose Verkomplizierung
(na gut, können ja Pascalstrings sein, aber das Wort ist hier einfach unnötig)

Weitere Annahme: Deine Datei hat nicht mehr als 2GB.

Und eine Frage: Der Name der Binärdatei soll per CMD-Parameter übergeben werden, gut.
Aber der Name der Zieltextdatei nicht?

Und noch eine Frage: Eine der Fehlermeldungen ist, wenn einem der malloc-Speicher ausgeht.
Man könnte die Aufgabe auch ganz ohne malloc lösen (zwar ist es dann langsamer
als die Mit-Variante, aber auch einfacher).
Denkst du, dass ihr malloc verwenden müsst?

Und Frage 3: Soll das Ganze eigentlich eher C oder C++ werden?
malloc und fopen oder new und istream?


----------



## wolfi1992 (17. November 2013)

ja genau  

doch der auch also ein aufruf sollte dann so aussehen ./cprogramm hello helloout.txt


----------



## sheel (17. November 2013)

(Hab oben noch was reineditiert, während du schon geantwortet hast)


----------



## wolfi1992 (17. November 2013)

ah ok  Programmiersprache ist c.
Zu malloc:  da bin ich mir ziemlich sicher dass es so gemeint ist da wir im Moment alles mit dynamisch erweiterbaren speicher machen und auch gerade lernen, also fopen() und fclose(output). lg


----------



## cwriter (17. November 2013)

@sheel


> Weitere Annahme: Deine Datei hat nicht mehr als 2GB.


Falls du damit auf 32/64 ansprichts: http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Opening-Streams.html


> Function: FILE * fopen64 (const char *filename, const char *opentype)
> This function is similar to fopen but the stream it returns a pointer for is opened using open64. Therefore this stream can be used even on files larger then 2^31 bytes on 32 bit machines.
> 
> Please note that the return type is still FILE *. There is no special FILE type for the LFS interface.
> ...


Ist also ziemlich wurscht, da man einfach ein define anfügen kann.

Gruss
cwriter


----------



## Scal (17. November 2013)

Vielen Dank für die Antworten!

Ich hab mir deinen Code angesehen aber ich versteh nicht wirklich was du genau machst bzw. was passiert. Kannst du mir vielleicht erklären was genau passiert?

lg ALex


----------



## 0664jester (17. November 2013)

Hallo,

if(NULL == (ret = (char **)malloc(sizeof(char *) * (*a)))) e = 1;


Was wird a beim ret = gemacht? Mir sagt das irgendwie nichts. 
Was heisst  char **?

lg


----------



## cwriter (17. November 2013)

Char** ist ein Pointer zu einem char-Pointer.
Bei diesem Code wird die erste Dimension des char** alloziert.

Suche doch einfach einmal nach Pointern im Internet. Da kriegst du massig Informationen. Das wurde schon oft erklärt und ich kann das sicher nicht so gut wie andere im Netz.

Gruss
cwriter


----------



## wolfi1992 (17. November 2013)

hi leute also ich glaub mein anfängerdasein wir mir bei der aufgabe echt zum Verhängnis .. 
ich schaff es eine Binärdatei zu schreiben und auch auszulesen aber das mit dem pascalstring krieg ich einfach nicht hin ?!


----------



## sheel (18. November 2013)

*OT:*
Der Thread ist irgendwie seltsam geworden.
Einer fragt, ein anderer bedankt sich für die Antworten,
ein Dritter stellt eine weitere Frage zum Thema...
Telepathie? Drei Accounts der selben Person?

Es würde wahrscneinlich helfen, wenn Scal und Jester
eigene Threads machen und dort erst mal ihre Probleme/Fragen hinschreiben.

Und bitte keine Pushposts.


*@wolfi*, zu http://www.tutorials.de/c-c/396376-pascal-string-einlesen.html#post2047233:
Versuch doch selbst mal nach und nach. (übrigens, "rd" ist nichts für fopen).
Grobe Ablaufbeschreibung vom Programm folgt, als Anstoß

Was ich im folgenden Text nicht extra jedesmal schreibe:
Wenn Fehler dann Beenden, und vor Beenden noch alles free´n und fclose´n was man hat.
Auch viele fer Dateifunktionen etc. gehören auf Fehler überprüft (anhand vom Returnwert)

Ablauf ca.:

```
Zuerst prüfen, ob die Parameteranzahl passt.
Dateien öffnen.
Mit fseek SEEK_END bzw. SEEKSET und ftell kannst du die Byteanzahl der Inputdatei rausfinden,
abspeichern in eine Variable filelen (zB.).
Dann mit fread das Anfangs-int auslesen (Funktionsbeispiel siehe Code irgendwo oben)
Laut Angabe darf muss das int >0 sein, sonst Fehler.
Dann mach einen char-Pointer, auf den du mit malloc zB. 10 Byte reservierst.
Dazu ein int size, wie viel Byte der hat.

Jetzt so oft, wie das Anfangsint angegeben hat in einer Schleife durch
{
int i einlesen
Wenn i > filelen: File korrupt-Fehler

Wenn i > size dann den char-Pointer per realloc vergrößern (auf i)
und size entsprechend auf i setzen

i Byte per fread in den char-Pointer einlesen
Per Schleife durchkontrollieren, ob die Byte wohl nicht zw. -1 und 31 sind (-1 und 31 auch nicht)
Wenn schon dann File korrupt

Per fwrite diese i Byte in die Zieldatei schreiben und einen Zeilenechsel dazu (fputc/fprintf...)

filelen -= i;
}

Nach der Schleife:
Wenn filelen != 0 dann file korrupt

Alles schließen und freigeben
```


*@cwriter*, zu http://www.tutorials.de/c-c/396376-pascal-string-einlesen.html#post2047250:
Das ist Linux 


*@Scal & Jester*, zu http://www.tutorials.de/c-c/396376-pascal-string-einlesen.html#post2047258 etc.
Was genau versteht ihr nicht? Was wollt ihr überhaupt machen?


----------



## wolfi1992 (18. November 2013)

Hi sheel danke mal für deine Erklärung  ! ich werds mal versuchen ob ich es hinbekomme. 
*g* also ich hab auf jeden fall nur EINEN account 

Lg


----------



## Scal (18. November 2013)

Ich hab auch meinen eigenen Account  

Ich hab mir deine Beschreibung mal durchgelesen @Sheel und werde heute versuchen das Ganze mal zu programmieren. Zum einlesen der Datei nehme ich aber schon "fopen(FILENAME, "rb") oder?

lg Alex


----------



## sheel (18. November 2013)

Genau.
Und zum Schreiben der Textdatei w oder a (macht dann einen Unterschied,
wenn es eine Datei mit dem Namen schon gibt:
w überschreibt alles und a hängt das Neue nur hinten an.
Na gut, man könnte auch zuerst abfragen, ob es die Datei gibt und dann sagen "Gibts schon"...)

Bisschen was Beschreibendes zum Vorgang, weil das so vllt. teilweise verwirrend ist:
Eingelesen/Rausgeschrieben wird jeweils ein einzelner String.
Nicht alle Strings einlesen, sondern einen einlesen, rausschreiben, einlesen, rausschreiben...
das ganze Rein-Raus hätte man theoretisch auch nur mit einem Zeichen oÄ. machen können,
aber so ist malloc drin (was ja anscheinend wichtig ist).

Weil der String beim Einlesen ja irgendwo gespeichert werden muss,
bevor man ihn wieder rausschreibt, gibts dafür den char-Pointer, mit malloc-iertem Speicher.
Angefangen mit irgendeiner Grundgröße wie 10, wird er dann eben vergrößert,
wenn man zu einem zu langen String kommt. Wenn dann später ein noch
längerer String in der Datei it wird eben wieder vergrößert usw.

Die Sache mit der Prüfung, ob die Bytes zw. -1 und 31 sind, dann Korrupt-Fehler:
In dem Nummernbereich gibt es keine wirklichen Textzeichen, nur Sonderzeug
wie ein Stringendzeichen (das den String hier abschneiden würde),
ein Dateiendzeichen (das man nicht in die Textdatei rausschreiben sollte),
einen Code der das letzte geschriebene Zeichen uU. wieder löscht
(wenn er irgendwo ausgegeben wird) oder etwas, das bei Ausgabe einen Ton erzeugt.
s steht zwar nicht konkret in der Angabe, aber wenn das Ergebnis
eine Textdatei sein soll ist sowas ungültig.

Und die Sache mit der Dateilänge (zwei Korruptprüfungen):
In der Schleife:
Die Stringlängenint in der Datei müssen ja überprüft werden, ob sie stimmen können.
Ganz am Anfang wird einmal die Dateilänge abgefragt, und wenn man einen String
mit x Byte eingelesen hat wird die Längenzahl um x verkleinert, so dass man sagen kann
"jetzt kommen noch xyz Byte".
Wenn jetzt ein Stringlängenint in der Datei ist, das größer als die restliche Dateilänge ist -> Fehler.

Nch der Schleife:
Man weiss ja von Anfang an, wie viele Strings in der Datei sind (das ganz erste int drin).
Man liest als xyz Strings sein und subtrahiert die gelesene Byteanzahl
von der Dateilängenvariable.
Wenn außer den Strings nichts in der Datei ist sollten am Schluss 0 Byte übrigbleiben,
die man noch nicht verarbeitet hat. Wenn da aber noch was ist: Datei nciht in Ordnung.


----------

