• Hallo liebe Userinnen und User,

    nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.

    Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance.
    Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.

    Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.


    Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.

[Projektvorstellung] Starlight - C XML zu JSON Konverter, bitte um Feedback

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
Hallo,

ich hatte es ja in einem anderen Thread schon durchblicken lassen das ich an einem, XML zu JSON Konverter, in C, arbeite. Der Grund warum ich hier schreibe, ich würde es gerne von euch testen lassen und mir auch Verbesserungsvorschläge einholen wie man das Projekt verbessern kann.

Das Projekt liegt derzeit nur als C Sourcecode auf Github und muß kompiliert werden, eine fertige Exe wollte ich nicht verbreiten, wäre aber bei Bedarf nachzuliefern von mir, das nur kurz voher eingebracht.

Ein kurzer Überblick was funktioniert:
Parsen eine einfachen Test XML Datei und Datei-Ausgabe als JSON

Was nicht funktioniert*:
Wenn eine XML Datei eingebettete Tags verwendet, Beispiel:
[src=xml]<description><bold>Überschrift Produkt 1</bold><keywords>Klein Bunt Komisch</keywords></description>[/src]
damit kommt der Parser aktuell nicht klar weil Zeichen für Zeichen eingelesen wird und ich noch keinen Weg gefunden habe das zu umgehen.

* Dies ist in Version 2 nicht mehr der Fall und auch nested tags werden vom Parser unterstützt.

Die Handhabung erfolgt über die Befehlszeile, mit folgenden Befehlen:
"-v" = Verbose/etwas Status ausgeben
"-i" = Eingabe-Datei (Beispiel: "-iSample.xml")
"-o"= Ausgabe-Datei (Beispiel "-oAusgabe.json", ohne diesen Parameter ist der Standard "output.json" im Programmverzeichniss)

Zum Beispiel: "starlight.exe -v -iMeine.xml -oAusgabe.json"

Das Projekt auf Github mit "Makefile, C Code, Sample.xml":
https://github.com/jrie/starlight

Kompiliert werden kann mit gcc und folgenden Parametern wer nicht das Makefile verwenden will:
gcc starlight.c -std=c99 -lm -o starlight[.exe] (Dateiendung nur unter Windows)


Feedback zum Code wie auch zum Funktionsumfang sind natürlich willkommen, was mir natürlich aber auch wichtig wäre das Testen des Parsers und Ausgabemoduls mit mehr Beispieldaten. Im Netz habe ich leider nicht viel dazu gefunden, das ist auch ein Grund warum ich mich hier an euch wende.

Die Beispiel XML ist von Microsoft von MSDN.

Sollte eine XML nicht konvertiert werden können, obwohl keine Tags enthalten sind, würde ich mich freuen wenn ihr mir einen kleinen aber kompletten Ausschnitt dieser XML zeigen könnten, zum Beispiel via PN wenn die Daten diskret sind. Diese werden auch nicht von mir veröffentlicht.

Vielen Dank schon einmal fürs Lesen des Beitrages!


Anmerkungen zum Code und Feedback das ich derzeit noch nicht berücksichtigt habe da das Prototyping einer funktionstüchtigen Version im Vordergrund standen:
- Funktionen zu verwenden wo sinnvoll (fast alles in der Main)..
- Vermeidung von globalen Variablen
- Speicher für Puffer/Daten vorher zu reservieren und keine reallocs mit einzelnen Byte(s) zu machen
 
Zuletzt bearbeitet:

drfuture

Zeitreisender
Teammitglied

Registriert
14 Juli 2013
Beiträge
8.757
Ort
in der Zukunft
Servus theSplit,
ich habe mir den Code nun nicht angeschaut (vor allem da ich eh kein C kann :D)
Aber die erste Idee die mir bei "jedes Zeichen durchgehen" gekommen ist - Regex dazu zu verwenden.
Ich würde über Regex-suchen eine Art Tag-Baum aufbauen und den Baum dann in json raus schreiben - XML ist ja eh eine Hirarchische Baumstruktur...
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #3
Hi,

danke für deine Idee. Soweit ich die Standardbibliothek richtig deute gibt es aber kein Regex in C von Hause aus. Daher auch mein Versuch das Zeichenbasierend bzw. vorher mit einer gelesenen Zeile aus der XML zu lösen.

Hab das auch nochmal kurz gegoogelt:
Hier ein Thread dazu auf Stackoverflow - hier wird von anderen Bibliotheken gesprochen aber beim ersten Punkt steht auch "ANSI C besitzt kein eingebautes Regex". Ansonsten ist die Idee natürlich gut das Konvertierungsproblem so anzugehen.

Das Problem das ich mit einer zeilenbasierenden Lösung (Einlesen aus der Datei) hatte, auch mit der Testdatei, das die Tags bzw. Daten nicht zwingend in einer Zeile in der XML stehen/enden müssen, sondern sich auch über mehrere Zeilen erstrecken können. Aber mit Regex könnte man das "Closing" des Tags im gesamten Daten-String suchen - stimmt schon. Eventuell würde es auch Sinn machen das Parsing innerhalb des gesamten Dateiinhalts vorzunehmen, damit man leichter identifizieren kann was zusammen gehört und ein Ast mit Blättern bildet. :)

Also um bei der Beispieldatei zu bleiben:
Den Datenteil zwischen <book> und </book> ermitteln, diesen näher untersuchen ob weitere Tags enthalten sind, dann diese nach und nach herunterbrechen. Und als Einträge innerhalb des Tags "book" definieren. Wäre jetzt meine Idee spontan wie man es lösen könnte. Dann den gelesen Teil aus dem String entfernen bzw. zum nächsten Eintrag wandern.

PS: Ich hab nochmal einen kleinen "Bug" das der Tab-Charakter "\t" nicht mit verarbeitet wird wenn es um Tags oder Daten geht. So konnte ich noch zwei weitere Testdaten richtig kodieren.

An einer scheitert der Parser aber noch weil "zu komplex":
http://www.service-architecture.com...nted-databases/xml_file_for_complex_data.html
 
Zuletzt bearbeitet:

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
Aber mit Regex könnte man das "Closing" des Tags im gesamten Daten-String suchen - stimmt schon.
Das Problem daran ist, dass du das richtige Closing-Tag finden musst, nicht nur irgend eines. Beispiel:
Code:
<div>
    <div>
        <p>Hello, world!</p>
    </div>
</div>
<div>
    <p>Don’t panic!</p>
</div>
Angenommen, du hast gerade das oberste <div> gefunden. Jetzt einfach nach dem nächsten </div> zu suchen, geht offensichtlich schief. Du müsstest eine Regex bauen, die den vollständigen theoretisch möglichen Inhalt des ersten Divs erfasst, inkl. Matching aller Start-/Endtags darin. Dazu kommen leere Tags, z.B. ein leerer Div: <div/>. Nur dann kannst du wissen, dass das gefundene </div> auch das richtige ist. Mit Regex kriegst du das, wenn überhaupt, definitiv nicht fehlerlos und schon gar nicht wartbar hin.

