# Dynamischen langen String Parsen



## Davicito (12. Juni 2011)

Hallo, ich brauche mal einen Rat, wie ich aus einem dynamisch langem String parsen kann.

Nehmen wir an, ich habe einen String namens "string" in denen Wörter, Zahlen stehen, die durch Leerzeichen getrennt sind. Ich würde gerne diesen String bis zum ersten Leerzeichen auslesen und das Wort vor dem ersten Leerzeichen in eine gesonderte Stringvariable "string2" einfügen.

Mein Problem dabei ist, dass ich mit sscanf nicht arbeiten kann, da der Inhalt in "string" immer unterschiedlich lang ist.

kann man die sscanf-Funktion anders umbauen als:


```
node * anhaengen(node* list, void* neuer_String)
{
...
            char* string2 = NULL;
            char* string = (char*) aktl_Elem -> content;

            sscanf(string, "%s...",string2);

            if(strcasecmp((char*) neuer_String, string2) == 0)
			{
...
```

oder gibt es da eine noch bessere Funktion, die mir nur das erste Wort meines dynamischen Strings liefert?

Später habe ich angedacht, dass mir die sscanf-Funktion auch alle vorkommenden Wörter und Zahlen liefern soll. aber für einen Vergleich mit strcasecmp ist mir das erste Wort gerad am wichtigsten.

Liebe Grüße.


----------



## Muepe32 (12. Juni 2011)

Davicito hat gesagt.:


> Mein Problem dabei ist, dass ich mit sscanf nicht arbeiten kann, da der Inhalt in "string" immer unterschiedlich lang ist.



Den Zusammenhang musst du mir aber jetzt erklären. Sieh mal was in der Referenz zu sscanf und %s steht:


> %s	String of characters. This will read subsequent characters until a whitespace is found (whitespace characters are considered to be blank, newline and tab).


----------



## Davicito (12. Juni 2011)

HI, was verstehst Du denn nicht?

Wenn  ich auf der Seite: http://www2.hs-fulda.de/~klingebiel/c-stdlib/stdio.htm
unter sscanf nachlese, finde ich nichts, wie ich das erste Wort aus meinem String auslesen kann.

Gruß.


----------



## Muepe32 (12. Juni 2011)

Davicito hat gesagt.:


> HI, was verstehst Du denn nicht?



Wo der Zusammenhang zwischen variabler Stringlänge und Nichtfunktionieren von sscanf ist.

Ich kann nur nochmals aus der Referenz zitieren:


> %s String of characters. This will read subsequent characters until a whitespace is found (whitespace characters are considered to be blank, newline and tab).



->

```
const char* string1 = "Warum geht das nicht?";
char string2[255];
sscanf(string1, "%s", string2);
```
-> string2 == "Warum"


----------



## Matthias Reitinger (12. Juni 2011)

Hallo,

ich sehe keinen Grund, wieso du hier eine Kopie anlegen müsstest. Mach es doch z.B. so:

```
char* firstSpace = strchrnul(string, ' ');
int firstWordLength = firstSpace - string;
  
if (firstWordLength == strlen(neuer_String) &&
    strncasecmp(neuer_String, string, firstWordLength) == 0) {
  ...
}
```

Grüße,
Matthias

\edit:



Muepe32 hat gesagt.:


> -> string2 == "Warum"


Du meinst wohl strcmp(string2, "Warum") == 0 ;-]


----------



## Muepe32 (12. Juni 2011)

Matthias Reitinger hat gesagt.:


> Du meinst wohl strcmp(string2, "Warum") == 0 ;-]



Innerhalb von [c][/c] hätte ich strcmp gemeint, ausserhalb passt für mich  str1 == str2


----------



## sheel (12. Juni 2011)

Hi

deine Verwendung von sscanf wäre schon richtig (bis auf "..."), die Funktion hat auch überhaupt kein Problem mit variablen Stringlängen. Das Problem ist, dass du die Länge des ersten Wortes zuerst wissen musst, um genügend Speicher zu reservieren.


```
int i = 0;
while(string[i] != '\0' && string[i] != ' ' && string[i] != '\t' && string[i] != '\n' && string[i] != '\r') i++;
string2 = (char*) malloc( ++i );

sscanf(string, "%s", string2);
```

Gruß

PS: Wa, wie lang hab ich jetzt gebraucht?


----------



## Matthias Reitinger (12. Juni 2011)

sheel hat gesagt.:


> ```
> int i=0;
> while(string[i]&&string[i]!=32&&string[i]!=8&&string[i]!=10&&string[i]!=13)i++;
> string2=(char*)malloc(++i);
> ...


Nichts für ungut, aber es ist schon bemerkenswert, wie unleserlich du deinen C-Code formulierst und formatierst. Mal ab und zu ein strukturierendes Leerzeichen setzen ist nicht verboten. Und wieso verwendest du Zahlen als magische Konstanten (32, 8, 10, 13...), wenn du doch genauso gut (und viel verständlicher!) die jeweiligen Zeichenkonstanten schreiben könntest? Dann wäre dir nämlich auch nicht der Fehler passiert, dass du statt dem Tabulatorzeichen ('\t' == 9) das Backspacezeichen ('\b' == 8) abtestest. Mal abgesehen davon, dass es auch noch die Funktion isspace gibt.

Wie gesagt, nichts für ungut, aber mir ist das jetzt schon öfters aufgefallen, dass dein Code oft nur sehr schwer lesbar ist. Musste das einfach mal loswerden.

Zum Algorithmus an sich ist noch zu sagen, dass man statt sscanf genauso gut strncpy verwenden könnte, wenn man die Länge des Wortes sowieso schon kennt.

Grüße,
Matthias


----------



## sheel (12. Juni 2011)

Hast eigentlich recht...bin schon so von VS verwöhnt...
Werd in Zukunft mehr darauf achten und habs oben auch editiert.
Besser so?


----------



## Davicito (12. Juni 2011)

> Wo der Zusammenhang zwischen variabler Stringlänge und Nichtfunktionieren von sscanf ist.



Schau mal... der String beihaltet immer mal mehr Wörter oder Zahlen und mal weniger.  Diese sind durch ein Blank von einander getrennt. Wenn ich jetzt also mit sscanf arbeiten möchte, muss ich von vorn herein wissen, wie lang mein String ist. Sprich das geht nur, wenn mein String immer eine constant Anzahl von Wörtern bzw. Zahlen besitzt. 
hatte oben in meinem Code nur mal gezeigt, wie ich das angedacht hatte.

in der Referenz steht auch nur, dass bei %s bis zu einem Whitespace gelesen wird. Welches in meinem fall gut wäre, da ich ja das erste Wort bis zum Whitespace aus meinem String lesen möchte. aber dies ist halt nicht der Fall. Wenn ich das Ergebnis mit printf lese, wird gar nichts aus gegeben. 

Matthias Reitinger hat mich auf Anhieb verstanden was ich eigentlich wollte. Dennoch finde ich seine Lösung nicht gerade die Beste!! wollte schon irgentwie mit der Funktion sscanf arbeiten, aber in der Referenz steht ebend nicht drin, wie ich mit variable langen  Strings - ich sags nochmal - die eine verschiedene Wortanzahl haben können,  nur das erste Wort isolieren kann, um dieses für einen Vergleich mit strcasecmp verwenden kann.

Liebe Grüße.

PS. weil ich die letzten 2 Berichte erst später gelesen habe, das Erste Wort, welches ich abfangen möchte, ist auch immer unterschiedlich lang. D.H. strncpy kommt nicht in Frage!


----------



## Matthias Reitinger (12. Juni 2011)

sheel hat gesagt.:


> Hast eigentlich recht...bin schon so von VS verwöhnt...
> Werd in Zukunft mehr darauf achten und habs oben auch editiert.
> Besser so?


Viel besser  Danke.



Davicito hat gesagt.:


> Schau mal... der String beihaltet immer mal mehr Wörter oder Zahlen und mal weniger.  Diese sind durch ein Blank von einander getrennt. Wenn ich jetzt also mit sscanf arbeiten möchte, muss ich von vorn herein wissen, wie lang mein String ist. Sprich das geht nur, wenn mein String immer eine constant Anzahl von Wörtern bzw. Zahlen besitzt.


Wie kommst du darauf? sscanf ist es egal, wie viele Wörter in der Zeichenkette sind. Hast du den Quellcode von Muepe32 denn ausprobiert? Er funktioniert nämlich.



Davicito hat gesagt.:


> in der Referenz steht auch nur, dass bei %s bis zu einem Whitespace gelesen wird. Welches in meinem fall gut wäre, da ich ja das erste Wort bis zum Whitespace aus meinem String lesen möchte. aber dies ist halt nicht der Fall. Wenn ich das Ergebnis mit printf lese, wird gar nichts aus gegeben.


Dass dein Quellcode aus dem ersten Beitrag so nicht funktioniert, ist dir ja wohl klar?! Du reservierst ja überhaupt keinen Speicher für string2.



Davicito hat gesagt.:


> Matthias Reitinger hat mich auf Anhieb verstanden was ich eigentlich wollte. Dennoch finde ich seine Lösung nicht gerade die Beste!! wollte schon irgentwie mit der Funktion sscanf arbeiten, aber in der Referenz steht ebend nicht drin, wie ich mit variable langen  Strings - ich sags nochmal - die eine verschiedene Wortanzahl haben können,  nur das erste Wort isolieren kann, um dieses für einen Vergleich mit strcasecmp verwenden kann.


Meine Lösung funktioniert und legt keine unnötige Kopie des Wortes an. Was willst du denn noch? Warum du krampfhaft versuchst, mit sscanf zu arbeiten, verstehe ich übrigens nicht.

Grüße,
Matthias

\edit: 





Davicito hat gesagt.:


> PS. weil ich die letzten 2 Berichte erst später gelesen habe, das Erste Wort, welches ich abfangen möchte, ist auch immer unterschiedlich lang. D.H. strncpy kommt nicht in Frage!


Wieso nicht?


----------



## Davicito (12. Juni 2011)

> Wie kommst du darauf? sscanf ist es egal, wie viele Wörter in der Zeichenkette sind. Hast du den Quellcode von Muepe32 denn ausprobiert? Er funktioniert nämlich.



Ja.. ich möchte es aber lieber vermeiden, unnötig Speicher -> char string2[255]; zu reservieren als vielleicht mein wort gebraucht hätte. 



> Dass dein Quellcode aus dem ersten Beitrag so nicht funktioniert, ist dir ja wohl klar?! Du reservierst ja überhaupt keinen Speicher für string2.



Das ich immer Speicher neu reservieren muss, war mir nicht klar, weil ich nicht weiß wieso, wenn ich nur ein Wort aus einem string kopieren möchte. Wenn das so ist, wie so nicht auch bei den Funktionen: strtok oder sscanf? Da wird doch auch nie vorher Speicher allokiert. Wieso also  bei meiner Variable "string2", wenn ich die mit char* deklariere und dann mit NULL initialisiere? Wenn ich da eine Wissenslücke habe, bitte um Aufklärung. Bin immer noch nicht firm, was C betrifft, da ich viel zu verwöhnt bin von Java!



> Meine Lösung funktioniert und legt keine unnötige Kopie des Wortes an. Was willst du denn noch? Warum du krampfhaft versuchst, mit sscanf zu arbeiten, verstehe ich übrigens nicht.



..ich denke mir einfach, das müsse doch noch praktischer von statten gehen. kann mir nicht vorstellen, das man das so wie Du das gemacht hat, so kompliziert gemacht werden muss. natürlich geht das auch so. aber vielleicht ist das auch nicht die eleganteste Lösung!

Gruß.


----------



## sheel (12. Juni 2011)

Wenn du nicht unnötig viel Speicher reservieren möchtest, such doch einfach nach dem ersten Leerzeichen etc
und finde so heraus, wieviel Buchstaben es bis dahin sind.
Auch wenn mein Code vorher nicht schön formatiert war, zeigt er das prinzipiell.

Und auch für sscanf (um das es ja gerade geht) muss man zuerst Speicher haben.
string ist ja schon vorhanden, nur string2 eben noch nicht.
Und dabei ist es egal, ob die Menge zuviel oder genau richtig ist, aber es muss was da sein.

Auch in Java kannst du nicht xyz=null; schreiben und xyz dann verwenden.
In Java wirds halt NullPointerException genannt.


----------



## Davicito (12. Juni 2011)

Hi sheel,

hab mal Deinen Code nach gebaut 


```
...
char* string = NULL;
		char* string2 = NULL;

