Compilerbau htmlparser










   

Was ist das Ziel

Einen HTML-Parser zu bauen. Dafür halten wir uns folgende Überlegungen vor Auge: Ein C-Programm besteht aus Ausdrücken mit Klammern, [, ], (, ), {, }, die geöffnet und geschlossen werden. Was unterscheidet eigentlich ein Pascal-Programm von einem C-Programm in dieser Hinsicht? Ganz einfach: Ein Pascal-Programm öffnet einen Block mit BEGIN und END, ein C-Programm mit { und }. Generell ist das aber dasselbe. Was in C, { und } ist, ist in Pascal BEGIN und END. Das sind eigentlich Klammern. Um solche Ausdrücke zu Parsen muss man wie bei den arithmetischen Termen und Ausdrücken einen Parser schreiben.
Ein Parser besteht aus drei Funktionen:
expression() term() factor() Was macht nun HTML aus? Nun hier gibt es nicht einfach BEGIN und END, sondern hier gibt es ziemlich viele Klammern. Ein Ausdruck wird quasi mit <TAG> geöffnet und mit </TAG> geschlossen. Diese Tags sind quasi unsere Klammern. Nur, dass es viele verschiedene Bezeichnungen für diese Klammern gibt. So gäbe es quasi <TAG1> </TAG2> und <TAG2> </TAG2> Es gibt also nicht nur ein BEGIN und END.
Jetzt ist die Frage wie machen wir das? Die Antwort lautet: Bei arithmetischen Ausdrücken können wir uns einer Stack-Architektur bedienen. Dabei werden allerdings die Werte auf dem Stack abgelegt - nicht die Klammern. Bei unserem HTML-Parser müssen wir allerdings quasi die Klammern, also die Tags auf den Stack ablegen, denn: Wir müssen wissen, ob das entsprechende Tag mit dem entsprechenden geschlossen wurde. Ein Tag kann nicht über ein anderes geschlossen werden.
Damit wir wissen, welche Tags wo wie hingehören, müssen wir die Tags auf dem Stack ablegen.
Nahe an einen Parser für arithmetische Ausdrücke kommen wir dann: Wir haben Tags die geöffent und geschlossen werden. Das sind unser ( und ). Wir haben aber auch Tags, die nicht geöffnet und geschlossen werden, zum Beispiel <BR>. Dies entspricht + und *. Und wir haben den eigentlichen Inhalt. Das entspricht den Werten. Der Unterschied ist aber einfach, dass wir die Tags auch auf dem Stack ablegen müssen, was wir bei arithmetischen Ausdrücken mit Klammern nicht tun.

Ich erlaube mir bei dem Parser jetzt einen Trick. Weil ich keine Lust habe, eine Tabelle mit HTML-Tags zum Beispiel für den Lexer zu erstellen - die zwischen Tags unterscheidet, die geöffnet und geschlossen werden und Tags die alleine stehen.
Mein Programm wird einfach davon ausgehen: Der HTML-Code ist richtig eingegen worden und nicht falsch.
Jetzt steht zur Frage, was überhaupt zu tun war? Wir wollen jedes HTML-Tag, was geöffnet und geschlossen wird, als eine Art Klammer begreifen. Wie in C '{' und '}'. In Pascal gibt es das auch 'BEGIN' und 'END'. Das sind eigentlich Klammern. In HTML gibt es quasi viele Klammern, nämlich <TAG> und </TAG>. Ich wollte um jedes sich öffnende Tag und sich jedes schließende Tag einen Block machen. Der HTML-Codes soll mit Kästen umrandet als Blöcke dargestellt werden. Dabei ist die Darstellung recht einfach: Der HTML-Code muss quasi übernommen werden, nur wird hier nicht direkt der HTML-Code an den Browser übergeben, sonst wird er nicht als Quelltext dargestellt. Also werden alle Sonderzeichen ersetzt. Zumindest die '<' durch < und so weiter. Für jeden Block wird ein <div> angegeben. Der Block im Block wird mittels <div> jedes mal 4% kleiner, 2% zum linken Rand, 2% zum rechten Rand. Und der erste Block ist blau, der darin weiß, der wieder darin wieder blau. Wenn nun irgendwelche Tags da stehen, die geöffnet und geschlossen werden, ist es egal was das für ein Tag ist. Es wird so oder so ein Block angezeigt. Von daher kann man wenigstens die Tags nicht unterscheiden, die auch wieder geschlossen werden, denn sie werden alle gleich dargestellt. Damit man sieh, ob die Tags auch wieder geschlossen werden, werden sie auf dem Stack abgelegt. Nun könnte man, wenn inzwischen Tags kommen, die nicht geschlossen werden, die allerdings genauso auf dem Stack liegen, einfach auf dem Stack alle Tags überspringen, bis man bei dem Tag angelangt ist, was das zu schließende Tag wieder schließt.

Ich habe schon einen funktionierenden Code geschrieben (nächster Beitrag)

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

#define TOKEN_FLAG_IS_NOT_TAG 0
#define TOKEN_FLAG_IS_TAG_OPEN 1
#define TOKEN_FLAG_IS_TAG_CLOSE 2
#define TOKEN_FLAG_EOF 3