Grundproblem ist, dass ein XML-Parser State mitführen muss, eben wegen dem Tag-Matching. Und Regex kann nur sehr eingeschränkt mit State umgehen. Sinnvollerweise könntest du Regex höchstens anwenden, um ein beliebiges nächstes Tag im Zeichenstrom zu finden. Das wäre sowas wie
Code:
(?:(<\/*(\w+)>)|(<(\w+)\s*\/>))
um öffnende, schließende und leere Tags zu finden.
 

fryk

Guest

F
Ja, man soll nicht mit Regex an HTML/XML rangehen. Auf Stackoverflow gibt das regelmäßig Ärger.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #6
@Brother John:

Was ich gerade mache um etwas State mitzuführen, offene Tags werden gesammelt/gespeichert und erst dann geschlossen wenn ein Tag wirklich geschlossen wird. Was die Logik aber bisher nicht beinhaltet - wenn der Opening Tag gleich dem eingebundenen Kind Tag ist, oder um es im Code auszudrücken - der "previousTag" (letzter erkannter Tag) gleich dem "currentTag" (aktuell gefundener Tag) ist.

An dieser Stelle könnte man so argumentieren, gibt es das Array Tag "book" und sind dort mehrere Tags mit "book" enthalten.
So das wir in JSON auf das das hier kommen würden:

"book" : [{
"book": {
"id": 12,
"title": "A lost world"
},
"book": {
"id": 14,
"title": "Jurassic Park"
}
}]

Nun gut, so unwahrscheinlich wäre es auch nicht. Also sollte man den Fall schon mit einkreisen, der Vollständigkeit halber schon, stimmt.

Eventuell kann ich den Parser so umschreiben das ein Tag mit gleichem Namen innerhalb des "Elternelements" nicht auf Gleichheit geprüft wird.

In XML sehe das ganze ja noch Problematischer aus, wie dein HTML Beispiel.
<book><book id=12><title>A lost world</title></book><book id=14><title>Jurassic Park</title></book></book>

Aber von der herangehensweise "reicht" es, meiner Meinung nach die Openings zu sammeln und davon abzuarbeiteten bzw. von oben nach unten zu schließen sobald ein Closing-Tag gefunden wird - so wie es aktuell der Fall ist. Das setzt natürlich aber auch voraus das die XML wohl geformt ist und nichts ungeschlossen ist, sonst kommt der Datengau. Aber hier könnte man einen Fall Back schreiben wenn der tag "book" in "book" nach einem DatenTag nicht mehr geschlossen wird, aber neu geöffnet werden soll. So als Idee.

Außerdem habe ich in meinem Code einen "tabIndex", was die "höhe" wiederspiegelt - ein Tag <book> auf "tabIndex 1" (Wurzel) ist nicht gleich dem Tag <book> auf "tabIndex" 2 (möglicher Kindeintrag) auch bei der Auswertung was ein Array ist und was ein Array-Element ist.

Jedoch ist die Verschachtelung gerade noch nicht ganz korrekt und ein Fall-Back fehlt gänzlich.
 

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
Aber hier könnte man einen Fall Back schreiben wenn der tag "book" in "book" nach einem DatenTag nicht mehr geschlossen wird, aber neu geöffnet werden soll. So als Idee.
Genau. HTML macht solche Sachen. Z.B. darf man bei Listenpunkten das schließende </li> weglassen. Das ist dann zwar kein XML mehr mehr, aber dein Datenformat kannst du dir natürlich völlig frei definieren. Nur solltest du den Parser dann nicht mehr XML-Parser nennen. ;)

Eventuell kann ich den Parser so umschreiben das ein Tag mit gleichem Namen innerhalb des "Elternelements" nicht auf Gleichheit geprüft wird.
Als Ideen dazu: Du brauchst den Pfad vom XML-Wurzelknoten bis zu deinem gerade aktiven Knoten. Das reicht fürs Matching schon aus. Woher du den Pfad kriegst, kommt darauf an, ob du deinen Parser baumorientiert (Stichwort: DOM-Parser) oder streamorientiert (Stichwort: SAX-Parser) anlegst.

Baumorientiert baust du dir im RAM sowieso die Hierarchie aus der kompletten XML als Hierarchie von Knoten-Objekten nach. Da du zu jedem Zeitpunkt weißt, welches der gerade aktive Knoten ist, hast du alle nötigen Informationen. Kommt jetzt ein öffnender Tag vorbei, fügst du deinem aktuellen Knoten einfach ein entsprechendes neues Kind hinzu, und dieses wird zum aktiven Knoten. Kommt ein schließender Tag vorbei, prüfst du, ob der zum aktiven Knoten passt. Wenn nicht: rumms! Wenn ja, wird der Elternknoten des aktiven Knotens aktiv und weiter gehts.

Streamorientiert ist das nicht ganz so hübsch. Du liest ja nur sequentiell den Datenstrom und gibst dem Nutzer des Parsers diverse Anknüpfpunkte – oder hast die fest verdrahtet drin, weil du genau weißt, dass du eh nur Json generieren willst. Eine recht einfache Lösung ist es, den Pfad zum aktuellen Knoten als Stack mitpflegen. Der Name jedes öffnenden Tags kommt auf den Stack und jeder schließende Tag räumt – falls er passt – das letzte Element vom Stack wieder ab. Damit hast du genau das nachgebildet, was dir DOM durch den Knotenbaum auch liefert.

Soweit ich das überblicke, benutzt du deinen tabIndex als simple Variante eines Stacks, um Ebenen zu zählen. Was dir fehlt ist die Info, von welchem »Typ« jede Ebene ist.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #8
Spontan würde ich wohl einen DOM-Parser erstellen wollen, allerdings fehlt hier - wenn ich das Konzept richtig nachvollziehe, jegliche Struktur Knoten mit anderen Knoten bzw. Kind Elementen zu verknüpfen. Also um es etwas in C auszudrücken Knoten Struct mit Pointern auf Kind und einem möglichen Elternknoten, so wie auch Pointer auf Kindknoten/Tags wenn vorhanden. Das würde auch einige Variablen wegoptimieren da man die Tiefe beim Traversing der Knoten ermitteln könnte bzw. wie weit ein Element vom Hauptknoten entfernt ist, welcher immer einen Tabindex von 1 hat oder ob ein Tag Kinder besitzt. Das muss ich mir mal durch den Kopf gehen lassen wie einfach oder schwer das wird - ich vermute ich darf viel umschreiben weil die aktuelle Struktur das nicht ganz unterstützen kann. Muss testen. :)

Zu deiner Beobachtung, der Tabindex im struct Tag ist quasi der Tiefe der gefundenen offenen Tags + 1, wird ein Tag geschlossen wird er auch vom dem Stack der offenen Tags entfernt (wie in deiner Idee) - das was ich heute morgen mit book in book meinte - zur Zeit erfolgt noch eine Überprüfung das Book auch wirklich mit "/book" geschlossen wird. Im Parser Code die strcmp Funktionen auf 279 und 296. Es wird nicht klar festgelegt das der "/" Charakter ein "Closing Tag" eingeleitet hat, das müsste ich definitiv schonmal noch ergänzen. Wenn der Tag nämlich der gleiche ist, aber es sich dabei nicht um eine geschlossenen Tag handelt, ist der Tag logischerweise, trotz gleichen Namens, nicht geschlossen sondern hat ein Kind-Element.

Der eigentliche Stack, auf dem die ganzen Tags gesammelt liegen ist die "tagList" (ein Array von Tag-Elementen).

Um den Typ eines Knotens zu ermitteln besitzt jeder Tag folgenden Variablen "isDataTag" (wenn wir Daten/einen String innerhalb des Tags "entdeckt" haben) true.
Die Datenlänge (tag.dataLength) - also besitzt der Tag eigene Daten wie name, id usw. (siehe sample.xml) und zu guter letzt,
"tag.entrieSize" - also die Anzahl der Dateneinträge in einem Knoten (Tag opening, wenn ein Tag mit Daten* folgt +1 bei den Entries, usw. , bis zum Tag Closing).
ein Tag mit Daten ist ein Tag der ohne Umschweife geöffnet und geschlossen wird - also ohne vermischt zu sein.

Die boolschen Variablens/Flags "tag.isArrayed" oder "tag.isClosing" (bool) zu setzen findet erst nach vollständigem einlesen der Daten in einer extra Schleife, in den Zeilen 516 bis 540 statt.

Ich hoffe die Erklärungen erleichtern das Verständnis was genau passiert. :)


*Edit:
Ich überarbeite die Anwendung heute, angefangen mit der Kernparser zum Einlesen der XML und zum identifizieren von Tags.

Edit2:
Hier der neue Parser, eigentlich nicht viel Unterschied zur Vorhergehensweise, die Herangehensweise ist die gleiche... was fehlt ist dass anlegen der Daten im RAM und es werden noch keine Aussagen über die geparsten Daten (DatenTag, Array Item) gemacht.

[src=c]// Import c header files
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

// Structs

struct keyValue {
char* key;
char* value;
};

struct tag {
bool isClosed;
bool isDataTag;

unsigned int tabIndex;
unsigned int entrieSize;

int parentIndex;
unsigned int* childIndexes;

char* name;
char* tagData;

struct keyValue* keyValues;
};

struct tag* tagList;
unsigned int tagCount = 0;

// Function declarations
void cleanupApplication();
char* createTabString(char* tabString, const unsigned int tabIndex);

// Variables
char* sourceFilePath = NULL;
char* outputFilePath = NULL;

// Main

int main(int argc, char* argv[]) {
char* arg = (char*) malloc(sizeof (char) * 3);
char* value = NULL;

atexit(cleanupApplication);

/* Comment me out for debugging*/
//sourceFilePath = (char*) calloc(11, sizeof (char));
//strcpy(sourceFilePath, "sample.xml");
//sourceFilePath[10] = '\0';

bool showOutput = false;

// Parse the commands given
for (int i = 1; i < argc; i++) {
if (strlen(argv) > 1) {
strncpy(arg, argv, 2);
arg[3] = '\0';
argv += 2;

if (value == NULL) {
value = (char*) calloc(strlen(argv) + 1, sizeof (char));
} else {
value = (char*) realloc(value, sizeof (char) * (strlen(argv) + 1));
}
strcpy(value, argv);
value[ strlen(argv) ] = '\0';

switch (arg[1]) {
case 'i':
sourceFilePath = (char*) malloc(sizeof (char) * (strlen(value) + 1));
strcpy(sourceFilePath, value);
printf("Source: %s\n", value);
break;
case 'o':
outputFilePath = (char*) malloc(sizeof (char) * (strlen(value) + 1));
strcpy(outputFilePath, value);
printf("Output: %s\n", value);
break;
case 'v':
showOutput = true;
printf("Showing output...\n");
break;
default:
break;
}
}
}

if (arg != NULL) {
free(arg);
}

if (value != NULL) {
free(value);
}


// Reading the source file
if (sourceFilePath == NULL) {
printf("No input file specified...\n");
exit(0);
} else {
if (outputFilePath == NULL) {
printf("Default output file used: \"output.json\"\n");
outputFilePath = (char*) realloc(outputFilePath, sizeof (char)*(strlen("output.json") + 1));
strcpy(outputFilePath, "output.json");
outputFilePath[strlen("output.json")] = '\0';
}

unsigned int bufferLength = 0;

// Get and create a buffer for the source data
FILE* sourceFile;

sourceFile = fopen(sourceFilePath, "r");
if (sourceFile == NULL) {
printf("Error opening input file.\n");
exit(1);
} else {
fseek(sourceFile, 0, SEEK_END);
bufferLength = ftell(sourceFile);
rewind(sourceFile);

// Read in the data from the source file, filtering only special
// characters like newline (unix/win) and tab characters

bool hasOpenTag = false;
bool isInsideTag = false;

bool insideKeyValue = false;
bool createKeyValue = false;
bool insideValue = false;

bool ignoreTag = false;

bool closingFollowing = false;


char letter = '\0';

char* currentTag = (char*) malloc(sizeof (char) * 255);
char* previousTag = (char*) malloc(sizeof (char) * 255);

char* key = (char*) malloc(sizeof (char) * 255);
char* value = (char*) malloc(sizeof (char) * 255);

char* tagData = (char*) malloc(sizeof (char) * bufferLength);

unsigned int tagWritePos = 0, keyWritePos = 0, valueWritePos = 0, dataWritePos = 0;
int parentIndex = 0, currentIndex = 0;

while (!feof(sourceFile)) {
letter = fgetc(sourceFile);

switch (letter) {
case '?':
case '!':
if (hasOpenTag) {
ignoreTag = true;
hasOpenTag = false;
}
break;
case '\t':
case '\r':
case '\n':
continue;
break;
case '/':
if (hasOpenTag) {
closingFollowing = true;
//continue // Skip reading this character while this is a closing tag
}
break;
case '=':
if (createKeyValue) {
insideKeyValue = true;
key[keyWritePos] = '\0';
valueWritePos = 0;
continue;
}
break;
case '\"':
if (insideValue) {
insideKeyValue = false;
insideValue = false;
// Create the key value for tag
key[keyWritePos] = '\0';
value[valueWritePos] = '\0';

printf("Adding KV: %s | %s\n", key, value);


continue;
}

if (insideKeyValue) {
insideValue = true;
continue;
}
break;
case ' ':
if (ignoreTag) {
continue;
}

if (!insideValue) {

if (insideKeyValue) {
createKeyValue = true;
insideKeyValue = false;

// Create the key value for tag
key[keyWritePos] = '\0';
value[valueWritePos] = '\0';

printf("Adding keyValue: %s : %s\n", key, value);

keyWritePos = 0;
valueWritePos = 0;

continue;
}

if (hasOpenTag) {
hasOpenTag = false;
createKeyValue = true;

// Create the tag
currentTag[tagWritePos] = '\0';
printf("Adding tag: %s\n", currentTag);

tagWritePos = 0;
continue;
}

keyWritePos = 0;
}
break;
case '<':
if (isInsideTag) {
strcpy(previousTag, currentTag);
previousTag[tagWritePos] = '\0';
}

if (!hasOpenTag) {
if (strlen(tagData) > 1) {
tagData[dataWritePos] = '\0';
printf("TagData is: %s\n", tagData);
tagData[0] = '\0';
}

hasOpenTag = true;
tagWritePos = 0;
keyWritePos = 0;
valueWritePos = 0;
dataWritePos = 0;
continue;
}
break;
case '>':
if (ignoreTag) {
tagWritePos = 0;
ignoreTag = false;
continue;
}

if (hasOpenTag) {
// Create the tag on the stack
currentTag[tagWritePos] = '\0';
printf("Adding tag: %s\n", currentTag);
isInsideTag = true;
hasOpenTag = false;
createKeyValue = false;

tagWritePos = 0;
dataWritePos = 0;
continue;
} else if (insideKeyValue) {
// End key value creation
key[keyWritePos] = '\0';
value[valueWritePos] = '\0';

printf("Adding keyValue: %s : %s\n", key, value);
isInsideTag = true;
createKeyValue = false;

keyWritePos = 0;
valueWritePos = 0;
dataWritePos = 0;
continue;
}
default:
break;
}

if (hasOpenTag) {
currentTag[tagWritePos] = letter;
++tagWritePos;
} else if (createKeyValue) {
if (!insideKeyValue) {
key[keyWritePos] = letter;
++keyWritePos;
} else {
value[valueWritePos] = letter;
++valueWritePos;
}
} else if (isInsideTag) {
tagData[dataWritePos] = letter;
++dataWritePos;
}

}

fclose(sourceFile);

if (currentTag != NULL) {
free(currentTag);
}

if (previousTag != NULL) {
free(previousTag);
}

if (key != NULL) {
free(key);
}

if (value != NULL) {
free(value);
}
}

}
}

// Create and return the tabString

char* createTabString(char* tabString, const unsigned int tabIndex) {
unsigned int current = 0;

tabString = (char*) realloc(tabString, sizeof (char)*(tabIndex + 1));

while (current < tabIndex) {
tabString[current] = '\t';
++current;
}

tabString[tabIndex] = '\0';

return tabString;
}


// Clean up by freeing ressources

void cleanupApplication() {
free(sourceFilePath);
free(outputFilePath);

printf("Job done. Exiting. Bye bye...\n\n");
}

[/src]

Zum Testen habe ich mehrere XML Dateien verwendet, aber Hauptaugenmerk liegt hierauf:
http://www.service-architecture.com...nted-databases/xml_file_for_complex_data.html
 
Zuletzt bearbeitet:

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
Also um es etwas in C auszudrücken Knoten Struct mit Pointern auf Kind und einem möglichen Elternknoten, so wie auch Pointer auf Kindknoten/Tags wenn vorhanden.
Ganz genau genommen reichen entweder Links auf den Parentknoten oder eine Liste mit Links auf die Kindknoten schon aus. Macht halt das Ablaufen des Baums in die unverlinkte Richtung laufzeit-langsam und nervig zu coden. Deswegen haben praktisch alle Baum-Implementierungen Eltern- und Kindlinks.

Zu deinem Edit2:
Parent und Kinder würde ich als tag* und tag*[] modellieren. Das hat den Vorteil, dass du auf Eltern/Kinder direkt zugreifen kannst, ohne erst den Index dereferenzieren zu müssen. Außerdem tust du dir leichter, wenn du einen bestehenden Baum auch verändern willst. Beim Löschen/Einfügen musst du dich ums Anpassen der Indexe kümmern, Pointer bleiben gleich.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #10
Hallo,

ich nun so weit gekommen das der Code nun auch eingebettete Tags herauslöst und die Daten die außerhalb des Tags kommen an die Daten des ersten Tags anhängen. Und noch andere Verbesserungen des Parsers bzw. Korrekturen.

Jetzt bin ich an einem Punkt gekommen wo ich gerne auf die "Kind"-Elemente (was Adressen/Pointer sein sollten) gekommen bin.

Das Struct sieht wie folgt aus:
[src=c]typedef struct tag {
bool isClosed;
bool isDataTag;

unsigned int tabIndex;
unsigned int entrieSize;
unsigned int keyValueLength;

char* name;
char* tagData;

struct keyValue* keyValues;

struct tag* parent;
struct tag** children;
} tag;[/src]


Das Funktioniert auch alles soweit richtig, doch kann ich nicht auf "Tag[0].children[index]->name" zugreifen und erhalte einen Speicherzugriffsfehler bei der Ausgabe mit printf, das sieht wie folgt aus:
[src=c]for (int j = 0; j < tagList.entrieSize; j++) {
printf("%s", &tagList.children[j]->name);
}[/src]

Jetzt meine Frage, ich habe mit dem doppelten "Pointer" "tag** children" mit Sicherheit schon einen Fehler, obwohl das Speicherzuweisen und Elementzuweisen mit Adressoperator wie folgt funktioniert:
[src=c]tagList[openTags[openTagCount - 1]].children = (tag**) realloc(tagList[openTags[openTagCount - 1]].children, sizeof (tag**) * (tagList[openTags[openTagCount - 1]].entrieSize + 1));
tagList[openTags[openTagCount - 1]].children[tagList[openTags[openTagCount - 1]].entrieSize] = &tagList[tagCount - 1];[/src]

OpenTagCount verweist immer auf das letzte geöffnete Element vor dem Tag.


Ich stehe gerade auf dem Schlauch bzw. weiß ich schon das meine Deklaration falsch ist, aber ich dachte es würde so funktionieren, tut es aber nicht, ich kann nicht auf die Werte der Adressen der Pointer zugreifen, die Adressen werden allerdings mit
[src=c]prinf("%p\n", tagList.children[j]);[/src]
angezeigt :unknown:

Daher die Frage, wie kann ich die Deklaration eines dynamisch erweiterbaren Arrays von Adressen in C realisieren (wie es Bruder John in seinem Kommentar zu Edit 2 vorschlägt?) ?
Die Definition im Struct mit
struct tag* children[]

Schlägt nicht fehl, aber ich bekomme bei diesem dynamischen Array Datensalat bei der Ausgabe der Daten und Taginhalte weil die Werte ("überschrieben"?) worden sind durch das C flexible Array?

Über Hilfe würde ich mich freuen.

Der aktuelle Code des Projekts ist nach wie vor auf Github, wer sich einen Überblick machen will:
https://github.com/jrie/starlight
 
Zuletzt bearbeitet:

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
Puh! Bei der ganzen Pointerei und Allokiererei und Abräumerei kann einem ja ganz schwindelig werden. Ich blicke jedenfalls nicht mehr so ganz durch und bin sooo (!) froh über C++, wo dieses ganze Zeug einfach da ist und geht, ohne dass man irgendwas machen muss. SCNR, nachdem ich mir den C++-Hinweis so lang verkniffen hab. :D

OK, zum Thema. Das mit dem Durchblicken ist vielleicht schon ein guter Ansatz. Ich habe das Gefühl, dass auch dir zwischen den Anforderungen deiner Domäne (XML->JSON-Konvertierung), den Anforderungen deiner gewählten Datenstruktur (Baum) und diversen Implementierungsdetails ein bisschen die Übersicht verloren geht.

Wenn ich auf Github reinschaue, fällt mir gleich die Spaghetti-main() ins Auge. Brich das Ding auf in kleine Funktionen, die genau eine und nur eine Funktion erfüllen. Das wird die Übersicht massiv steigern, weil du dann zum einen kleine Codeblöcke hast, über die du dir unabhängig vom ganzen Rest Gedanken machen kannst. Und es steht an den Blöcken sogar noch dran, welchen Sinn sie haben (die Funktionsnamen). :)