int i = 0;
while(string[i] != ' ')
			{
				i++;
				string2 = (char*) malloc(i);
			}
strncpy(string2, string, i);

printf("string: %s\n", string2);

if(strcasecmp((char*) neuer_String, string2) == 0)
			{
```

Das Programm zeigt mir jetzt auch immer nur das erste Wort auf der Konsole an und bricht dann nach ca. 20 Wörten ab und gibt dann folgendes wieder. 


```
...
string: und
string: Medizin
string: Und
string: leider
string: auch
string: Theologie
string: Durchaus
string: studiert
string: mit
string: heissem
string: Bemuehn
string: Da
string: steh
string: ich
string: nun
string: ich
string: armer
string: Tor
string: Und
string: Habe
Liste: malloc.c:3096: sYSMALLOc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) *
2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >=
(unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & 
~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) ==
0)' failed.
Abgebrochen
Davicito@ubuntu:~/C/Beleg_01$
```

Damit kann ich leider nichts anfangen. Wiss Ihr vielleicht was ich hier wieder falsch gemacht habe?

Gruß.


----------



## sheel (12. Juni 2011)

Kannst du den Code posten, der diese Ausgabe erzeugt?
Der gezeigt ist es sicher nicht, der stürzt nämlich sofort ab.

Und da es wahrscheinlich nicht so lang ist, bitte gleich das komplette Programm.

PS: Das Programm ist als Debug kompiliert, oder?


----------



## Davicito (12. Juni 2011)

wie du willst.


```
...
if(kopf == aktl_Elem -> left && aktl_Elem -> content == NULL)
{	
		char* conttmp = (char*) set(neuer_String);
		//! Blank hinten einfügen
		strcat(conttmp, " ");
		//! Dateinmane hinten einfügen
		strcat(conttmp, (char*) set(dateiname));
		//! Blank hinten einfügen
		strcat(conttmp, " ");
		//! ZeilenCounter für ListenContent in Zeichen umwandeln
		char t[6];
		sprintf(t, "%i", counter);
		//! ZeilenCounter hinten einfügen
		strcat(conttmp, (char*)set((void*)t));
		//! Alle Parameter in Content einfügen
		neu_Elem -> content     = set((void*) conttmp);
		//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
}
else 
{
       char* string = NULL;
		char* string2 = NULL;
		
		while(kopf != (aktl_Elem = aktl_Elem -> left))
		{
			
			string = (char*) aktl_Elem -> content;
			
			int i = 0;
            while(string[i] != ' ')
            {
                i++;
                string2 = (char*) malloc(i);
            }
            strncpy(string2, string, i);	
			
			printf("string: %s\n", string2);

			if(strcasecmp((char*) neuer_String, string2) == 0)
			{
				char* conttmp = (char*) aktl_Elem -> content;
				//! ZeilenCounter für ListenContent in Zeichen umwandeln
				char t[6];
				sprintf(t, "%i", counter);
				//! ZeilenCounter hinten einfügen
				strcat(conttmp, (char*)set((void*)t));
				neu_Elem -> content     = set((void*) conttmp);
			}
			else
			{
				char* conttmp = (char*) set(neuer_String);
				//! Blank hinten einfügen
				strcat(conttmp, " ");
				//! Dateinmane hinten einfügen
				strcat(conttmp, (char*) set(dateiname));
				//! Blank hinten einfügen
				strcat(conttmp, " ");
				//! ZeilenCounter für ListenContent in Zeichen umwandeln
				char t[6];
				sprintf(t, "%i", counter);
				//! ZeilenCounter hinten einfügen
				strcat(conttmp, (char*)set((void*)t));
				//! Alle Parameter in Content einfügen
				neu_Elem -> content     = set((void*) conttmp);
				//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
			}
			
		}
}
...
```

PS. ja das Programm ist fehlerfrei kompiliert worden.


----------



## sheel (12. Juni 2011)

Das ist zwar auch kein Komplettprogramm...wenn es fürs Forum zu lang wird,
kannst du ruhig die Datei anhängen.
Ich werd von der Codemenge sicher nicht erschlagen.

Ich versuch, so gut wie möglich auf das sichtbare einzugehen:

```
char* string = NULL;
        char* string2 = NULL;
        
        while(kopf != (aktl_Elem = aktl_Elem -> left))
        {
            
            string = (char*) aktl_Elem -> content;
            
            int i = 0;
            while(string[i] != ' ')
            {
                i++;
                string2 = (char*) malloc(i);
            }
            strncpy(string2, string, i);    
            
            printf("string: %s\n", string2);
```
In der Schleife weist du String schonmal was zu. Gut.

Das strncpy und printf am Schluss schauen auch OK aus.

Die Schleife mit dem malloc allerdings ist grausam.
Angenommen, das erste Wort hat 8 Buchstaben, reservierst du 1, dann 2, dann 3...bis 8 Byte.
Macht bei 8 Buchstaben ca. 36 Byte, bei 10 55 usw...
Erstens brauchst du das Ganze garnicht, zweitens ist nirgends ein free drinnen.
Schau nocheinmal meine Codezeilen an. Da ist das malloc außerhalb der Schleife.

Der eigentliche Fehler lässt sich im geposteten Code nicht finden.

Zum PS: Ich meinte, ob du es als Debug oder Release kompiliert hast.

Gruß

PS: Der Satz in der Ausgabe...ist das Faust?


----------



## Davicito (12. Juni 2011)

Uff!

Debugen unter Ubuntu weiß nicht nicht wie man das machen kann!

und 2. ich habe folgendes vor. 
meine anhaeng-Funktion soll Wörter, was es bekommt in meine doppelt verkettete generische Liste einfügen.

der funktionierende Code dazu sieht so aus.


```
typedef struct node
{
	struct node* right; // Zeiger auf das Nachfolgerelement
	struct node* left;  // Zeiger auf das Vorgängerelement
	void* content; // Für den Dateninhalt	
} node;
...
```


```
...
#include "Liste.h"

node* init_Liste()
{
	node* n = (node*) malloc(sizeof(node));
	n -> right   = n;
	n -> left    = n;
	n -> content = NULL;
		
	return n;
}

node* anhaengen(node* list, void* neuer_String, void* dateiname, void* (*set)(void*), int counter)
{
	node* neu_Elem = (node*) malloc(sizeof(node)); //! Speicher für neuen Knoten besorgen 
	//! Überprüfen, ob Speicher angelegt worden ist	
	if(neu_Elem == NULL)
	{
		printf("\n Listenelement konnte nicht angelegt werden, da kein Speicher vorhanden ist! \n");
		exit(1);
	}
	
	//*******  Vorbereiten des Datensatzes für die generiche Liste *************************
	char* conttmp = (char*) set(neuer_String);
	//! Blank hinten einfügen
	strcat(conttmp, " ");
	//! Dateinmane hinten einfügen
	strcat(conttmp, (char*) set(dateiname));
	//! Blank hinten einfügen
	strcat(conttmp, " ");
	//! ZeilenCounter für ListenContent in Zeichen umwandeln
	char t[6];
	sprintf(t, "%i", counter);
	//! ZeilenCounter hinten einfügen
	strcat(conttmp, (char*)set((void*)t));
	//! Alle Parameter in Content einfügen
	neu_Elem -> content     = set((void*) conttmp);
	printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
		
	//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
	//! Neues Element zeigt mit linkem Zeiger auf aktuelles Element (Liste)
	neu_Elem -> left       = list;
	//! Neues Element zeigt mit rechtem Zeiger auf rechten Nachbarn des Aktuellen Elements
	neu_Elem -> right      = list -> right;
	//! Von aktuellem Element auf rechtes Zeigen und von rechtem Element auf mit left auf neues Element zeigen
	list -> right-> left   = neu_Elem;
	//! Von aktuellem Element mit right auf neues Element zeigen
	list -> right          = neu_Elem;
		
	return neu_Elem;
}
...
//! Kopieren von übergebenem String
void* copyString(void* stringUebergabe)
{
	//! Wandelt void zeiger in char zeiger
	char* string = (char*) stringUebergabe;
	char* speicher = (char*) malloc(sizeof(node/*strlen(string)+1)*/));
	//Nach Speicherreservierung immer prüfen, ob auch Speicher vorhanden war!!
	if(speicher == NULL)
	{
		printf("\n Programm wurde beendet, da kein Speicher vorhanden ist! \n");
		exit(1);
	}
	//! Kopiert StringUebergabe in Speicher
	strcpy(speicher, string);
	return speicher;	
}
...
```

So wie es oben ist, wird auch alles nach und nach in meine Liste eingegeben. D.H. der Content eines Listen Elements beinhaltet immer ein Wort Dateiname und die Zeile in dem das Wort vorgekommen ist.
Beispiel: Haben Test.txt 3

das wird auch prima gemacht.

Was ich also die ganze Zeit versucht habe ist, wenn ein neues Wort  der anhaeng-Funktion übergeben wird soll geprüft werden ob das Wort in der Liste schon vorhanden ist. Wenn ja, soll nicht das selbe Wort nochmal in die Liste eingefügt werden, sondern nur die Zeilenzahl hinten im Content des gleichen Wortes angefügt werden. Sprich: haben Test.txt 3 6.
Wenn das Wort noch nicht in der Liste steht, soll es neu in die Liste eingefügt werden.

Das hatte ich zweifelhaft versucht mit If-Abfragen in meiner anhaeng-Funktion zu realisieren.

sprich:

```
...
node* anhaengen(node* list, void* neuer_String, void* dateiname, void* (*set)(void*), int counter)
{
	node* kopf      = list;
	node* aktl_Elem = list;

	node* neu_Elem = (node*) malloc(sizeof(node)); //! Speicher für neuen Knoten besorgen 
	//! Überprüfen, ob Speicher angelegt worden ist	
	if(neu_Elem == NULL)
	{
		printf("\n Listenelement konnte nicht angelegt werden, da kein Speicher vorhanden ist! \n");
		exit(1);
	}
	//! nodeCounter bekommt Maximale Anzahl bei jedem Funktionsaufruf der Listenelemente b zugewiesen
	//neu_Elem -> nodeCounter = list -> nodeCounter++;
	
	// Wenn Liste leer ist, wird das erste Wort eingefügt	
if(kopf == aktl_Elem -> left /*&& aktl_Elem -> content == NULL*/)
	{	//printf("Test!\n");
		char* conttmp = (char*) set(neuer_String);
		//! Blank hinten einfügen
		strcat(conttmp, " ");
		//! Dateinmane hinten einfügen
		strcat(conttmp, (char*) set(dateiname));
		//! Blank hinten einfügen
		strcat(conttmp, " ");
		//! ZeilenCounter für ListenContent in Zeichen umwandeln
		char t[6];
		sprintf(t, "%i", counter);
		//! ZeilenCounter hinten einfügen
		strcat(conttmp, (char*)set((void*)t));
		//! Alle Parameter in Content einfügen
		neu_Elem -> content     = set((void*) conttmp);
		printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
		
		//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
		//! Neues Element zeigt mit linkem Zeiger auf aktuelles Element (Liste)
		neu_Elem -> left       = list;
		//! Neues Element zeigt mit rechtem Zeiger auf rechten Nachbarn des Aktuellen Elements
		neu_Elem -> right      = list -> right;
		//! Von aktuellem Element auf rechtes Zeigen und von rechtem Element auf mit left auf neues Element zeigen
		list -> right-> left   = neu_Elem;
		//! Von aktuellem Element mit right auf neues Element zeigen
		list -> right          = neu_Elem;
	}	
	else
	{ 
		char* string = NULL;
		char* string2 = NULL;
		
		while(kopf != (aktl_Elem = aktl_Elem -> left))
		{
			
			string = (char*) aktl_Elem -> content;
			
			int i = 0;			
			while(string[i] != ' ')	i++;
			string2 = (char*) malloc(++i);
			
			strncpy(string2, string, i);
			
			//sscanf(string, "%s", string2);			
			
			//printf("string: %s\n", string2);

			if(strcasecmp((char*) neuer_String, string2) == 0)
			{
				char* conttmp = (char*) aktl_Elem -> content;
				//! ZeilenCounter für ListenContent in Zeichen umwandeln
				char t[6];
				sprintf(t, "%i", counter);
				//! ZeilenCounter hinten einfügen
				strcat(conttmp, (char*)set((void*)t));
				neu_Elem -> content     = set((void*) conttmp);
			}
			else
			{
				char* conttmp = (char*) set(neuer_String);
				//! Blank hinten einfügen
				strcat(conttmp, " ");
				//! Dateinmane hinten einfügen
				strcat(conttmp, (char*) set(dateiname));
				//! Blank hinten einfügen
				strcat(conttmp, " ");
				//! ZeilenCounter für ListenContent in Zeichen umwandeln
				char t[6];
				sprintf(t, "%i", counter);
				//! ZeilenCounter hinten einfügen
				strcat(conttmp, (char*)set((void*)t));
				//! Alle Parameter in Content einfügen
				neu_Elem -> content     = set((void*) conttmp);
				//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );

				//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
				//! Neues Element zeigt mit linkem Zeiger auf aktuelles Element (Liste)
				neu_Elem -> left       = list;
				//! Neues Element zeigt mit rechtem Zeiger auf rechten Nachbarn des Aktuellen Elements
				neu_Elem -> right      = list -> right;
				//! Von aktuellem Element auf rechtes Zeigen und von rechtem Element auf mit left auf neues Element zeigen	
				list -> right-> left   = neu_Elem;
				//! Von aktuellem Element mit right auf neues Element zeigen
				list -> right          = neu_Elem;
			}
			//free(string2);
			printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
			
		}
	}
	return neu_Elem;
}
```

PS. Ich kann Dir  nicht das gesamte Programm geben, da es eine große Baustelle ist und ich einige Funktionen auskommentiert habe .. es befindet sich in einer Umstrukturierung und wenn du den Quelltext siehst, wirst du nichts mehr verstehen können. Handelt sich auch zudem um ca. 500 Zeilen!

Gruß.


----------



## sheel (12. Juni 2011)

Davicito hat gesagt.:


> Debugen unter Ubuntu weiß nicht nicht wie man das machen kann!


Von dem redet ja auch keiner.
Du solltest dir bezüglich Debug/Release dringend anschauen, wie man einen Compiler bedient.
Das sind eigentlich Grundlagen, vor allem auf der Uni.



Davicito hat gesagt.:


> und 2. ich habe folgendes vor.
> meine anhaeng-Funktion soll Wörter, was es bekommt in meine doppelt verkettete generische Liste einfügen.
> 
> der funktionierende Code dazu sieht so aus.
> ...


Da ich dir auch bei dieser Funktion schon helfen musste, ist sie mir noch in Erinnerung.
Da braucht es keine weiteren Erklärungen, ich weiss worum es geht.

Zu bemängeln ist allerdings, dass du Dateinamen und Zeilennummer mit dem Wert in Content speicherst.
Dadurch bekommst du ja erst die ganzen aktuellen Probleme.
Warum nicht in 3 Variablen auftrennen (und noch dazu den Dateinamen nicht immer redundant speichern)?



Davicito hat gesagt.:


> Was ich also die ganze Zeit versucht habe ist, wenn ein neues Wort  der anhaeng-Funktion übergeben wird soll geprüft werden ob das Wort in der Liste schon vorhanden ist. Wenn ja, soll nicht das selbe Wort nochmal in die Liste eingefügt werden, sondern nur die Zeilenzahl hinten im Content des gleichen Wortes angefügt werden. Sprich: haben Test.txt 3 6.
> Wenn das Wort noch nicht in der Liste steht, soll es neu in die Liste eingefügt werden.


Auch hier gilt die Antwort auf den vorigen Absatz.
Mit einer anderen Datenstruktur machst du dir das Leben viel einfacher.
Warum bist du schon wochenlang so versessen auf eine einzelne doppelt verkettete Liste?

Ich würde folgendes machen:
Liste aus Dateinamen, von der jedes Element wieder eine Liste aus Worten hat.
Jedes Wort hat wieder eine Liste aus int mit den Zeilennummern.
Für jede Liste kann man optional auch ein Array verwenden.

Schaut zwar auf den ersten Blick nach mehr Arbeit aus, macht aber alles übersichtlicher, einfacher und erspart zudem noch die Wurschtelei mit den Stringfunktionen.



Davicito hat gesagt.:


> PS. Ich kann Dir  nicht das gesamte Programm geben, da es eine große Baustelle ist und ich einige Funktionen auskommentiert habe .. es befindet sich in einer Umstrukturierung und wenn du den Quelltext siehst, wirst du nichts mehr verstehen können. Handelt sich auch zudem um ca. 500 Zeilen!


Kannst oder willst du nur nicht?
Ich hätte damit kein Problem.
Für den Fall, dass ich wirklich einmal den Sinn einer Anweisung nicht nachvollziehen kann, hast du ja (für dich) sowieso schon alles schön auskommentiert.
Und 500 Zeilen ist auch kein Problem, hab schon bei weitem größere Programme gesehen und geschrieben.

Gruß


----------



## Davicito (13. Juni 2011)

> Von dem redet ja auch keiner.
> Du solltest dir bezüglich Debug/Release dringend anschauen, wie man einen Compiler bedient.
> Das sind eigentlich Grundlagen, vor allem auf der Uni.



Ich kenne nur:  cc -o -Wall -std=c99 Liste Liste.c bzw. gcc -g -Wall -std=c99 -o Liste Liste.c mit dem ich kompeliere und sehe, ob mir der Kompiler Fehlermeldungen ausspuckt oder nicht!
Wenn Du dass meintest, arbeite ich schon die ganze Zeit damit.



> Zu bemängeln ist allerdings, dass du Dateinamen und Zeilennummer mit dem Wert in Content speicherst.
> Dadurch bekommst du ja erst die ganzen aktuellen Probleme.
> Warum nicht in 3 Variablen auftrennen (und noch dazu den Dateinamen nicht immer redundant speichern)?



Es soll ebent nicht 3 Variablen sein, da es eine Auflage in unserem Kurs ist, generische Variablen zu benutzen. D.H. ein typlosen Container (void) der alles beinhalten soll um so die Wiederverwendbarkeit zwischen den Funktionen zu garantieren.



> Mit einer anderen Datenstruktur machst du dir das Leben viel einfacher.
> Warum bist du schon wochenlang so versessen auf eine einzelne doppelt verkettete Liste?



Das ist mir auch bekannt, das ich mit anderen Datenstrukturen einfacher fahre als mit nur einer Variablen. Dies ist aber nicht Aufgabe und Idee einer generischen Datenstruktur! Es wird von uns verlangt!
Zudem müssen wir unsere doppelt verkettete Liste weiter ausbauen und alles was wir im Unterricht lernen, müssen wir auf  unserer Liste anwenden. Es sind nicht meine Wünsche.. So viel steht fest.

hier meine Liste.h:

```
//! Diese Struktur definiert die einzelnen Listenknoten für die doppelt verkettete Liste an
typedef struct node
{
	struct node* right; // Zeiger auf das Nachfolgerelement
	struct node* left;  // Zeiger auf das Vorgängerelement
	void* content; // Für den Dateninhalt	
} node;

int strcasecmp(const char* s1, const char* s2);

// Startfunktion der Liste.
node* init_Liste();

// Diese Funktion fügt einzelne Stings in die Liste ein.
node* anhaengen(node* aktl_Elem, void* neuer_String, void* dateiname, void* (*set)(void*), int counter);

// Diese Funktion sortiert Wörter
void sort(node* list);//, int wortLineCounter);

// Ausgabe aller Listenelemente.
void print_all(node* list, void* (*getcontent)(void*), void* (*getfilename)(void*), FILE* file2, char* option);

void deleten(struct node* list);
void delete_all(struct node* list, int wordnumber);
```

Hier meine Liste.c

```
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "Liste.h"
#define SIZE 256

//! Listeninitierung: right und left zeigen auf sich selbst, content zeigt auf NULL  und lineCounter und nodeCounter werden mit 0 initialisiert
node* init_Liste()
{
	node* n = (node*) malloc(sizeof(node));
	n -> right   = n;
	n -> left    = n;
	n -> content = NULL;
		
	return n;
}

//************************************ Listenfunktionen ***********************************
//! Parameter: 1. aktuelles Element, 2. aktuelles Wort, 3. Dateiname 4. Kopie des Strings, 5. ZeilenCounter
node* anhaengen(node* list, void* neuer_String, void* dateiname, void* (*set)(void*), int counter)//, int counter2)
{
	node* kopf      = list;
	node* aktl_Elem = list;

	node* neu_Elem = (node*) malloc(sizeof(node)); //! Speicher für neuen Knoten besorgen 
	//! Überprüfen, ob Speicher angelegt worden ist	
	if(neu_Elem == NULL)
	{
		printf("\n Listenelement konnte nicht angelegt werden, da kein Speicher vorhanden ist! \n");
		exit(1);
	}
	//! nodeCounter bekommt Maximale Anzahl bei jedem Funktionsaufruf der Listenelemente b zugewiesen
	//neu_Elem -> nodeCounter = list -> nodeCounter++;
	
	// Wenn Liste leer ist, wird das erste Wort eingefügt	
	if(kopf == aktl_Elem -> left /*&& aktl_Elem -> content == NULL*/)
	{	//printf("Test!\n");
		char* conttmp = (char*) set(neuer_String);
		//! Blank hinten einfügen
		strcat(conttmp, " ");
		//! Dateinmane hinten einfügen
		strcat(conttmp, (char*) set(dateiname));
		//! Blank hinten einfügen
		strcat(conttmp, " ");
		//! ZeilenCounter für ListenContent in Zeichen umwandeln
		char t[6];
		sprintf(t, "%i", counter);
		//! ZeilenCounter hinten einfügen
		strcat(conttmp, (char*)set((void*)t));
		//! Alle Parameter in Content einfügen
		neu_Elem -> content     = set((void*) conttmp);
		printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
		
		//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
		//! Neues Element zeigt mit linkem Zeiger auf aktuelles Element (Liste)
		neu_Elem -> left       = list;
		//! Neues Element zeigt mit rechtem Zeiger auf rechten Nachbarn des Aktuellen Elements
		neu_Elem -> right      = list -> right;
		//! Von aktuellem Element auf rechtes Zeigen und von rechtem Element auf mit left auf neues Element zeigen
		list -> right-> left   = neu_Elem;
		//! Von aktuellem Element mit right auf neues Element zeigen
		list -> right          = neu_Elem;
	}	
	else
	{ 
		char* string = NULL;
		char* string2 = NULL;
		
		while(kopf != (aktl_Elem = aktl_Elem -> left))
		{
			
			string = (char*) aktl_Elem -> content;
			
			int i = 0;			
			while(string[i] != ' ')	i++;
			string2 = (char*) malloc(++i);
			
			strncpy(string2, string, i);
			
			//sscanf(string, "%s", string2);			
			
			//printf("string: %s\n", string2);

			if(strcasecmp((char*) neuer_String, string2) == 0)
			{
				char* conttmp = (char*) aktl_Elem -> content;
				//! ZeilenCounter für ListenContent in Zeichen umwandeln
				char t[6];
				sprintf(t, "%i", counter);
				//! ZeilenCounter hinten einfügen
				strcat(conttmp, (char*)set((void*)t));
				neu_Elem -> content     = set((void*) conttmp);
			}
			else
			{
				char* conttmp = (char*) set(neuer_String);
				//! Blank hinten einfügen
				strcat(conttmp, " ");
				//! Dateinmane hinten einfügen
				strcat(conttmp, (char*) set(dateiname));
				//! Blank hinten einfügen
				strcat(conttmp, " ");
				//! ZeilenCounter für ListenContent in Zeichen umwandeln
				char t[6];
				sprintf(t, "%i", counter);
				//! ZeilenCounter hinten einfügen
				strcat(conttmp, (char*)set((void*)t));
				//! Alle Parameter in Content einfügen
				neu_Elem -> content     = set((void*) conttmp);
				//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );

				//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
				//! Neues Element zeigt mit linkem Zeiger auf aktuelles Element (Liste)
				neu_Elem -> left       = list;
				//! Neues Element zeigt mit rechtem Zeiger auf rechten Nachbarn des Aktuellen Elements
				neu_Elem -> right      = list -> right;
				//! Von aktuellem Element auf rechtes Zeigen und von rechtem Element auf mit left auf neues Element zeigen	
				list -> right-> left   = neu_Elem;
				//! Von aktuellem Element mit right auf neues Element zeigen
				list -> right          = neu_Elem;
			}
			//free(string2);
			printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
			
		}
	}
	return neu_Elem;
}

void deleten(struct node* list)
{
	//! Zeiger neu setzen
	list->right->left = list->left;
	list->left->right = list->right;
	
	//! Freigeben des allokierten Speichers
	free(list);
	//free(list->dateiname);
	free(list->content);
}


void delete_all(struct node* list, int wordnumber)
{	
	int i = 0;
	//! Freigeben für Wörter -1 Elemente
	while (i<wordnumber)
	{
		deleten(list);	
		list=list->left;			
		i++;
	}	
	//! Freigeben für letztes Wort
	deleten(list);
}
/*
void sort(node* list)//, int wortLineCounter)
{
	//! Listenkopf - Abbruchbedingung für innere While-Schleife, kopf und aktuelles Element zu Anfang identisch
	node* kopf      = list;
	node* aktl_Elem = list; 
		 
	//! Für die Sortierung der Liste	
	void* tmpWort = NULL;
	void* tmpFilename = NULL;	
	int tmpLineCounter = 0;
	
	//Start des Bubblesort-Algorithmus
	int i = 0; // Zähler für Äußere While-Schleife - Bubblesort
	
	//! Führt Schleife Wortanzahl mal 2 durch
	while(i <= list->nodeCounter*2)
	{
		//! Solange Listenkopf ungleich aktuellem Element und aktuelles Element ungleich NULL (linksrum)
		while(kopf != (aktl_Elem = aktl_Elem -> left) && aktl_Elem -> left -> content != NULL)
		{
		  	
			char* string1 = (char*) aktl_Elem -> content;		//!Wort des aktuellen Elements
			char* string2 = (char*) aktl_Elem -> left -> content;   //!Wort des linken Elements
								
			
			if(strcasecmp(string1, string2) > 0)
			{					
				//! Wort aus aktuellen Element sichern
				tmpWort = aktl_Elem -> content;
				//! Wort aus aktuellen Element sichern
				tmpFilename = aktl_Elem -> dateiname;
				//! Zeilennummer aus aktuellem Element sichern
				tmpLineCounter = aktl_Elem -> lineCounter;

				//! Wort des linken Elements dem aktuellen Element zuweisen
				aktl_Elem -> content = aktl_Elem -> left -> content;
				//! Wort des linken Elements dem aktuellen Element zuweisen
				aktl_Elem -> dateiname = aktl_Elem -> left -> dateiname;
				//! Zeilennummer des linken Elements dem aktuellen Element zuweisen
				aktl_Elem -> lineCounter = aktl_Elem -> left -> lineCounter;

				//! Gesichertes Wort in linken Nachbarn Einfügen
				aktl_Elem -> left -> content = tmpWort;
				//! Gesichertes Wort in linken Nachbarn Einfügen
				aktl_Elem -> left -> dateiname = tmpFilename;
				//! Gesicherts Zeilennummer in linken Nachbarn Einfügen
				aktl_Elem -> left -> lineCounter = tmpLineCounter;
			}
			//! Wenn Wörter gleich sind, soll nach Zeilennummer sortiert werden
			if(strcasecmp(string1, string2) == 0)
			{
				int zeilennr1 = aktl_Elem -> lineCounter;
				int zeilennr2 = aktl_Elem -> left ->lineCounter;

				if(zeilennr1 > zeilennr2)
				{					
					//! Wort aus aktuellen Element sichern
					tmpWort = aktl_Elem -> content;
					//! Wort aus aktuellen Element sichern
					tmpFilename = aktl_Elem -> dateiname;
					//! Zeilennummer aus aktuellem Element sichern
					tmpLineCounter = aktl_Elem -> lineCounter;

					//! Wort des linken Elements dem aktuellen Element zuweisen
					aktl_Elem -> content = aktl_Elem -> left -> content;
					//! Wort des linken Elements dem aktuellen Element zuweisen
					aktl_Elem -> dateiname = aktl_Elem -> left -> dateiname;
					//! Zeilennummer des linken Elements dem aktuellen Element zuweisen
					aktl_Elem -> lineCounter = aktl_Elem -> left -> lineCounter;

					//! Gesichertes Wort in linken Nachbarn Einfügen
					aktl_Elem -> left -> content = tmpWort;
					//! Gesichertes Wort in linken Nachbarn Einfügen
					aktl_Elem -> left -> dateiname = tmpFilename;
					//! Gesicherts Zeilennummer in linken Nachbarn Einfügen
					aktl_Elem -> left -> lineCounter = tmpLineCounter;
				}
			}		
			
		}
		i++;		
	}	
}
*/

//! Parameter: 1.aktuelles_Element, 2.Kopiertes Wort, 3.Ausgabedatei, 4.Übergebener Optionsparameter
void print_all(node* list, void* (*getcontent)(void*), void* (getfilename)(void*), FILE* file2, char* option)
{
	//! Listenkopf - Abbruchbedingung für innere While-Schleife, kopf und aktuelles Element zu Anfang identisch
	node* kopf      = list;
	node* aktl_Elem = list;
	
	char* wort = NULL;	// Für den Rückgabewert aus der Funktionsvariablen - getcontent 
	char* filename = NULL;  // Für den Rückgabewert aus der Funktionsvariablen - getfilename

	//! Wenn die Liste leer ist soll nichts passieren
	if(kopf == NULL && kopf -> content == NULL)
	{
		return; 
	}			
	else
	{				
		int zahl = 0; //! Für den Schwellenwert
						
		/*sscanf(option,"-w=%d", &zahl);	//!Ließt Schwellenwert ein	
		strtok(option, "=");  //! Trennt Zeichenfolge, aus option, bis zum = Zeichen ab. 
		
		//! Wenn der Optionsparameter '-w=n'gewählt wurde		
		if(strcasecmp(option, "-w") == 0 && zahl > 1)
		{		
			//! Werden alle Listenelemente abgegangen					
			while(kopf != (aktl_Elem = aktl_Elem -> left))
			{				
				//! Wenn Listenelemente größer bzw. gleich groß sind werden diese ausgegeben.				
				if(strlen(aktl_Elem -> content) >= zahl && zahl >= 1 && strlen(aktl_Elem -> content) >= zahl && zahl >= 1)
				{	
					char* string1 = (char*) aktl_Elem -> content;		//!Wort des aktuellen Elements
					char* string2 = (char*) aktl_Elem -> left -> content;   //!Wort des linken Elements			
			
					if(strcasecmp(string1, string2) < 0)
					{
						printf(" %i. ", aktl_Elem -> nodeCounter);	
						wort = (char*) getcontent(aktl_Elem -> content);
						filename = (char*) getfilename(aktl_Elem -> dateiname);		
						printf(" %i", aktl_Elem -> lineCounter);
						
						if(strcasecmp((char*)aktl_Elem ->left-> content, (char*)aktl_Elem ->left-> left-> content) < 0)
						{						
							printf("\n");
						}							
						fprintf(file2,"%i %s %s %i\n", aktl_Elem -> nodeCounter, wort, filename, aktl_Elem -> lineCounter);
						//printf("\nFehler bei fprintf: %i\n\n", h);
					}
					else if (strcasecmp(string1, string2) == 0)
					{
											
						printf(", %i", aktl_Elem -> lineCounter);
						if(strcasecmp((char*) aktl_Elem ->left-> content, (char*)aktl_Elem ->left-> left-> content) < 0)
						{						
							printf("\n");
						}
						fprintf(file2,"%i ", aktl_Elem -> lineCounter);				

					}						
					//!Informationen werden anschließend in eine Textdatei einlesen
					//fprintf(file2,"%i %s %s %i\n", aktl_Elem -> nodeCounter, wort, filename, aktl_Elem -> lineCounter);				
				}											
			}				
		}*/
		//! Wenn der Optionsparameter '-p'gewählt wurde		
		if(strcasecmp(option, "-p") == 0)
		{
			char* string1 = NULL;
			char* string2 = NULL;	
			//! Werden alle Listenelemente abgegangen und ausgegeben				
			while(kopf != (aktl_Elem = aktl_Elem -> left))
			{
								
				char* conttmp1 = (char*) aktl_Elem -> content;		//!Wort des aktuellen Elements
				char* conttmp2 = (char*) aktl_Elem -> left -> content; //!Wort des linken Elements			
				
				sscanf(conttmp1, "%s ",string1);
				sscanf(conttmp2, "%s ",string2);
				//if(strcasecmp(string1, string2) < 0 )
				//{
					//printf(" %i. ", aktl_Elem -> nodeCounter);	
					wort = (char*) getcontent(aktl_Elem -> content);
					//filename = (char*) getfilename(aktl_Elem -> dateiname);		
					//printf(" %i", aktl_Elem -> lineCounter);
					
				//	if(strcasecmp((char*)aktl_Elem ->left-> content, (char*)aktl_Elem ->left-> left-> content) < 0)
				//	{						
						printf("\n");
				//	}							
				//	fprintf(file2,"%i %s %s %i\n", aktl_Elem -> nodeCounter, wort, filename, aktl_Elem -> lineCounter);
					//printf("\nFehler bei fprintf: %i\n\n", h);
				//}
				/*else if (strcasecmp(string1, string2) == 0)
				{
											
					printf(", %i", aktl_Elem -> lineCounter);
					if(strcasecmp((char*) aktl_Elem ->left-> content, (char*)aktl_Elem ->left-> left-> content) < 0)
					{						
						printf("\n");
					}
					fprintf(file2,"%i ", aktl_Elem -> lineCounter);				

				}*/					
				
				//!Informationen werden anschließend in eine Textdatei einlesen
				//fprintf(file2,"%i %s %s %i\n", aktl_Elem -> nodeCounter, wort, filename, aktl_Elem -> lineCounter);											
			}						
		}
		//! Fehlermeldung bei Falscheingabe, vom Anwender.
		else 
		{
			printf("\n Falschen Optionsparameter gewählt!\n\n");
			printf(" Wählen Sie nach Aufruf './Liste' als Optionsparameter '-p' oder '-w=n' aus!\n");
			printf(" - der Optionsparameter '-p' gibt alle Wöter mit der Wordindexnummer aus.\n");
			printf(" - Optionsparameter '-w=n' dient als Schwellenwert für Wortlängen. Für 'n'\n");
			printf("   wählen Sie einen beliebigen Schwellenwert, der größer ist als 1 \n\n");
		}			
	}        		
}

//***************************** Funktionen für Funktionsvariablen *************************
//! Kopieren von übergebenem String
void* copyString(void* stringUebergabe)
{
	//! Wandelt void zeiger in char zeiger
	char* string = (char*) stringUebergabe;
	char* speicher = (char*) malloc(sizeof(node/*strlen(string)+1)*/));
	//Nach Speicherreservierung immer prüfen, ob auch Speicher vorhanden war!!
	if(speicher == NULL)
	{
		printf("\n Programm wurde beendet, da kein Speicher vorhanden ist! \n");
		exit(1);
	}
	//! Kopiert StringUebergabe in Speicher
	strcpy(speicher, string);
	return speicher;	
}
//! Gibt String der Liste auf Konsole aus
void* printContent(void* contentUebergabe)
{
	char* string = (char*) contentUebergabe;	
	printf(" %s ",string);	
	return (void*) string;
}
void* printFilename(void* filenameUebergabe)
{
	char* string = (char*) filenameUebergabe;	
	printf(" %s ",string);	
	return (void*) string;
}

//************************************** Main-Funktion *************************************
int main(int argc, char *argv[])
{		
	node* kopf = (node*) init_Liste(); 	//! init_List() wird dem Kopf der Liste zugewiesen
	node* aktl_Elem = kopf; 		//! Zum Anfang wird aktuelles Element auf kopf gesetzt
	
	//! Definition benötiger variablen um die Texfiles zu öffnen, einzulesen und abzuspeichern
	char buffer[SIZE];
	FILE *file1, *file2;
	char splitt[] = ",;.:!?><#'’+*~´`^?[]{}()/\%&$§=|öäüÖÜÄß¢£¤¥¦§ ¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿¡ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÑÒÓÔÕÖ×ØÙÚÛÜÝÐÞßáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\"\n\0 "; //Definition der Trennzeichen	
	char* wort;
			
	
	if(argc > 1 && argv[1] != NULL && (file2 = fopen(argv[2], "w+")) != NULL && (file1 = fopen(argv[3], "r")) != NULL)
	{	
		int i = 3; //! Startpunkt für Eingabedateien
		while(i < argc)
		{
			int counter = 0;		
			if((file1 = fopen(argv[i], "r")) != NULL)
			{			
				while(fgets(buffer, SIZE, file1) != NULL)
				{					
					counter++; //! Counter für Zeilenanzahl				
					wort = strtok(buffer, splitt);				
							
					while(wort != NULL)
					{
						//! Wort darf nicht mit folgenden Zeichen beginnen!					    	
						if(strncmp(wort,"-",1) != 0 && strncmp(wort,"0",1) != 0 && strncmp(wort,"1",1) != 0
 						&& strncmp(wort,"2",1) != 0 && strncmp(wort,"3",1) != 0 && strncmp(wort,"4",1) != 0
 						&& strncmp(wort,"5",1) != 0 && strncmp(wort,"6",1) != 0 && strncmp(wort,"7",1) != 0
 						&& strncmp(wort,"8",1) != 0 && strncmp(wort,"9",1) != 0)
						{				
							anhaengen(aktl_Elem, wort, argv[i], copyString, counter);
						}
						//! speichert NULL in wort für Abbruchbedingung
						wort = strtok(NULL, splitt);		    				
					}								    			
				}			
				fclose(file1);		
			}		
			i++;
		}
		//! Aufruf sortieren von aktuellem Element
		//sort(aktl_Elem);
		//! Gibt alle Listenelemente aus
		//print_all(aktl_Elem, printContent, printFilename, file2, argv[1]);
		
		fclose(file2); //! Da die äußere While-Schleife evtl. mehrere Eingabetextdateien ließt, soll die geschriebene Datei auch solange offen bleiben.  
	}
	//! Fehlermeldung bei Falscheingabe, vom Anwender.
	else
	{ 
		printf("\n Das Programm wurde mit den falschen Parametern aufgerufen! \n\n");
		printf(" Rufen Sie das Programm folgendermaßen auf:\n");
		printf(" - './Liste <option> <Ausgabedatei> <Eingabedatei>'. \n");
		printf(" - Sie können auch mehrere Eingabedateien übergeben. \n");
		printf("   ->'...<Ausgabedatei> <Eingabedatei1>,..,< Eingabedatei<n> >'. \n\n");
		exit(1);	
	}
	
	//! Freigeben von allokiertem Speicher
	//delete_all(aktl_Elem, aktl_Elem -> nodeCounter);
	free(kopf) ;
	return EXIT_SUCCESS;
}
```


----------



## Matthias Reitinger (13. Juni 2011)

Davicito hat gesagt.:


> Es soll ebent nicht 3 Variablen sein, da es eine Auflage in unserem Kurs ist, generische Variablen zu benutzen. D.H. ein typlosen Container (void) der alles beinhalten soll um so die Wiederverwendbarkeit zwischen den Funktionen zu garantieren.


Wer sagt denn, dass du deswegen deine Daten in einen String kodieren musst? Du kannst doch genauso gut einen Zeiger auf ein struct in deiner Liste speichern, z.B.:

```
typedef struct eintrag
{
    char* wort;
    node* funde;
} eintrag;

typedef struct fund
{
    char* dateiname;
    int zeile;
} fund;
```
Die Einträge speichern dann selber wieder jeweils eine doppelt verkettete Liste mit den Fundstellen den jeweiligen Wortes.

Grüße,
Matthias


----------



## Davicito (13. Juni 2011)

Hi Matthias,

verstehe...als wenn ich das dann generisch umbaue, müsste es bei mir so aussehen?

```
typedef struct node
{
	struct node* right; // Zeiger auf das Nachfolgerelement
	struct node* left;  // Zeiger auf das Vorgängerelement
	void* content; // Für den Dateninhalt	
    node* liste;
} node;
typedef struct liste
{
       char* dateiname;
       int nodeCounter;
       int lineCounter;
} liste;
...
```

oder prauche ich im unteren Struct noch 2 Zeiger? Wenn ja, warum? Hab ja schließlich ein auf sich selbst zeigende Liste. Und wieso brauche in in Zeile 6 noch node* liste;? Und ich vermisse da in Deinem Beispiel die zwei Zeiger der Liste.


----------



## sheel (13. Juni 2011)

Da sollen ja auch keine node left und right rein.
Du hast selber gesagt: Der Sinn der Übung ist, Listenfuntionen (wie anhängen, finden, löschen) zu machen, die unabhängig von den gespeicherten Daten funktionieren. Die Daten also als void*

Die node-struct, die du vorher gehabt hast, passt da perfekt:

```
typedef struct node
{
    struct node* right; // Zeiger auf das Nachfolgerelement
    struct node* left;  // Zeiger auf das Vorgängerelement
    void* content; // Für den Dateninhalt   
} node;
```

Mit Matthias Reitingers Vorschlag kommt dann sowas zusammen:

```
typedef struct node
{
    struct node* right;
    struct node* left;
    void* content; //da kommt eintrag rein
} node;

typedef struct eintrag
{
    char* wort;
    node* funde; //Eine Liste aus Funden
} eintrag;
 
typedef struct fund
{
    char* dateiname;
    int zeile;
} fund;
```

In eintrag und fund wird kein left/right mehr gebraucht, weil das ja schon im node ist.


----------



## Davicito (13. Juni 2011)

ok.... und wenn ich jetzt meine Liste so nach dieser Struktur befüllen will, wie wird das denn jetzt realisiert?

vielleicht so? eintrag -> wort und fund -> dateiname bzw. fund -> zeile
oder muss ich das doch umständlicher von node aus machen ? node -> eintrag -> wort und node -> eintrag -> fund -> zeile.

Hab mit derartigen Strukturen noch nie gearbeitet.

Gruß.


----------



## Matthias Reitinger (13. Juni 2011)

Vielleicht wäre es ganz sinnvoll, wenn du deine doppelt verkettete Liste zuerst mal komplett losgelöst von deiner eigentlichen Problemstellung implementierst. Also einen Satz von Funktionen, die es erlauben, eine Liste zu erzeugen, sie wieder zu zerstören, Elemente einzufügen, Elemente aufzufinden… Diesen Funktionen muss es komplett egal sein, auf was der content-Zeiger verweist.

Andererseits verwendest du dann zur Lösung deines Problems diesen Satz von Funktionen, ohne irgendwas über die genaue Implementierung der Liste wissen zu müssen. Deine Liste ist dann eine „black box“, also eine Kiste mit Knöpfen (Funktionsdeklarationen) drauf, deren interne Verdrahtung (Implementierung) du nicht kennst.

Vielleicht fällt dir die Abstraktion dann leichter. Weil du vorhin Java erwähnt hattest, könntest du dein Problem auch erst mal in Java mit LinkedList modellieren und dann versuchen, es auf C zu übertragen.

Grüße,
Matthias


----------



## Davicito (13. Juni 2011)

ich verstehe nur zum Teil deine Argumentation... das beantwortet nur leider nicht meine frage wie ich das jetzt in meine Liste bekomme. wie würdest du jetzt das Wort, den Dateinamen und die Zeilennummer in die Liste von Sheel oben einfügen bzw. initialisieren? 

Ich hab jetzt schon ne Ewigkeit versucht das alleine hinzu bekommen, aber ich bin zu doof dafür!

Liste.h

```
typedef struct node
{
	struct node* right; // Zeiger auf das Nachfolgerelement
	struct node* left;  // Zeiger auf das Vorgängerelement
	void* content; // Inhalt von contdef => Inhaltsdefinition von content.	
} node;
typedef struct contdef
{
	char* wort;
	node* def;  // Erweiterung für ein wort in der Liste
}contdef;
typedef struct def
{
	char* dateiname;
	char* zeile;
} def;
...
```

Liste.c

```
...
#include "Liste.h"

node* init_Liste()
{
	node* n = (node*) malloc(sizeof(node));
	n -> right   = n;
	n -> left    = n;
	//n -> content = NULL;
	n -> contdef -> wort = NULL;
	n -> dateiname = NULL;
	n -> zeile = NULL;	
	return n;
}
...
node* anhaengen(node* list, void* neuer_String, void* dateiname, void* (*set)(void*), int counter)
{
      node* kopf      = list;
	node* aktl_Elem = list;

	contdef* neu_Elem = (contdef*) malloc(sizeof(contdef));
      if(neu_Elem == NULL)
	{
		printf("\n Listenelement konnte nicht angelegt werden, da kein Speicher vorhanden ist! \n");
		exit(1);
	}

          neu_Elem -> wort = (char*) set(neuer_String);
		neu_Elem -> dateiname = (char*) set(dateiname);
		
		//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
		//! Neues Element zeigt mit linkem Zeiger auf aktuelles Element (Liste)
		neu_Elem -> left       = list;
		//! Neues Element zeigt mit rechtem Zeiger auf rechten Nachbarn des Aktuellen Elements
		neu_Elem -> right      = list -> right;
		//! Von aktuellem Element auf rechtes Zeigen und von rechtem Element auf mit left auf 
       neues Element zeigen
		list -> right-> left   = neu_Elem;
		//! Von aktuellem Element mit right auf neues Element zeigen
		list -> right          = neu_Elem;
}
```

Gruß.


----------



## Matthias Reitinger (13. Juni 2011)

Davicito hat gesagt.:


> Liste.h
> 
> ```
> typedef struct node
> ...


Die Definitionen von contdef und def haben in Liste.h nichts verloren! Die Liste soll ja generisch sein. Wieso hast du den Typ von zeile auf char* geändert? Du willst doch eine Zeilen*nummer* speichern, also passt der *numerische* Datentyp int besser.



Davicito hat gesagt.:


> Liste.c
> 
> ```
> ...
> ...


Wiederum: die Funktion init_Liste soll generisch sein, also nichts davon wissen, welche Elemente in ihr gespeichert werden! Die Funktion sollte z.B. so aussehen:

```
node* init_Liste(void* content)
{
	node* n = malloc(sizeof(node));
	n -> right   = n;
	n -> left    = n;
	n -> content = content;
	return n;
}
```
content kann jetzt alles sein, also konkret in deinem Fall z.B. ein Zeiger auf ein struct contdef oder auf struct def.

Eine Liste mit einem Eintrag legst du dann z.B. so an:

```
char wort[] = "foo";
char dateiname[] = "foo.txt";

def* fund = malloc(sizeof(def));
fund->dateiname = dateiname;
fund->zeile = 42;

contdef* eintrag = malloc(sizeof(contdef));
eintrag->wort = wort;
eintrag->def = init_Liste(fund);

node* liste = init_Liste(eintrag);
```

Glaub es mir einfach: wenn du zuerst deine Liste generisch implementierst und erst dann dein Problem davon losgelöst angehst, tust du dich viel leichter.

Grüße,
Matthias


----------



## Davicito (13. Juni 2011)

ok wenn ich das so mache, scheint er auch nicht mehr beim Kompilieren zu meckern, dass er die Bezeichner wort und dateinamen nicht kennt. aber da ja meine beiden Zeiger im ersten Strukt (node) stehen, findet er diese jetzt nicht mehr.

Fehlerausgabe lautet:
error: ‘contdef’ has no member named ‘node’
wenn ich das so wie in dem Codebeispiel versuche - zeigertechnisch - in meine Liste zu implementieren.

```
...
neu_Elem -> wort = (char*) set(neuer_String);
		neu_Elem -> dateiname = (char*) set(dateiname);
		
		//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
		//! Neues Element zeigt mit linkem Zeiger auf aktuelles Element (Liste)
		neu_Elem -> left       = list;
		//! Neues Element zeigt mit rechtem Zeiger auf rechten Nachbarn des Aktuellen Elements
		neu_Elem -> right      = list -> right;
		//! Von aktuellem Element auf rechtes Zeigen und von rechtem Element auf mit left auf neues Element zeigen
		list -> right-> left   = neu_Elem;
		//! Von aktuellem Element mit right auf neues Element zeigen
		list -> right          = neu_Elem;
...
```

hab das noch so versucht und es geht gut. nur weiß ich nicht, ob das zulässig ist!

```
...
     node* kopf      = list;
	node* aktl_Elem = list;

	contdef* neu_Elem = (contdef*) malloc(sizeof(contdef)); //! Speicher für neuen Knoten besorgen 
	//! Überprüfen, ob Speicher angelegt worden ist	
	if(neu_Elem == NULL)
	{
		printf("\n Listenelement konnte nicht angelegt werden, da kein Speicher vorhanden ist! \n");
		exit(1);
	}
       neu_Elem -> wort = (char*) set(neuer_String);
		neu_Elem -> dateiname = (char*) set(dateiname);

		node* neu_Node = (node*) malloc(sizeof(node));
		//printf("conttmp: %s\n", (char*) (neu_Elem -> content) );
		//! Neues Element zeigt mit linkem Zeiger auf aktuelles Element (Liste)
		neu_Node -> left       = list;
		//! Neues Element zeigt mit rechtem Zeiger auf rechten Nachbarn des Aktuellen Elements
		neu_Node -> right      = list -> right;
		//! Von aktuellem Element auf rechtes Zeigen und von rechtem Element auf mit left auf neues Element zeigen
		list -> right-> left   = neu_Node;
		//! Von aktuellem Element mit right auf neues Element zeigen
		list -> right          = neu_Node;
```

Ich hatte "zeile" als char deklariert, weil zeile mehrere Zeilennummern beherbergen kann und soll. Es soll ja schließlich nur Wörter in die Liste aufgenommen werden, die noch nicht existieren. Also wenn ein Wort aber schon existiert, soll nur dessen Zeilennummer in dem schon existierendem Wort der Liste in "zeilel" hinzugefügt / hinten angehängt werden. Wenn ich also die Variable "zeile" als int deklariere, überschreibe ich ja diesen Wert nur mit der Zeilennummer  des mehrfachen vorkommenden Wortes.

also nochma. Wenn das Wort z.B und in einem Text mehrfach vorkommt schon in der liste steht - also: und Gruß.txt 4 - dann soll nur die Zeile des nächst gefundenen und's in der Variable "zeile" hinten angefügt werden. -> und Gruß.txt 4 8 20 45 70. Mann könnte das bestimmt auch mit einem Array machen. aber dann müsste ich wieder ausreichend Listenelemente vorsehen und würde dann wieder nur unnötig Speicher verschwenden und als "char* zeile;" kann ich da mit strcat einfach in dieser Variablen hinten anfüfen und würde dann nur soviel speicher belegen, wie der String groß ist.

Gruß.


----------



## Matthias Reitinger (13. Juni 2011)

Davicito hat gesagt.:


> Fehlerausgabe lautet:
> error: ‘contdef’ has no member named ‘node’
> wenn ich das so wie in dem Codebeispiel versuche - zeigertechnisch - in meine Liste zu implementieren.
> 
> ...


Du vermischst hier schon wieder die Implementierung der generischen Liste und problemspezifischen Code. Das kann nicht vernünftig funktionieren. Ich versuche noch ein letztes mal damit bei dir durchzukommen: *Implementiere die Liste unabhängig davon, welche Elemente sie speichert.* Also kein wort oder dateiname oder zeile in der Implementierung der Listenfunktionen.



Davicito hat gesagt.:


> Ich hatte "zeile" als char deklariert, weil zeile mehrere Zeilennummern beherbergen kann und soll.


Dann würde sich doch aber ein Array aus ints viel besser eigen!? Abgesehen davon reicht es, genau eine Zeilennummer pro Eintrag speichern, denn:



Davicito hat gesagt.:


> Es soll ja schließlich nur Wörter in die Liste aufgenommen werden, die noch nicht existieren. Also wenn ein Wort aber schon existiert, soll nur dessen Zeilennummer in dem schon existierendem Wort der Liste in "zeilel" hinzugefügt / hinten angehängt werden.


Du hast den vorgeschlagenen Aufbau nicht ganz verstanden. Deine Hauptliste soll für jedes vorgekommene Wort einen Eintrag besitzen. Jeder Eintrag speichert jeweils 1. das Wort und 2. eine Liste mit den Vorkommnissen (Funde) dieses Wortes. Jedes Vorkommnis verzeichnet 1. den Dateinamen und 2. die Zeilennummer.

Beispiel:
foo.txt

```
foo bar
foobar
foo
```

bar.txt

```
foobar
foo
```

Struktur des Ergebnisses:

```
[ # Liste von Einträgen
  { # Eintrag
    wort: "foo",
    funde: [ # Liste von Funden
      { dateiname: "foo.txt", zeile: 1 }, # Fund
      { dateiname: "foo.txt", zeile: 3 }, # Fund
      { dateiname: "bar.txt", zeile: 2 }  # Fund
    ]
  },
  { # Eintrag
    wort: "bar",
    funde: [ # Liste von Funden
      { dateiname: "foo.txt", zeile: 1 } # Fund
    ]
  },
  { # Eintrag
    wort: "foobar",
    funde: [ # Liste von Funden
      { dateiname: "foo.txt", zeile: 2 }, # Fund
      { dateiname: "bar.txt", zeile: 1 }  # Fund
    ]
  }
]
```
So klarer geworden?



Davicito hat gesagt.:


> Mann könnte das bestimmt auch mit einem Array machen. aber dann müsste ich wieder ausreichend Listenelemente vorsehen und würde dann wieder nur unnötig Speicher verschwenden und als "char* zeile;" kann ich da mit strcat einfach in dieser Variablen hinten anfüfen und würde dann nur soviel speicher belegen, wie der String groß ist.


Abgesehen davon, dass das überhaupt nicht nötig ist (s.o.), verkomplizierst du die Sache mit einem String nur. Du kannst nicht einfach strcat aufrufen, ohne vorher entsprechend Speicher reserviert zu haben.

Grüße,
Matthias


----------



## Davicito (13. Juni 2011)

Danke dir Matthias, aber du verwirrst mich nur noch mehr******

Ich geb es auf! ich bau jetzt meine Liste wieder zurück zu einer normalen Liste. Ich hab nähmlich nicht mehr viel Zeit für mein Problem, da ich es übermorgen fertig haben muss.

Ich wollte eigentlich nur gewusst haben, wir Ihr das in einer Funktion, in der nun schon oben dargestellten generischen Liste implementieren würdet. Ich muss ja schließlich auch meiner Fungtion irgendwie mitteilen, wie ich die Zeiger setzten muss um Bestandteil meiner generischen Liste zu sein. Aber Ihr sprecht in Rätseln.
Trotzdem vielen Dank für Eure Bemühungen.

 Gruß.


----------



## Matthias Reitinger (13. Juni 2011)

Hallo Davicito,

tut mir Leid, dass ich mich nicht klarer ausdrücken konnte.

Mein Tipp ist aber immer noch, die allgemein gehaltene Liste mit allen nötigen Operationen (neues Element einfügen, Elemente durchlaufen, etc.) zuerst fertigzustellen. Dabei können wir dir auch gerne helfen.

Besonders wenn du unter Zeitdruck stehst, ist ein strukturiertes Vorgehen zielführender als einfach alles irgendwie zusammenzuwürfeln.

Grüße,
Matthias


----------