char tokenbuf[256];
int tokenflag;
char tagstack[256][256];
int sp = 0;

void push(char *str) {
   strcpy(tagstack[sp], str);
    sp++;
}

void pop(char *str) {
   sp--;
   strcpy(str, tagstack[sp]);
}

FILE *fp;

int gettoken(char *str) {
   int i;
   int tflag;
   int ch;

   if(feof(fp))
      return TOKEN_FLAG_EOF;
   while(((ch = fgetc(fp)) != '<') && (!feof(fp)));
   if(feof(fp))
      return TOKEN_FLAG_EOF;
   i = 0;
   str[i] = ch;
   while((ch = fgetc(fp)) == ' ');
   i = 1;
   str[i] = ch;

   if(ch == '/')
      tflag = TOKEN_FLAG_IS_TAG_CLOSE;
   else
      tflag = TOKEN_FLAG_IS_TAG_OPEN;
   i = 2;
   while((ch = fgetc(fp)) != '>') {
      str[i] = ch;
      i++;
   }
   str[i] = ch;
   i++;
   str[i] = 0x00;
return tflag;
}

int tagcmp(char *tag1, char *tag2) {
   int i, j;

   if((tag1[0] != '<') && (tag2[0] != 0))
      return -2;
   i = 1;
   j = 1;
   while(tag1[i] == ' ') i++;
   while(tag2[j] == ' ') j++;
   if(tag2[j] != '/')
      return -3;
   j++;
   while(tag2[j] == ' ') j++;
   while((tag1[i] != ' ') && (tag1[i] != '>') && (tag2[j] != ' ') && (tag2[j] != '>')) {
      if(tag1[i] != tag2[j])
            return -1;
   i++;
   j++;
   }
   if((tag1[i] != ' ') && (tag1[i] != '>'))
      return -1;
   if((tag2[j] != ' ') && (tag2[j] != '>'))
      return -1;
   return 0;
}

void factor() {
   char tokenbuf2[256];

   tokenflag = gettoken(tokenbuf);
   if(tokenflag == TOKEN_FLAG_EOF)
      return;
   else if(tokenflag == TOKEN_FLAG_IS_TAG_OPEN)
      push(tokenbuf);
   else if(tokenflag == TOKEN_FLAG_IS_TAG_CLOSE) {
      pop(tokenbuf2);
      tagcmp(tokenbuf2, tokenbuf);
      while(tagcmp(tokenbuf2, tokenbuf) != 0) {
         pop(tokenbuf2);
      }
      printf("%s %s\n", tokenbuf, tokenbuf2);
   }
   factor();

}

int main(void) {
   if((fp = fopen("test.html", "r")) == NULL) {
      perror("Can't open HTML-File");
      return -2;
   }
   factor();
   fclose(fp);
return 0;
}


<tag>
<tag1>
<tag2>
</tag2>
<tag2>
</tag2>
<tag1>
</tag1>
</tag1>
<tag2>
<tag1>
</tag1>
</tag2>
</tag>
Auch getestet, an dem:

<tag>
<tag1>
<tag2>
</tag2>
<tag2>
</tag2>
<tag1>
<br>
<br>
</tag1>
</tag1>
<tag2>
<tag1>
</tag1>
</tag2>
</tag>


Generell geht das jetzt, ich habe es an meiner index.html und an meiner os3.html getestet. Es kommt allerdings zu Speicherzugriffsfehlern, wenn die Datei sehr lange ist. Ich behaupte das ist aber zunächst etwas, an dem man nichts ändern kann, weil die rekursiven Aufrufe sich häufen. Ich werde jetzt aber gucken.

So, man kann die Sache jetzt in zweierlei Hinsicht verbessern. Das eine ist: Wir haben jetzt keine Tabelle bei dem Lexer. Das heißt wir können Tags die geöffnet und geschlossen werden von anderen nicht gut unterscheiden. Aber wir haben uns dafür ein "algorithmisches Stück" ausgedacht. Wenn nämlich auf dem Stack ein Tag liegt, was nicht dem zu schließenden gleicht, betrachten wir es als ein Tag, was nicht geschlossen wird und nehmen diese Tags solange vom Stack, bis wir bei dem sind, was das erstere schließt. In sofern können wir aber im Nachhinein alle Tags als einzelne betrachten, die alle vom Stack genommen werden. Wir können sie zum Beispiel wiederum in eine Schlange einreihen und wenn das zu findende Tag gefunden ist, sie zunächst alle als einzelne Tags posten.
Es gibt aber noch eine massive Verbesserung: Wir sind von einem Compiler ausgegangen. Also benutzen wir rekursive Funktionsaufrufe. Wir sind aber dahinter gekommen, dass wir nicht expression(), term(), factor() verwenden, sondern nur factor(). Demnach können wir bei einer Funktion aber genausogut eine Schleife für unseren Stack verwenden. Weil factor() so oder so nur factor() aufruft.

Das war allerdings nicht die Ursache für den Absturz. Ursache war, dass sich in meiner HTML-Datei ein paar Fehler bezüglich der Reihenfolge der Setzung der HTML-Tags ergeben. Wir haben gesagt, das Programm kann nicht, wenn Fehler im Code sind.

Bei wikipedia mit dem Artikel über Debian kommt kein Fehler.