Ich probier das mal als Beispiel grob für den Baum. Aufs Wesentliche runtergebrochen schaut deine Datenstruktur so aus:

Code:
struct TreeNode {
    TreeNode* parent;     // Zeiger auf eine *einzelne* TreeNode
    TreeNode** children;  // Zeiger auf das 1. Element eines dynamisch
                          // allokierten Arrays von Pointern auf TreeNode.
    size_t child_count;
    
    // ... irgendwelches Detailzeugs; erstmal unwichtig ...
}



Die Struktur hat Vor- und Nachteile, aber lassen wir die mal beiseite. … Was machst du mit so einer Node aus Ressourcenmanagementsicht? Irgendwann wird sie erzeugt, lebt dann vor sich hin, ändert ein paarmal die Anzahl ihrer Kinder und stirbt letztendlich. Also würde ich schonmal drei Funktionen bauen:

Code:
TreeNode* create_node();
void destroy_node(TreeNode* node);
void resize_node(TreeNode* node, size_t new_size);

Die zu implementieren, sieht schon recht übersichtlich aus. Und die TDD-Anhänger fangen zu sabbern an, weil das Resmgt erfüllt den heiligen Gral der unabhängigen Testbarkeit. ;)

Daher die Frage, wie kann ich die Deklaration eines dynamisch erweiterbaren Arrays von Adressen in C realisieren (wie es Bruder John in seinem Kommentar zu Edit 2 vorschlägt?) ?
Die Definition im Struct mit
struct tag* children[]
Ganz konkret kanns da durchaus passiert sein, dass mir C++ reingerutscht ist. Ich bin halt nur fortgeschrittener C-Laie. In C++ ist *[] äquivalent zu **, vielleicht gilt das für C nicht so ganz. :unknown:
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #12
Vielen Dank für das rasche Posting, ich geb dir auch Recht, ich könnte die Main und speziell das Erstellen und Freeing einer Node/Tag in Funktionen unterteilen.
Genauso wie die KeyValue Erstellung und auch den gesamten ParserCode aus der Main lösen.

Das sind allerdings noch Sachen, die ich mir vorgenommen habe zu erledigen, sobald ich weiß das der Parser soweit funktioniert, momentan hat meine Faulheit, den Code einfach runterzuschreiben wo ich Ihn brauche funktioniert... vor allem weil ich immer Daten geändert habe, mußte das natürlich dann aber auch an zwei drei Stellen dann gleich tun, damit alles funktionierte. Also eher Suboptimal und Fehleranfällig wenn man das Struct ändert, aber Werte vergisst zu setzen... :)

Ich hab den Code auf Github nochmal ein wenig angepasst, was einen Fehler entfernt hat das nur Daten-Tags als Child-Tags gesetzt worden sind.

Der Code, etwas entschlackt, der jetzt ein Kind Element hinzufügen soll - als Pointer/Adresse:
[src=c]// Add child tag
unsigned int tagIndex = openTags[openTagCount - 1]; // Index des letzten geöffneten Tags
tagList[tagIndex].children = (tag**) realloc(tagList[tagIndex].children, sizeof (tag**) * (tagList[tagIndex].entrieSize + 1));
tagList[tagIndex].children[tagList[tagIndex].entrieSize] = &tagList[tagCount];

printf("tagchild: %s\n", tagList[tagIndex].children[tagList[tagIndex].entrieSize]->name);[/src]