Also, was ich heute machen werde bzw. was ich mit meinem HTML-Parser machen werde.
Ich werde die nächsten Zeiten von 10:30 Uhr AM bis 12:30 Uhr PM bei meiner Mutter sein. Dort werde ich programmieren.
Ich werde nur da programmieren und nur dann, sonst nimmt das ganze wieder überhand.
Ich werde zunächst den Parser verfeinern. Ich denke mir folgenden Trick aus: Die <div>-Box nachher im Output, muss bereits beginnen, wenn das entsprechende Tag geöffnet wird. Ob es überhaupt eines ist, das wieder geschlossen wird, entscheidet sich leider erst, wenn es geschlossen wird. Ich werde deswegen einen Vordurchlauf über die HTML-Datei starten. Dabei werde ich alle Tags auf einen extra Stapel oder einer extra Schlange einreihen, die von ihrem Namen wieder geschlossen werden.
Vorraussetzung dafür ist, dass der HTML-Code richtig ist und das nicht ein Tag mit selben Namen ein Mal geöffnet und geschlossen wird, und ein anderes Mal nicht. Desweiteren werde ich einen kleinen "Browser" schreiben. Dafür werde ich Qt4 verwenden. Qt4 stellt ja schön Schriftarten in einem Programm mit graphischer Benutzeroberfläche da. Und außerdem auch Bilder.
Was der Browser bei seinen ersten Tests können wird, ist die Darstellung von Schrift mit verschiedenen Schriftarten und: Die Darstellung von Bildern. Was er noch nicht kann, sind Tabellen.
Für so einen "Browser" ist so ein Parser unbedingt notwendig, denn die Darstellung der Schrift und allem weiteren übernimmt so oder so Qt4. Programme, die eine graphische Benutzeroberfläche verwenden, verwenden immer Bibliotheken, wie Qt4 oder Gtk+. Oder unter Windows gibt es den Borland C++ Builder.

Aber, jetzt muss ich weiter programmieren. #include <stdio.h>
#include <string.h>

#define TOKEN_FLAG_IS_NOT_TAG 0
#define TOKEN_FLAG_IS_TAG_OPEN 1
#define TOKEN_FLAG_IS_TAG_CLOSE 2
#define TOKEN_FLAG_EOF 3

char tokenbuf[256];
int tokenflag;
char tagstack[1024][256];
int sp = 0;

void push(char *str) {
   strcpy(tagstack[sp], str);
   sp++;
}

void pop(char *str) {
   sp--;
   strcpy(str, tagstack[sp]);
}

FILE *fp;

int gettoken(char *str) {
   int i;
   int tflag;
   int ch;

   if(feof(fp))
      return TOKEN_FLAG_EOF;
   while(((ch = fgetc(fp)) != '<') && (!feof(fp)));
   if(feof(fp))
      return TOKEN_FLAG_EOF;
   i = 0;
   str[i] = ch;
   while((ch = fgetc(fp)) == ' ');
   i = 1;
   str[i] = ch;

   if(ch == '/')
      tflag = TOKEN_FLAG_IS_TAG_CLOSE;
   else
      tflag = TOKEN_FLAG_IS_TAG_OPEN;
   i = 2;
   while((ch = fgetc(fp)) != '>') {
      str[i] = ch;
   i++;
   }
   str[i] = ch;
   i++;
   str[i] = 0x00;
return tflag;
}

int tagcmp(char *tag1, char *tag2) {
   int i, j;

   if((tag1[0] != '<') && (tag2[0] != 0))
      return -2;
   i = 1;
   j = 1;
   while(tag1[i] == ' ') i++;
   while(tag2[j] == ' ') j++;
   if(tag2[j] != '/')
      return -3;
   j++;
   while(tag2[j] == ' ') j++;
   while((tag1[i] != ' ') && (tag1[i] != '>') && (tag2[j] != ' ') && (tag2[j] != '>')) {
      if(tag1[i] != tag2[j])
         return -1;
      i++;
      j++;
   }
   if((tag1[i] != ' ') && (tag1[i] != '>'))
      return -1;
   if((tag2[j] != ' ') && (tag2[j] != '>'))
      return -1;
   return 0;
}

void factor() {
   char tokenbuf2[256];

   while(1) {
      tokenflag = gettoken(tokenbuf);
      if(tokenflag == TOKEN_FLAG_EOF)
         return;
      else if(tokenflag == TOKEN_FLAG_IS_TAG_OPEN)
         push(tokenbuf);
      else if(tokenflag == TOKEN_FLAG_IS_TAG_CLOSE) {
         pop(tokenbuf2);
         tagcmp(tokenbuf2, tokenbuf);
         while(tagcmp(tokenbuf2, tokenbuf) != 0) {
            pop(tokenbuf2);
         }
         printf("%s %s\n", tokenbuf2, tokenbuf);
      }
   }

}

int main(void) {
   if((fp = fopen("test.html", "r")) == NULL) {
      perror("Can't open HTML-File");
      return -2;
   }
   factor();
   fclose(fp);
return 0;
}
Da kommt noch ein Fehler, weil das Ende der Datei wird nicht richtig erkannt und es kommt ein Fehler. Das märze ich als erstes aus.