Warum ich das nochmal poste, weil der Printout des Namens des Kind Elements in diesem Aufruf funktioniert - kein Problem hier.

Würde ich jetzt folgenden Code später schreiben, funktioniert das nicht mehr (resultiert in einen Speicherfehler):
[src=c]printf("tag child name: %s\n", tagList[1].children[0]->name); // Attribut Name des Kinds ausgeben[/src]

Die TagList wird weder free(d) noch ist sie Out of scope, das irritiert mich gerade.

Kann das durch das Reallokieren kommen, das die Speicherstellen verschoben wurden sind und keine gültigen Daten mehr enthalten?
Sollte ich doch vielleicht mal integer Indexe testen?

Edit2:

Habe nun beide Varianten im Quellcode online, einmal werden die Tags als Adressen nach "//Add child tag**" gesetzt, bei zweiter Variante als Integer Indexe.
Letzte Funktioniert, die erste bekomme ich so nicht zum laufen.

Edit3:

Ich habe eben die Probe aufs Exempel gemacht ob die Speicheradressen der Pointer und Integer Verweise gleich sind, Ergebiss bei mir: Teilweise... daher auch das Abstürzen des Programms:
Die neue Variante ist auf Github.

Wie man sieht steht da unter anderem folgendes:
[src=c]Children
0x6f67657461636e69 address pointer - 0x1bb5070 address index - listitem
0x1bb51e0 address pointer - 0x1bb51e0 address index - listitem[/src]

Beide zeigen auf jeweils zwei Elemente dessen Name "listitem" ist (2 Kindelemente) und an der Adresse Index liegen soll, wie man sieht habe ich aber für den Pointer irgend einen Abstrußen wert!

Die Ausgabe erfolgt so:
[src=c]printf("Children\n");
for (int j = 0; j < tagList.entrieSize; j++) {
printf("%p address pointer - %p address index - %s\n", tagList.childrenPointer[j]->name, tagList[tagList.children[j]].name, tagList[tagList.children[j]].name);
//printf("%s\n", tagList.children[j]->name);
}[/src]


Hier mal die gesamte Ausgabe des Programms unter Debian:
[src=c]jan@debian:~/Schreibtisch/starlight$ ./starlight2 -istandard_small.xml -v
Source: standard_small.xml
Showing output...
Default output file used: "output.json"


Print out summary:
site - 0 index, 0 keyValues, 1 entries

Children
0x1bb4de0 address pointer - 0x1bb4de0 address index - regions

regions - 1 index, 0 keyValues, 1 entries

Children
0x1bb4d50 address pointer - 0x1bb4d50 address index - africa

africa - 2 index, 0 keyValues, 2 entries

Children
0x1bb4db0 address pointer - 0x1bb4db0 address index - item
0x2074757020646563 address pointer - 0x1bb5b80 address index - item

item - 3 index, 1 keyValues, 12 entries
KV: id : item0

Children
0x1bb5510 address pointer - 0x1bb4e40 address index - location
0x64726f7779656b address pointer - 0x1bb4ec0 address index - quantity
0x7f390000000c address pointer - 0x1bb4f00 address index - name
0x61206e6f6974636e address pointer - 0x1bb5200 address index - payment
0x6f67657461636e69 address pointer - 0x1bb4ff0 address index - description
0x7470697263736564 address pointer - 0x1bb5360 address index - shipping
0x30 address pointer - 0x1bb5400 address index - incategory
0x1bb5460 address pointer - 0x1bb5460 address index - incategory
0x664f2063 address pointer - 0x1bb54e0 address index - incategory
0x1bb5b60 address pointer - 0x1bb5b60 address index - incategory
0x1bb5380 address pointer - 0x1bb5380 address index - incategory
0x1bb6e60 address pointer - 0x1bb5690 address index - mailbox

location - 4 index, 0 keyValues, 0 entries
Data: United States

quantity - 4 index, 0 keyValues, 0 entries
Data: 1

name - 4 index, 0 keyValues, 0 entries
Data: duteous nine eighteen

payment - 4 index, 0 keyValues, 0 entries
Data: Creditcard

description - 4 index, 0 keyValues, 1 entries

Children
0x79726f6765746163 address pointer - 0x1bb5010 address index - parlist

parlist - 5 index, 0 keyValues, 2 entries

Children
0x6f67657461636e69 address pointer - 0x1bb5070 address index - listitem
0x1bb51e0 address pointer - 0x1bb51e0 address index - listitem

listitem - 6 index, 0 keyValues, 1 entries

Children
0x79726f6765746163 address pointer - 0x1bb4d30 address index - text

text - 7 index, 0 keyValues, 1 entries
Data: page rous lady idle authority capt professes stabs monster petition heave humbly removes rescue runs shady peace most piteous worser oak assembly holes patience but malice whoreson mirrors master tenants smocks yielded

Children
0x1bb5140 address pointer - 0x1bb5140 address index - keyword

keyword - 8 index, 0 keyValues, 0 entries
Data: officer embrace such fears distinction attires

listitem - 6 index, 0 keyValues, 1 entries

Children
0x756f697463616620 address pointer - 0x1bb5250 address index - text

text - 7 index, 0 keyValues, 0 entries
Data: shepherd noble supposed dotage humble servilius bitch theirs venus dismal wounds gum merely raise red breaks earth god folds closet captain dying reek

shipping - 4 index, 0 keyValues, 0 entries
Data: Will ship internationally, See description for charges

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category540

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category418

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category985

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category787

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category12

mailbox - 4 index, 0 keyValues, 1 entries

Children
0x1bb5720 address pointer - 0x1bb5720 address index - mail

mail - 5 index, 0 keyValues, 4 entries

Children
0x1bb5780 address pointer - 0x1bb5780 address index - from
0x1bb57e0 address pointer - 0x1bb57e0 address index - to
0x1bb5830 address pointer - 0x1bb5830 address index - date
0x79726f7463697620 address pointer - 0x1bb5870 address index - text

from - 6 index, 0 keyValues, 0 entries
Data: Libero Rive mailto:Rive@hitachi.com

to - 6 index, 0 keyValues, 0 entries
Data: Benedikte Glew mailto:Glew@sds.no

date - 6 index, 0 keyValues, 0 entries
Data: 08/05/1999

text - 6 index, 0 keyValues, 2 entries
Data: virgin preventions half logotype weapons granted factious already carved fretted impress pestilent discomfort sinful conceiv corn preventions greatly suit observe sinews enforcement gold gazing set almost catesby turned servilius cook doublet preventions shrunk smooth great choice enemy disguis tender might deceit ros dreadful stabbing fold unjustly ruffian life hamlet containing leaves

Children
0x6f7661207375696c address pointer - 0x1bb57a0 address index - keyword
0x79726f6765746163 address pointer - 0x1bb5a70 address index - emph

keyword - 7 index, 0 keyValues, 0 entries
Data: girdles deserts flood george nobility reprieve

emph - 7 index, 0 keyValues, 0 entries
Data: armed

item - 3 index, 1 keyValues, 16 entries
KV: id : item1

Children
0x726564726f20646e address pointer - 0x1bb58e0 address index - location
0x622068636e656220 address pointer - 0x1bb5af0 address index - quantity
0x72756f6373696420 address pointer - 0x1bb5b30 address index - name
0x2073746e656d7572 address pointer - 0x1bb59d0 address index - payment
0x73756f6972756320 address pointer - 0x1bb5a20 address index - description
0x7020737365637865 address pointer - 0x1bb5ec0 address index - shipping
0x1bb7e30 address pointer - 0x1bb5f90 address index - incategory
0x206572 address pointer - 0x1bb5ff0 address index - incategory
0x1bb60c0 address pointer - 0x1bb60c0 address index - incategory
0x1bb6140 address pointer - 0x1bb6140 address index - incategory
0x1bb6220 address pointer - 0x1bb6220 address index - incategory
0x1bb62a0 address pointer - 0x1bb62a0 address index - incategory
0x1bb7030 address pointer - 0x1bb7030 address index - incategory
0x1bb5900 address pointer - 0x1bb5900 address index - incategory
0x1bb6600 address pointer - 0x1bb6600 address index - incategory
0x1bb6680 address pointer - 0x1bb6680 address index - mailbox

location - 4 index, 0 keyValues, 0 entries
Data: Moldova, Republic Of

quantity - 4 index, 0 keyValues, 0 entries
Data: 1

name - 4 index, 0 keyValues, 0 entries
Data: condemn

payment - 4 index, 0 keyValues, 0 entries
Data: Money order, Creditcard, Cash

description - 4 index, 0 keyValues, 1 entries

Children
0x6877206369666661 address pointer - 0x1bb5a40 address index - text

text - 5 index, 0 keyValues, 0 entries
Data: gold promotions despair flow tempest pitch concluded dian wenches musing author debase get bourn openly gonzago determine conceit parcel continue trophies bade cries merrily signet sportive valor planetary hastings empire vow merciless shoulders sewing player experience hereford dorset sue horn humorous fiend intellect venture invisible fathers lucilius add jests villains ballad greek feelingly doubt circumstances hearty britaines trojans tune worship canst france pay progeny wisdom stir mov impious promis clothes hangman trebonius choose men fits preparation

shipping - 4 index, 0 keyValues, 0 entries
Data: Will ship only within country, Buyer pays fixed shipping charges, See description for charges

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category966

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category488

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category741

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category692

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category493

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category36

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category977

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category65

incategory - 4 index, 1 keyValues, 0 entries
KV: category : category211

mailbox - 4 index, 0 keyValues, 1 entries

Children
0x1bb6730 address pointer - 0x1bb6730 address index - mail

mail - 5 index, 0 keyValues, 4 entries

Children
0x1bb6790 address pointer - 0x1bb6790 address index - from
0x1bb6820 address pointer - 0x1bb6820 address index - to
0x1bb6870 address pointer - 0x1bb6870 address index - date
0x1bb68b0 address pointer - 0x1bb68b0 address index - text

from - 6 index, 0 keyValues, 0 entries
Data: Javam Suwanda mailto:Suwanda@gmu.edu

to - 6 index, 0 keyValues, 0 entries
Data: Mehrdad Glew mailto:Glew@cohera.com

date - 6 index, 0 keyValues, 0 entries
Data: 11/28/1999

text - 6 index, 0 keyValues, 5 entries
Data: back greg flay across sickness peter enough royal herb embrace piteous die servilius avoid milk sky clouds unbraced put sacrifices seas childish longer flout heavy pitch rosalind orderly music delivery appease confound brook balance bravery bench bearing compounds attentive learned senses concave boughs discourse punishment physic further reading chair discords instruments bankrupts countrymen horrid royalties necessity tend cap curiously waken therewithal horse gather uncleanliness chief traffic where nuptial either remember peerless doing ruin coxcomb excess ponderous doubtful rite vill discontent manage beatrice straight muse shame prays maecenas any conveyance fingers whereupon child case deprive almost wed dreams slew

Children
0x1bb67b0 address pointer - 0x1bb67b0 address index - keyword
0x1bb6be0 address pointer - 0x1bb6be0 address index - keyword
0x1bb6160 address pointer - 0x1bb6160 address index - bold
0x1bb6180 address pointer - 0x1bb6180 address index - emph
0x1bb61a0 address pointer - 0x1bb61a0 address index - keyword

keyword - 7 index, 0 keyValues, 0 entries
Data: laying chance dungeons pleasant thyself fellow purse steward heaven ambassador terrible doubtfully

keyword - 7 index, 0 keyValues, 0 entries
Data: preparation rejoice gallants shepherd barbarian ford