So, ich schreibe jetzt an meinem HTML-Parser weiter. Jetzt mal eine Frage: Stoßen wir auf folgendes Problem: Wenn wir Tags vom Stapel holen, holen wir nachher, wenn wir auf ein Tag stoßen, was nicht geschlossen wird, alle Tags vom Stapel, so, dass der Stapel nachher leer ist? Die Antwort lautet nein: Denn wir holen die Tags erst vom Stapel, wenn sie geschlossen werden.
Nun verbessern wir unser Programm:
  1. Das erste ist, wir müssen den letzten Fehler am Ende beseitigen.
  2. Wir tun alles auf einen zweiten Stapel oder eine zweite Schlange, was vom Stapel geholt wird, wenn ein Tag geschlossen werden, was auf dem Stapel aber nicht dem Tag, was geschlossen wird entspricht
  3. Wir machen einen Vordurchlauf, welcher speichert, was denn nun die Tags sind, die geschlossen werden und was die Tags sind, die nicht geschlossen werden.
Der Fehler lag bei:

<!DOCTYPE html>

Ich habe festgestellt, dass zwar <html> und so weiter angezeigt, und so weiter angezeigt wird,
da steht aber auch <!DOCTYPE html>
Das war der Fehler.
Da lag zwar nicht der eigentliche Fehler, weil ich <!DOCTYPE html> entfernt habe und es immer noch zum Fehler kam, aber der Fehler lag bei der Syntax vom wikipedia Artikel zu Debian. Diese ist zwar nicht falsch, aber benutzt Spitzen, die über die "Kern"-Logik von HTML hinausgehen. Ich habe das Programm jetzt wieder an einem eigenen HTML-Code getestet, da geht es.
<html>
<head> <title> </title> </head>
<body>
<br><br>
<ul>
<li> Hallo </li>
<li> Hallo </li>
<li> Hallo </li>
</ul>
</body>
</html>

Nachdem wir uns über diesen Fehler klar geworden sind, pushen wir alle Tags auf einen zweiten Stack, die nicht geschlossen werden.
Nachdem wir das Tag gefunden hat, das zu dem schließenden gehört, holen wir diese erst alle vom zweiten Stapel und posten dann die, die geschlossen werden. Leider handelt es sich bei dem zweiten um keinen Stapel - sondern um eine Schlange. Wenn wir nämlich vom Stack die Tags herunternehmen und in dieser Reihenfolge auf einen anderen Stack bringen, dann werden die obersten als erste auf dem zweiten Stack landen.

Die nächste Version des Programms sieht so aus - nächster Beitrag. #include <stdio.h>
#include <string.h>

#define TOKEN_FLAG_IS_NOT_TAG 0
#define TOKEN_FLAG_IS_TAG_OPEN 1
#define TOKEN_FLAG_IS_TAG_CLOSE 2
#define TOKEN_FLAG_EOF 3

char tokenbuf[256];
int tokenflag;
char tagstack[1024][256];
char tagqueuenotclosed[512][256];
int sp = 0;
int qptop = 0;
int qpbot = 0;

void push(char *str) {
   strcpy(tagstack[sp], str);
   sp++;
}

void pop(char *str) {
   sp--;
   strcpy(str, tagstack[sp]);
}

void put(char *str) {
   strcpy(tagqueuenotclosed[qptop], str);
   qptop++;
}

int get(char *str) {
   if (qpbot < qptop) {
      strcpy(str, tagqueuenotclosed[qpbot]);
      qpbot++;
      return 1;
   }
return 0;
}

FILE *fp;

int gettoken(char *str) {
   int i;
   int tflag;
   int ch;

   if(feof(fp))
      return TOKEN_FLAG_EOF;
   while(((ch = fgetc(fp)) != '<') && (!feof(fp)));
   /*if(feof(fp))
      return TOKEN_FLAG_EOF;*/
   i = 0;
   str[i] = ch;
   while((!feof(fp)) && ((ch = fgetc(fp)) == ' '));
   i = 1;
   str[i] = ch;

   if(ch == '/')
      tflag = TOKEN_FLAG_IS_TAG_CLOSE;
   else
      tflag = TOKEN_FLAG_IS_TAG_OPEN;
   i = 2;

   while((!feof(fp)) && ((ch = fgetc(fp)) != '>')) {
      str[i] = ch;
      i++;
   }
   str[i] = ch;
   i++;
   str[i] = 0x00;

return tflag;
}

int tagcmp(char *tag1, char *tag2) {
   int i, j;

   if((tag1[0] != '<') && (tag2[0] != 0))
      return -2;
   i = 1;
   j = 1;
   while(tag1[i] == ' ') i++;
   while(tag2[j] == ' ') j++;
   if(tag2[j] != '/')
      return -3;
   j++;
   while(tag2[j] == ' ') j++;
   while((tag1[i] != ' ') && (tag1[i] != '>') && (tag2[j] != ' ') && (tag2[j] != '>')) {
      if(tag1[i] != tag2[j])
         return -1;
      i++;
      j++;
   }
   if((tag1[i] != ' ') && (tag1[i] != '>'))
      return -1;
   if((tag2[j] != ' ') && (tag2[j] != '>'))
      return -1;
   return 0;
}

void factor() {
   char tokenbuf2[256];

   while(1) {
      tokenflag = gettoken(tokenbuf);
      if(tokenflag == TOKEN_FLAG_EOF) {
            break;
      }
      else if(tokenflag == TOKEN_FLAG_IS_TAG_OPEN)
         push(tokenbuf);
      else if(tokenflag == TOKEN_FLAG_IS_TAG_CLOSE) {
         pop(tokenbuf2);
      tagcmp(tokenbuf2, tokenbuf);
      while(tagcmp(tokenbuf2, tokenbuf) != 0) {
         put(tokenbuf2);
         pop(tokenbuf2);
      }
      while(get(tokenbuf2) != 0)
         printf("%s\n", tokenbuf2);
      printf("%s %s\n", tokenbuf2, tokenbuf);
      }
   }
return;
}

int main(void) {
   if((fp = fopen("test6.html", "r")) == NULL) {
      perror("Can't open HTML-File");
      return -2;
   }
   factor();
   fclose(fp);
return 0;
}

Und kommt damit klar:
<html>
<head> <title> </title> </head>
<body>
<br>
<br>
<ul>
<li> Hallo </li>
<li> Hallo </li>
<li> Hallo </li>
</ul>
</body>
</html>



Was ich jetzt machen werde ist ein Vordurchlauf - dabei werden im zweiten Durchlauf alle Tags, die in der Schlange sind, als Tags betrachtet, die nicht geschlossen werden. Die Schlange verfügt nicht über die Operationen push und pop, sondern put und get. Wenn wir auf den Stapel allerdings mehrfach push und pop anwenden, wird der Stapel überschrieben. Das passiert bei der Schlange nicht.
Bei der Schlange haben wir quasi zwei Zeiger. Einen auf den Anfang der Schlange und einen auf das Ende. Der Zeiger auf das Ende wird jedes Mal erhöht, wenn put angewendet wird. Der Zeiger auf den Anfang, wenn get angewendet wird. Dabei wird die Schlange aber nicht überschrieben. Also kann man die Schlange nachher noch nutzen.

Also, das fertige Programmstück lautet, nächster Beitrag.
#include <stdio.h>
#include <string.h>

#define TOKEN_FLAG_IS_NOT_TAG 0
#define TOKEN_FLAG_IS_TAG_OPEN 1
#define TOKEN_FLAG_IS_TAG_CLOSE 2
#define TOKEN_FLAG_EOF 3

char tokenbuf[256];
int tokenflag;
char tagstack[1024][256];
char tagqueuenotclosed[512][256];
int sp = 0;
int qptop = 0;
int qpbot = 0;

void push(char *str) {
   strcpy(tagstack[sp], str);
   sp++;
}

void pop(char *str) {
   sp--;
   strcpy(str, tagstack[sp]);
}

void put(char *str) {
   strcpy(tagqueuenotclosed[qptop], str);
   qptop++;
}

int get(char *str) {
   if (qpbot < qptop) {
      strcpy(str, tagqueuenotclosed[qpbot]);
      qpbot++;
      return 1;
   }
return 0;
}

FILE *fp;

int gettoken(char *str) {
   int i;
   int tflag;
   int ch;

   if(feof(fp))
      return TOKEN_FLAG_EOF;
   while(((ch = fgetc(fp)) != '<') && (!feof(fp)));
   /*if(feof(fp))
      return TOKEN_FLAG_EOF;*/
   i = 0;
   str[i] = ch;
   while((!feof(fp)) && ((ch = fgetc(fp)) == ' '));
   i = 1;
   str[i] = ch;

   if(ch == '/')
      tflag = TOKEN_FLAG_IS_TAG_CLOSE;
   else
      tflag = TOKEN_FLAG_IS_TAG_OPEN;
   i = 2;

   while((!feof(fp)) && ((ch = fgetc(fp)) != '>')) {
      str[i] = ch;
      i++;
   }
   str[i] = ch;
   i++;
   str[i] = 0x00;

return tflag;
}

int tagcmp(char *tag1, char *tag2) {
   int i, j;

   if((tag1[0] != '<') && (tag2[0] != 0))
      return -2;
   i = 1;
   j = 1;
   while(tag1[i] == ' ') i++;
   while(tag2[j] == ' ') j++;
   if(tag2[j] != '/')
      return -3;
   j++;
   while(tag2[j] == ' ') j++;
   while((tag1[i] != ' ') && (tag1[i] != '>') && (tag2[j] != ' ') && (tag2[j] != '>')) {
         if(tag1[i] != tag2[j])
            return -1;
         i++;
         j++;
   }
   if((tag1[i] != ' ') && (tag1[i] != '>'))
      return -1;
   if((tag2[j] != ' ') && (tag2[j] != '>'))
      return -1;
   return 0;
}