bold - 7 index, 0 keyValues, 0 entries
Data: childish carrions imaginary wooden preventions bounteous sounded consider sayings fishified fine prime may

emph - 7 index, 0 keyValues, 0 entries
Data: dotage discipline choughs mew here

keyword - 7 index, 0 keyValues, 0 entries
Data: season presently victory women beating


At this point, this version does not save anything and is just processing the data. Add "-v" to see the analyzation data.
Job done. Exiting. Bye bye...
[/src]
 
Zuletzt bearbeitet:

Skipjack

Neu angemeldet

Registriert
17 Juli 2013
Beiträge
213
Das sind allerdings noch Sachen, die ich mir vorgenommen habe zu erledigen, sobald ich weiß das der Parser soweit funktioniert ...
Die Standardausrede des Coders. ;)

Ganz im Ernst, Deine Main ist die Seuche.
Ich beobachte the Thread schon länger, aber die Main hat mich bisher abgeschreckt, mich wirklich damit zu befassen.
Ich finde die Herangehensweise schon etwas merkwürdig: erst alles runtercoden, dabei Fehler suchen, die vielleicht gar nicht auftreten würden, wenn Du es direkt strukturieren würdest, um dann später ein Refactoring durchzuführen, um dann ggf. neue Fehler zu suchen.

Es ist natürlich jetzt auch einfach, hier klug umher zu schwätzen, aber ich hätte das Problem der dynamischen Arrays umgangen.
Ich habe selber vor einiger Zeit einen ASN.1 En/Decoder für ein Embedded-System implementieren müssen und habe dafür einen Linked-List Ansatz verwendet.
Jedes Tag wird durch eine Node-Struktur verwaltet, die u.a. optionale Pointer auf ein erstes Kind, sowie das nächste Tag in derselben Hierarchieebene enthält.
Alle Operationen auf diesen Nodes sind durch Routinen weggekapselt, damit der Hauptcode übersichtlich und lesbar bleibt.

Vielleicht schaffe ich ja in den nächsten Tagen ein Beispiel dafür. Mal sehen.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #14
Weil jeder hier meine Main kritisiert habe ich endlich 4 Funktionen hinzugefügt und die Main entschlackt sowie 2 Indent Levels weggebrochen die unnötig waren. :cool:

CreateTag und CreateKeyValueItem, sowie das auslagern des Command Parsers in eine Funktion + das Summary wird nur auch in einer Funktion erstellt.

Ich glaube das ist fern von perfekt - aber es ist ein Anfang! :)

Für Tips und Hinweise bin ich natürlich nach wie vor offen, Brother John hat ja zum Teil schon gute Lösungen gegeben, aber ich habe bisher noch nicht raus wie ich pass by reference (tagListe) an eine Funktion übergeben kann anstelle pass by value so dass das Freeing in einer Funktion stattfinden kann.

Die neue Version ist wieder auf Github online.

Falls jemand noch eine Lösung hat zu dem Problem mit dem Pointer auf Pointer Problem, bitte immer nur her damit :)
 

Skipjack

Neu angemeldet

Registriert
17 Juli 2013
Beiträge
213
Kann das durch das Reallokieren kommen, das die Speicherstellen verschoben wurden sind und keine gültigen Daten mehr enthalten?

Ohne jetzt genau Deine Strukturen durchblickt zu haben, macht mich die Stelle
[src=c]tagList = (tag*) realloc(tagList, sizeof (tag) * (tagCount + 1));[/src]
skeptisch.
Du verschiebst die gesamte Tagliste. Allerdings enthalten einzelne Tags doch Pointer auf Eltern oder Kinder, die auch irgendwo in der Tagliste liegen.
Verschiebst Du durch realloc die Liste im Speicher, liegen diese plötzlich auf ganz anderen Adressen. Die Pointer in den Tags haben aber noch den alten Inhalt - zeigen also dahin, wo das andere Tag vor dem Verschieben mal war. Wenn Du aber auf den Speicher zugreifst, muss es knallen.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #16
Du verschiebst die gesamte Tagliste. Allerdings enthalten einzelne Tags doch Pointer auf Eltern oder Kinder, die auch irgendwo in der Tagliste liegen.
Verschiebst Du durch realloc die Liste im Speicher, liegen diese plötzlich auf ganz anderen Adressen. Die Pointer in den Tags haben aber noch den alten Inhalt - zeigen also dahin, wo das andere Tag vor dem Verschieben mal war. Wenn Du aber auf den Speicher zugreifst, muss es knallen.

Die Erklärung wäre logisch. Allerdings ist der Fall das ein "parent"-Pointer gebrochen ist noch nicht aufgetreten.. bzw. hab ich das auch noch nicht überprüft- aber nach der Erklärung sollte ich zwingend auf Integer Indexing zurückgreifen und die Elemente die dynamischen Daten und keine festen, vorgeplanten, Speicherbereiche benutzen nicht über die Speicheradressen referenzieren sondern eben über deren Index in der (tag)Liste und C das Speichermanagement überlassen wo sich diese Elemente im Speicher, im Array, befinden.

Das ist doch mal gutes Feedback, wäre schön wenn C einem die Arbeit abnehmen würde und Referenzen auf Speicherobjekte überwachen würde - aber das hieß wohl Garbage Collection :D

Ich glaube der Fall wäre ein anderer wenn die Elemente statisch wären, heißt fixe Datenlängen/Attributwerte für char Pointer und keine dynamische Elemente in einem Struct.

Mir wurde in einem anderen Forum auch nahegelegt den Code neu zu schreiben, mit Rekursion und der Arbeit mit Strings anstelle von Zeichen. Eine Lösung wurde auch gepostet die in 70 Zeilen alle Tags (bis auf eingebettete) erkennt und die Daten einließt; bei Bedarf poste ich gern einen Link zu diesem Beitrag - wahlweise auch per PN.
Allerdings mit fixer Breite für Daten und Keys... fand ich nun nicht so prall. Aber wäre wohl auch irgendwie möglich das ganze zu erweitern.... nun ja.

Ich bin gerade schon zufrieden das es funktioniert so wie es mir denke ;) - zwar vielleicht mehr schlecht als Recht, aber deswegen auch bitte ich ja um Feedback was es zu verbessern gibt oder man anders machen kann. :T

--- [2015-08-28 18:25 CEST] Automatisch zusammengeführter Beitrag ---

Ganz konkret kanns da durchaus passiert sein, dass mir C++ reingerutscht ist. Ich bin halt nur fortgeschrittener C-Laie. In C++ ist *[] äquivalent zu **, vielleicht gilt das für C nicht so ganz. :unknown:

Jeder Stern nach einem Pointer* - Typ referenziert auf einen Pointer, also einem Pointer auf einem Pointer bzw. einem Array von Pointern - wenn ich richtig verstehe, es funktioniert ja auch, nur wird der Code, also das Referenzieren auf Adressen, falsch angewendet so das es zu den Pointer Fehlern kommt - siehe die Erklärung von Skipjack.

*Edit:
Habe nun die ganzen parent und children Pointer entfernt und durch Integer Indexe ersetzt. Zwar etwas umständlicher so aber so ist auch sichergestellt dass die Elemente wirklich gefunden werden können. :)
Eigentlich brauche ich heute dann nur noch den JSON Writer zu schreiben, das Parsen funktioniert so weit....
 