void factor() {
   char tokenbuf2[256];

   while(1) {
      tokenflag = gettoken(tokenbuf);
      if(tokenflag == TOKEN_FLAG_EOF) {
            break;
      }
      else if(tokenflag == TOKEN_FLAG_IS_TAG_OPEN)
            push(tokenbuf);
      else if(tokenflag == TOKEN_FLAG_IS_TAG_CLOSE) {
         pop(tokenbuf2);
         tagcmp(tokenbuf2, tokenbuf);
         while(tagcmp(tokenbuf2, tokenbuf) != 0) {
               put(tokenbuf2);
               pop(tokenbuf2);
         }
         while(get(tokenbuf2) != 0)
               printf("%s\n", tokenbuf2);
         printf("%s %s\n", tokenbuf2, tokenbuf);
      }
   }
return;
}

void factor2() {
   char tokenbuf2[256];
   int flag = 0;
   char color[64];
   int width = 100;
   int i;

   strcpy(color, "blue");
   while(1) {
      tokenflag = gettoken(tokenbuf);
      if(tokenflag == TOKEN_FLAG_EOF) {
            break;
      }
      else if(tokenflag == TOKEN_FLAG_IS_TAG_OPEN) {
         flag = 0;
         for(i = 0; i < qptop; i++) {
            if(strcmp(tagqueuenotclosed[i], tokenbuf) == 0)
                  flag = 1;
         }
         if(flag) {
            printf("<br><br>\n");
            printf("%s\n", tokenbuf);
            printf("<br><br>\n");
         }
         else {
            printf("<div width=\"%i \%\" color=\"%s\">", width, color);
            printf("%s\n", tokenbuf);
            if(strcmp(color, "blue") == 0)
               strcpy(color, "white");
            else
               strcpy(color, "blue");
            width -= 4;
         }
         push(tokenbuf);
      }
      else if(tokenflag == TOKEN_FLAG_IS_TAG_CLOSE) {
            pop(tokenbuf2);
            tagcmp(tokenbuf2, tokenbuf);
            while(tagcmp(tokenbuf2, tokenbuf) != 0) {
               put(tokenbuf2);
               pop(tokenbuf2);
            }
            while(get(tokenbuf2) != 0);
            printf("%s", tokenbuf);
            printf("</div>\n");
            width += 4;
         }
      }
return;
}

int main(void) {
   if((fp = fopen("test6.html", "r")) == NULL) {
      perror("Can't open HTML-File");
      return -2;
   }
   factor();
   fclose(fp);

   if((fp = fopen("test6.html", "r")) == NULL) {
      perror("Can't open HTML-File");
      return -2;
   }
   factor2();
   fclose(fp);

return 0;
}

So, ich habe es hingekriegt!
#include <stdio.h>
#include <string.h>

#define TOKEN_FLAG_IS_NOT_TAG 0
#define TOKEN_FLAG_IS_TAG_OPEN 1
#define TOKEN_FLAG_IS_TAG_CLOSE 2
#define TOKEN_FLAG_EOF 3

char tokenbuf[256];
int tokenflag;
char tagstack[1024][256];
char tagqueuenotclosed[512][256];
int sp = 0;
int qptop = 0;
int qpbot = 0;

void push(char *str) {
   strcpy(tagstack[sp], str);
   sp++;
}

void pop(char *str) {
   sp--;
   strcpy(str, tagstack[sp]);
}

void put(char *str) {
   strcpy(tagqueuenotclosed[qptop], str);
   qptop++;
}

int get(char *str) {
   if (qpbot < qptop) {
      strcpy(str, tagqueuenotclosed[qpbot]);
      qpbot++;
      return 1;
   }
return 0;
}

FILE *fp;

int gettoken(char *str) {
   int i;
   int tflag;
   int ch;

   if(feof(fp))
      return TOKEN_FLAG_EOF;
   while(((ch = fgetc(fp)) != '<') && (!feof(fp)));
   /*if(feof(fp))
      return TOKEN_FLAG_EOF;*/
   i = 0;
   str[i] = ch;
   while((!feof(fp)) && ((ch = fgetc(fp)) == ' '));
   i = 1;
   str[i] = ch;

   if(ch == '/')
      tflag = TOKEN_FLAG_IS_TAG_CLOSE;
   else
      tflag = TOKEN_FLAG_IS_TAG_OPEN;
   i = 2;

   while((!feof(fp)) && ((ch = fgetc(fp)) != '>')) {
      str[i] = ch;
      i++;
   }
   str[i] = ch;
   i++;
   str[i] = 0x00;

return tflag;
}

int tagcmp(char *tag1, char *tag2) {
   int i, j;

   if((tag1[0] != '<') && (tag2[0] != 0))
      return -2;
   i = 1;
   j = 1;
   while(tag1[i] == ' ') i++;
   while(tag2[j] == ' ') j++;
   if(tag2[j] != '/')
      return -3;
   j++;
   while(tag2[j] == ' ') j++;
   while((tag1[i] != ' ') && (tag1[i] != '>') && (tag2[j] != ' ') && (tag2[j] != '>')) {
      if(tag1[i] != tag2[j])
         return -1;
      i++;
      j++;
   }
   if((tag1[i] != ' ') && (tag1[i] != '>'))
      return -1;
   if((tag2[j] != ' ') && (tag2[j] != '>'))
      return -1;
   return 0;
}

void factor() {
   char tokenbuf2[256];

   while(1) {
      tokenflag = gettoken(tokenbuf);
         if(tokenflag == TOKEN_FLAG_EOF) {
            break;
      }
      else if(tokenflag == TOKEN_FLAG_IS_TAG_OPEN)
         push(tokenbuf);
      else if(tokenflag == TOKEN_FLAG_IS_TAG_CLOSE) {
         pop(tokenbuf2);
         tagcmp(tokenbuf2, tokenbuf);
         while(tagcmp(tokenbuf2, tokenbuf) != 0) {
            put(tokenbuf2);
            pop(tokenbuf2);
      }
      while(get(tokenbuf2) != 0);
      //printf("%s\n", tokenbuf2);
         //printf("%s %s\n", tokenbuf2, tokenbuf);
      }
   }
return;
}

void maketagtohtmlentities(char *tag, char *out) {
   int i;
   int j;
   int n;

   n = strlen(tag);
   for(i = 0, j = 0; j < n; j++) {
      if(tag[j] == '<') {
         out[i+0] = '&';
         out[i+1] = 'l';
         out[i+2] = 't';
         out[i+3] = ';';
         i += 4;
   }
   else if(tag[j] == '>') {
      out[i+0] = '&';
      out[i+1] = 'g';
      out[i+2] = 't';
      out[i+3] = ';';
      i += 4;
      }
      else {
         out[i] = tag[j];
         i++;
      }
   }
   out[i] = 0x00;
}

void factor2() {
   char tokenbuf2[256];
   char tokenbuf3[256];
   int flag = 0;
   char color[64];
   int width = 100;
   int i;

   strcpy(color, "blue");
   while(1) {
      tokenflag = gettoken(tokenbuf);
      if(tokenflag == TOKEN_FLAG_EOF) {
         break;
   }
   else if(tokenflag == TOKEN_FLAG_IS_TAG_OPEN) {
         flag = 0;
         for(i = 0; i < qptop; i++) {
               if(strcmp(tagqueuenotclosed[i], tokenbuf) == 0)
                  flag = 1;
         }
         if(flag) {
               printf("<br><br>\n");
               maketagtohtmlentities(tokenbuf, tokenbuf3);
               printf("%s\n", tokenbuf3);
               printf("<br><br>\n");
         }
         else {
               printf("<div style=\"width: %i\%; background-color: %s; margin:2\%;\">", width, color);
               maketagtohtmlentities(tokenbuf, tokenbuf3);
            printf("%s\n", tokenbuf3);
         if(strcmp(color, "blue") == 0)
               strcpy(color, "white");
      else
         strcpy(color, "blue");
         width -= 4;
         }
      push(tokenbuf);
      }
      else if(tokenflag == TOKEN_FLAG_IS_TAG_CLOSE) {
         pop(tokenbuf2);
         tagcmp(tokenbuf2, tokenbuf);
         while(tagcmp(tokenbuf2, tokenbuf) != 0) {
            put(tokenbuf2);
            pop(tokenbuf2);
         }
         while(get(tokenbuf2) != 0);
         maketagtohtmlentities(tokenbuf, tokenbuf3);
         printf("%s\n", tokenbuf3);
         printf("</div>\n");
         width += 4;
      }
   }
return;
}

int main(void) {
   if((fp = fopen("test6.html", "r")) == NULL) {
      perror("Can't open HTML-File");
      return -2;
   }
   factor();
   fclose(fp);

   if((fp = fopen("test6.html", "r")) == NULL) {
      perror("Can't open HTML-File");
      return -2;
   }
   factor2();
   fclose(fp);

return 0;
}

Ich habe die Sache weiter verbessert. Ein Fehler war bei der Abwechslung von blue und white. Wenn Boxen "nebeneinander" stehen, dann wird die eine blau, die andere weiß, die nächste blau, auch wenn sie in einer weißen oder blauen stehen. Dieses Problem lässt sich beheben, indem man die Folge der Abwechslungen ändert, oder, indem jede Box generell eine eigene Farbe hat - über Zufallszahlen.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define TOKEN_FLAG_IS_NOT_TAG 0
#define TOKEN_FLAG_IS_TAG_OPEN 1
#define TOKEN_FLAG_IS_TAG_CLOSE 2
#define TOKEN_FLAG_EOF 3

char tokenbuf[256];
int tokenflag;
char tagstack[1024][256];
char tagqueuenotclosed[512][256];
int sp = 0;
int qptop = 0;
int qpbot = 0;

void push(char *str) {
   strcpy(tagstack[sp], str);
   sp++;
}

void pop(char *str) {
   sp--;
   strcpy(str, tagstack[sp]);
}

void put(char *str) {
   strcpy(tagqueuenotclosed[qptop], str);
   qptop++;
}

int get(char *str) {
   if (qpbot < qptop) {
      strcpy(str, tagqueuenotclosed[qpbot]);
      qpbot++;
      return 1;
   }
return 0;
}

FILE *fp;

int gettoken(char *str) {
   int i;
   int tflag;
   int ch;

   if(feof(fp))
      return TOKEN_FLAG_EOF;
   while(((ch = fgetc(fp)) != '<') && (!feof(fp)));
   /*if(feof(fp))
      return TOKEN_FLAG_EOF;*/
   i = 0;
   str[i] = ch;
   while((!feof(fp)) && ((ch = fgetc(fp)) == ' '));
   i = 1;
   str[i] = ch;

   if(ch == '/')
      tflag = TOKEN_FLAG_IS_TAG_CLOSE;
   else
      tflag = TOKEN_FLAG_IS_TAG_OPEN;
   i = 2;

   while((!feof(fp)) && ((ch = fgetc(fp)) != '>')) {
      str[i] = ch;
      i++;
   }
   str[i] = ch;
   i++;
   str[i] = 0x00;

return tflag;
}