Zuletzt bearbeitet:

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
Skipjack hat recht. Das realloc auf die tagList ist tatsächlich das Problem.

Dass die kaputten Zeiger etwas sporadisch auftreten, hängt daran, wie realloc funktioniert. Wenn nämlich direkt anschließend an den aktuell belegten Specherbereich genug freier Speicher verfügbar ist, dann schnappt sich realloc einfach (einen Teil dieses) Speichers. Alle Zeiger bleiben gleich, kein Problem.

Nur wenn es nicht genügend freien Direktanschlussspeicher gibt, wird der komplette Speicherblock an einen Ort umkopiert, wo für die neue Größe genügend Platz ist. In dem Fall ändern sich sämtliche Zeiger.

Du hast zwei Möglichkeiten, das zu lösen:

  • Integer-Indexe verwenden,
  • globale tagList wegschmeißen.

Meine Empfehlung: weg mit der tagList. Du hast einen doppelt verlinkten Baum. Das reicht, um alle Knoten-Objekte zu verwalten. Mein Bild von oben ist eigentlich nicht ganz richtig. Die Zeiger in der children-Liste zeigen ja auf Elemente in der globalen tagList und dürften i.d.R. sogar sequentiell im Speicher liegen. Ohne globale tagList wäre jede Kind-Node tatsächlich ein separat allokiertes Objekt; genau so wie im Bild.

Vorteil: Es ändern sich keine Zeiger mehr. Auch wenn du die children-Liste re-allokierst nicht. Ist ja nur eine Liste von Zeigern auf die eigentlichen Objekte. Und die bleiben, wo sie sind. Das macht auch das Einfügen von neuen Knoten sehr simpel: neue Node malloc’en, children-Liste verlängern, neuen Zeiger dort reinhängen.

Nachteil: Aufräumen wird etwas komplexer. Beispiel:



Das ist der komplette Baum. Aktuell würdest du alle seine Nodes in tagList verwalten.

Angenommen, du willst »Wurzel« löschen. Dann muss auch der gesamte Baum darunter mit sterben. In deinem aktuellen Design würdest du einfach tagList free’en[1]. Das ist an Einfachheit tatsächlich nicht zu toppen. Ohne tagList musst du den Baum ablaufen und die Knoten einzeln zerstören.

[1] Ok, ok: zusätzlich noch die Nutzdaten, die in jedem Knoten hängen können. Aber bleiben wir mal bei der reinen Baumstruktur.

So einfach ist es mit der tagList aber nur, solange du einen kompletten Baum zerstörst. Es sieht schon anders aus, wenn du nur »Ast 2« zerstören willst. Viel Spaß mit der tagList. ;) Ohne die … naja … da läufst du den Baum halt nur ab »Ast 2« ab.

Oder: »Ast 1« mit allem, was darunter hängt, soll in einen ganz anderen Baum wandern. Mit tagList fängst du an, ganze Knoten durch die Gegend zu kopieren. Ohne verbiegst du nur einen Zeiger (Ast_1.parent) und erweiterst eine Zeiger-Liste (Neuer_Parent.children).

Auf den Punkt gebracht: Je mehr du den Baum editierst, desto nachteiliger ist die tagList. Dagegen helfen auch Integer-Indexe nur bedingt.

Zum Baum Ablaufen:
https://en.wikipedia.org/wiki/Tree_traversal
Da ist das sehr schön bebildert erklärt. Zum Zerstören bietet sich depth-first, post-order an.

P.S.: Optimierungsüberlegungen hab ich bewusst ausgeblendet. Dafür ist immernoch Gelegenheit, falls sich die Baumstruktur als Performance-Flaschenhals herausstellen sollte.

--- Edit ---
Dein Edit nicht gesehen.
Für den XML->JSON-Spezialfall sind Integer-Indexe auch ok, weil du beliebige Editierfähigkeit des Baums nicht brauchst. Böse Zungen könnten jetzt fragen: Wenn schon eine spezialisierte Datenstruktur, warum dann überhaupt ein Baum. XML sollte sich recht problemlos direkt nach JSON streamen lassen.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #18
Brother John, nicht falsch verstehen, aber ich muß derzeit ja nicht einzelne Elemente bearbeiten können ;)
Müsste ich allerdings die Datenstruktur bearbeiten, heißt Elemente einbauen, löschen oder verschieben gebe ich dir Recht, ich müsste die Indexe mitschleifen bzw. updaten, bei fixen Pointern (ohne statische Daten die sich nicht verschieben) wäre das kein Problem. Momentan ist alles eine Sequenzliste die Aufgebaut ist nach dem Aufkommen innerhalb der XML.


--- Edit ---
Dein Edit nicht gesehen.
Für den XML->JSON-Spezialfall sind Integer-Indexe auch ok, weil du beliebige Editierfähigkeit des Baums nicht brauchst. Böse Zungen könnten jetzt fragen: Wenn schon eine spezialisierte Datenstruktur, warum dann überhaupt ein Baum. XML sollte sich recht problemlos direkt nach JSON streamen lassen.

Ich dachte es wäre einfacher die Integer Indexe zu nehmen, da die Liste ja nur einmal aufgebaut wird und dann besteht.

Warum man XML nicht direkt nach JSON parsen kann? - Weil JSON im Gegensatz zu XML keine Tags mit gleichem Namen/anderem Inhalt so direkt übernehmen kann, es gibt immer nur einen Schlüssel zu einem Wert. In Json müssen daher Arrays gebildet werden, heißt das zwei Tags mit gleichem Namen praktisch in einem Array erfasst werden müssen.

In der standard_sample.xml gibt es zum Beispiel auch den Key "incategory" mit verschiedenen Key-Value Paaren, hier müsste man die Werte auch in einem Array für das Tag "incategory" mit den Werten:
"incategory" : {
"id": ["1", "2", "3", "4"]
}

Das heißt gleiche Wurzel/Ast - gleiche Keys für unterschiedliche Values.

Und das ist schon der kompliziertere Fall, für Äste mit gleichem Namen muß dann ein Array gebildet werden.

<book id="1"><name>Test</name></Book>
<book id="2"><name>bitte</name></Book>

wäre in Json so etwas zum Beispiel:

"book" : {
[{
"id" : "1",
"name" : "Test"
}, {
"id" : "2",
"name" : "bitte"
}]
}

Oder ähnlich. Ist also nicht ganz so einfach die XML einfach zu JSON umzukopieren da dabei eine gewisse Logik mit einspielen muß.
 

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
OK, leuchtet mir ein.

Versteh du mich auch nicht falsch. Ich bin so gepolt, auf jede Idee erstmal draufzuhauen, ob ich sie kaputt kriege. Wenn das nicht klappt, ist das schonmal ein gutes Zeichen.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #20
@Brother John:

Guten Morgen,

kein Problem, ich bin ja über jeden Denkansatz, der kommt, froh. :T
Ist halt alles ein Lernprozess.

Ich werde meine Nase noch in einige Bücher stecken müssen... eines über die Grundlagen der Sprache und auch gleich noch eines über Datenstrukturen und eines über Algorithmen, letzteres um mich komplett zu verwirren :D
Und wenn ich mit der Lektüre fertig bin kann ich vielleicht besser mit der Sprache umgehen und die Lösungen dann effizienter programmieren. Kommt Zeit dreht Rad ;)

Bis dato poche ich allerdings auf viele Anregungen :)
 
Oben