int tagcmp(char *tag1, char *tag2) {
   int i, j;

   if((tag1[0] != '<') && (tag2[0] != 0))
      return -2;
   i = 1;
   j = 1;
   while(tag1[i] == ' ') i++;
   while(tag2[j] == ' ') j++;
   if(tag2[j] != '/')
      return -3;
   j++;
   while(tag2[j] == ' ') j++;
   while((tag1[i] != ' ') && (tag1[i] != '>') && (tag2[j] != ' ') && (tag2[j] != '>')) {
      if(tag1[i] != tag2[j])
         return -1;
      i++;
      j++;
   }
   if((tag1[i] != ' ') && (tag1[i] != '>'))
      return -1;
   if((tag2[j] != ' ') && (tag2[j] != '>'))
      return -1;
   return 0;
}

void factor() {
   char tokenbuf2[256];

   while(1) {
      tokenflag = gettoken(tokenbuf);
      if(tokenflag == TOKEN_FLAG_EOF) {
         break;
   }
   else if(tokenflag == TOKEN_FLAG_IS_TAG_OPEN)
      push(tokenbuf);
   else if(tokenflag == TOKEN_FLAG_IS_TAG_CLOSE) {
      pop(tokenbuf2);
      tagcmp(tokenbuf2, tokenbuf);
      while(tagcmp(tokenbuf2, tokenbuf) != 0) {
         put(tokenbuf2);
      pop(tokenbuf2);
      }
      while(get(tokenbuf2) != 0);
         //printf("%s\n", tokenbuf2);
      //printf("%s %s\n", tokenbuf2, tokenbuf);
      }
   }
return;
}

void maketagtohtmlentities(char *tag, char *out) {
   int i;
   int j;
   int n;

   n = strlen(tag);
   for(i = 0, j = 0; j < n; j++) {
      if(tag[j] == '<') {
         out[i+0] = '&';
         out[i+1] = 'l';
         out[i+2] = 't';
         out[i+3] = ';';
         i += 4;
      }
      else if(tag[j] == '>') {
         out[i+0] = '&';
         out[i+1] = 'g';
         out[i+2] = 't';
         out[i+3] = ';';
         i += 4;
   }
      else {
      out[i] = tag[j];
         i++;
      }
   }
   out[i] = 0x00;
}

void factor2() {
   char tokenbuf2[256];
   char tokenbuf3[256];
   int flag = 0;
   char color[64];
   int width = 100;
   int i;
   char color1, color2, color3;

   strcpy(color, "deepskyblue");
   while(1) {
      tokenflag = gettoken(tokenbuf);
      if(tokenflag == TOKEN_FLAG_EOF) {
         break;
      }
      else if(tokenflag == TOKEN_FLAG_IS_TAG_OPEN) {
         flag = 0;
         for(i = 0; i < qptop; i++) {
            if(strcmp(tagqueuenotclosed[i], tokenbuf) == 0)
               flag = 1;
         }
         if(flag) {
               printf("<br><br>\n");
               maketagtohtmlentities(tokenbuf, tokenbuf3);
               printf("%s\n", tokenbuf3);
               printf("<br><br>\n");
         }
         else {
               color1 = (rand() % 64)+64;
               color2 = (rand() % 64)+64;
               color3 = (rand() % 64)+64;
               printf("<div style=\"width: %i\%; background-color: %X%X%X; margin:2\%;\">", width, color1, color2, color3);
               maketagtohtmlentities(tokenbuf, tokenbuf3);
            printf("%s\n", tokenbuf3);
         if(strcmp(color, "deepskyblue") == 0)
               strcpy(color, "white");
         else
               strcpy(color, "deepskyblue");
         width -= 4;
      }
      push(tokenbuf);
   }
   else if(tokenflag == TOKEN_FLAG_IS_TAG_CLOSE) {
         pop(tokenbuf2);
         tagcmp(tokenbuf2, tokenbuf);
         while(tagcmp(tokenbuf2, tokenbuf) != 0) {
               put(tokenbuf2);
               pop(tokenbuf2);
         }
         while(get(tokenbuf2) != 0);
         maketagtohtmlentities(tokenbuf, tokenbuf3);
         printf("%s\n", tokenbuf3);
         printf("</div>\n");
         width += 4;
      }
   }
return;
}
int main(void) {
   if((fp = fopen("test6.html", "r")) == NULL) {
      perror("Can't open HTML-File");
      return -2;
   }
   factor();
   fclose(fp);

   if((fp = fopen("test6.html", "r")) == NULL) {
      perror("Can't open HTML-File");
      return -2;
   }
   factor2();
   fclose(fp);

return 0;
}





David Vajda
Schickhardtstraße 5
D-72072, Tübingen
+491735807467
david@ituenix.de
http://www.ituenix.de


Dies Seite wurde aufgebaut mit