Discussion:
newbie: html parsen
(zu alt für eine Antwort)
t***@gmail.com
2013-11-04 13:47:57 UTC
Permalink
Hallo,

hier ist meine Funktion. Sie erhält als Parameter einen HTML-String (char *s) und soll den normalen Text aus dem HTML extrahieren:

void parse_html2(char *s, int n) {

int i;
//char res[1024];

char *tmp = (char*) malloc(n + 1);
if (tmp == NULL) {
fprintf(stderr, "malloc-error: %s\n", strerror(errno) );
exit(1);
}

for (i = 0; i < n; i++) {

if (s[i] == '>') {

++i;
while (s[i] != '<') {

tmp[i] += s[i];
//printf("%c", tmp[i]);
i++;
}

tmp[strlen(tmp) + 1] = 0;
printf("%s\n", tmp);

} // end if
} // end for
}

Wenn ich das Programm starte, erhalte ich diesen Fehler:

Speicherzugriffsfehler (Speicherabzug geschrieben)

Was ist falsch?

Gruss Thomas
Marcel Müller
2013-11-04 16:16:56 UTC
Permalink
Moin,
Post by t***@gmail.com
void parse_html2(char *s, int n) {
int i;
//char res[1024];
char *tmp = (char*) malloc(n + 1);
if (tmp == NULL) {
fprintf(stderr, "malloc-error: %s\n", strerror(errno) );
exit(1);
}
for (i = 0; i< n; i++) {
if (s[i] == '>') {
++i;
while (s[i] != '<') {
tmp[i] += s[i];
//printf("%c", tmp[i]);
i++;
}
tmp[strlen(tmp) + 1] = 0;
printf("%s\n", tmp);
} // end if
} // end for
}
Speicherzugriffsfehler (Speicherabzug geschrieben)
Was ist falsch?
Die innere While-Schleife endet nicht am Ende des Strings, sondern macht
einen Zugriff über s und tmp hinaus. Und dann knallt es.

Das ist aber sicher nicht der einzige Fehler. Mindestens mal die Zeile
tmp[i] += s[i]; ist suspekt. Hier werden ASCII-Codes addiert. Ferner
wird auf uninitialisierten Speicher (tmp[i]) zugegriffen.


Marcel
Friedhelm Waitzmann
2013-11-04 17:16:19 UTC
Permalink
Post by t***@gmail.com
Hallo,
hier ist meine Funktion. Sie erhält als Parameter einen
HTML-String (char *s) und soll den normalen Text aus dem HTML
1 void parse_html2(char *s, int n) {
2
3 int i;
4 //char res[1024];
5
6 char *tmp = (char*) malloc(n + 1);
7 if (tmp == NULL) {
8 fprintf(stderr, "malloc-error: %s\n", strerror(errno) );
9 exit(1);
10 }
11
12 for (i = 0; i < n; i++) {
13
14 if (s[i] == '>') {
15
16 ++i;
17 while (s[i] != '<') {
18
19 tmp[i] += s[i];
20 //printf("%c", tmp[i]);
21 i++;
22 }
23
24 tmp[strlen(tmp) + 1] = 0;
25 printf("%s\n", tmp);
26
27 } // end if
28 } // end for
29 }
Post by t***@gmail.com
Speicherzugriffsfehler (Speicherabzug geschrieben)
Was ist falsch?
Du schreibst in Zeile 24 strlen(tmp). Also erwartest du dort,
dass an der Addresse tmp eine Zeichenfolge zu finden ist, die am
Ende mit einem Zeichen NUL abgeschlossen ist, auch wenn sie
unbekannte Länge haben mag. Ist das der Fall?

Hinter dieses Abschluss‐NUL schreibst du ein weiteres NUL. Das
ist nur erlaubt, wenn du sicherstellen kannst, dass dieses
weitere innerhalb des in Zeile 6 angelegten Speicherbereichs
geschrieben wird, d.h., es muss gelten

strlen(tmp) + 1 < n + 1

bzw.

strlen(tmp) < n.

Dass diese Bedingung immer eingehalten wird, kann ich nicht
erkennen. Also kann dein Zugriffsfehler in dieser Zeile
auftreten.

Noch weitere Gesichtspunkte:

* Das Unterprogramm soll aus dem HTML‐Text etwas extrahieren.
Mit anderen Worten: Im HTML–Text gibt es Zeichen, die in die
Ausgabe übernommen werden, und solche, die es nicht werden.

Soll im Speicherbereich tmp[...] für jedes nicht in die Ausgabe
übernommene Zeichen eine Lücke gelassen werden? Wohl kaum. Also
brauchst du zur Adressierung der Zeichen in tmp[...] eine weitere
Variable j, die nur dann um eins heraufgezählt wird, wenn
wirklich ein Zeichen in tmp[...] hinzugefügt wird.

* Die Variablen n und i (Zeilen 1 und 3) hast du vom Typ int
angelegt. Nimm stattdessen size_t. Der Wertebereich von
size_t ist so ausgelegt, dass er jegliches Offset zur
Adressierung innerhalb von Arrays enthält. int kann meines
Wissens zu klein sein.

* Zeile 19: tmp[i] += s[i];
Warum willst du zur Ordnungsnummer des Zeichens tmp[i] die des
Zeichens s[i] dazuzählen? Willst du nur Zeichen aufsammeln,
ist tmp[i] = s[i] das Richtige.

Friedhelm.
--
Bitte in die Adressierung auch meinen |Please put my full name also into
Vor- u. Nachnamen stellen z.B. |the recipient like
Friedhelm Waitzmann <***@example>, (Friedhelm Waitzmann) ***@example,
"Waitzmann, Friedhelm" <***@example>
t***@gmail.com
2013-11-05 11:32:46 UTC
Permalink
Post by t***@gmail.com
Hallo,
void parse_html2(char *s, int n) {
int i;
//char res[1024];
char *tmp = (char*) malloc(n + 1);
if (tmp == NULL) {
fprintf(stderr, "malloc-error: %s\n", strerror(errno) );
exit(1);
}
for (i = 0; i < n; i++) {
if (s[i] == '>') {
++i;
while (s[i] != '<') {
tmp[i] += s[i];
//printf("%c", tmp[i]);
i++;
}
tmp[strlen(tmp) + 1] = 0;
printf("%s\n", tmp);
} // end if
} // end for
}
Speicherzugriffsfehler (Speicherabzug geschrieben)
Was ist falsch?
Gruss Thomas
Danke für dir Hilfe!!!

Gruss Thomas
t***@gmail.com
2013-11-05 13:09:54 UTC
Permalink
Post by t***@gmail.com
Hallo,
void parse_html2(char *s, int n) {
Danke nochmals. It works now (hier dass Resultat):

void parse_html3(char *s) {

size_t i, j;
char *res = (char*)malloc(strlen(s) + 1);

for (i = 0, j = 0; i < strlen(s); i++) {

if (s[i] == '>') {

while (s[++i] != '<' && s[i] != 0) {

if (s[i] == '\n')
continue;
res[j] = s[i];
++j;
}

} // end if
} // end for

printf("%s\n", res);
}
Friedhelm Waitzmann
2013-11-05 18:20:45 UTC
Permalink
Post by t***@gmail.com
hier ist meine Funktion. Sie erhält als Parameter einen
HTML-String (char *s) und soll den normalen Text aus dem HTML
1 void parse_html3(char *s) {
2
3 size_t i, j;
4 char *res = (char*)malloc(strlen(s) + 1);
5
6 for (i = 0, j = 0; i < strlen(s); i++) {
7
8 if (s[i] == '>') {
9
10 while (s[++i] != '<' && s[i] != 0) {
11
12 if (s[i] == '\n')
13 continue;
14 res[j] = s[i];
15 ++j;
16 }
17
18 } // end if
19 } // end for
20
21 printf("%s\n", res);
22 }

Das glaub' ich Dir noch nicht so ganz:

* In Zeile 4 wird dynamischer Speicher angefordert, und am Ende
des Unterprogramms in Zeile 22 wird der letzte existierende
Zeiger darauf (in Variable res) vergessen. Der Speicher
versumpft. Das Programm weiß nicht mehr, dass es ihn gibt.
Also: Gib ihn an die Speicherverwaltung zurück.

Folgendes vor Zeile 22 einfügen:

21.1 free(res); res = NULL;

Die Zuweisung

res = NULL;

ist überflüssig. Nur hat es sich bei fortlaufenden
Programmänderungen schnell, dass man einen Zeiger auf
dynamischen Speicher verwendet, der längst ungültig geworden
ist, indem man den Speicher mit free() freigegeben hat. Diese
Zuweisung sorgt dafür, dass bei eventuell noch folgender
Verwendung des Zeigerwertes in res der Nullzeiger
verwendet wird, was üblicherweise vom Betriebssystem als
Adressierungsfehler erkannt wird.

Falls in Zeile 4 kein Speicher angefordert werden konnte, wird
res ein Nullzeiger, NULL, zugewiesen. Bevor du den
dynamischen Speicher verwendest, solltest Du prüfen, ob Du
überhaupt welchen erhalten hast:

5.1 if (res == NULL) {
5.2 printf("Leider kein Speicher verfuegbar.\n");
5.3 } else {

Dann folgen Zeilen 6 bis 21.1 (der Übersicht halber etwas
eingerückt, weil sie ja Teil des Else‐Zweiges sind):

6 for (i = 0, j = 0; i < strlen(s); i++) {
...
21 printf("%s\n", res);
21.1 free(res); res = NULL;

und Zeile 21.2 (Abschluss des in Zeile 5.4 begonnenen Else‐Teils)
21.2 }

* Der Parameter s (Zeile 1) ist ein Zeiger auf ein *änderbares*
char. Bei folgendem Programmstück

{
const char *testme =
"<HTML><BODY><P>Einmal ist eine Muecke nachts "
"durch den Wald gesummt\n"
"und hat sich etwas zu stechen gesucht.\n"
"Aber sie hat nur das Wiesel gefunden."
"</P></BODY></HTML>";

parse_html3(testme);
}

sollte der Compiler zum Aufruf von parse_html3 zumindest ein
Warning, wenn nicht gar einen Fehler liefern: testme ist ein
Zeiger auf ein String‐Literal und damit auf einen
unveränderbaren (const) Speicherbereich. In der Deklaration
von parse_html3 aber wird es versäumt, zuzusichern, dass
parse_html3 den Speicherbereich, auf den der Parameter s
zeigt, nicht verändert.

Ändere Zeile 1 ab in

1 void parse_html3(const char *s) {

Mit dieser Änderung weiß der Compiler im obigen Aufruf

parse_html3(testme);

zweierlei:

1., dass er das String‐Literal dem Unterprogramm zur
pfleglichen (d.h. nicht verändernden) Behandlung anvertrauen
darf

2., dass irgendeine Zuweisung s[...] = ...; innerhalb von
parse_html3 auf jeden Fall ein Fehler ist.

Die Änderungsvorschläge zusammengefasst:

1 void parse_html3(const char *s) {
2
3 size_t i, j;
4 char *res = (char*)malloc(strlen(s) + 1);
5
5.1 if (res == NULL) {
5.2 printf("Leider kein Speicher verfuegbar.\n");
5.3 } else {
6 for (i = 0, j = 0; i < strlen(s); i++) {
7
8 if (s[i] == '>') {
9
10 while (s[++i] != '<' && s[i] != 0) {
11
12 if (s[i] == '\n')
13 continue;
14 res[j] = s[i];
15 ++j;
16 }
17
18 } // end if
19 } // end for
20
21 printf("%s\n", res);
21.1 free(res); res = NULL;
21.2 }
22 }

* Noch etwas zum selben Verwendungsbeispiel:

(1) Welches Zeichen gibt das Programm im folgenden Ausgabebeispiel
an der Stelle des Fragezeichens aus?

durch den Wald gesummt?und hat sich etwas zu stechen gesucht.

(2) Füge am Anfang des Programms

#include <string.h> /* Für memset() */

ein und zwischen

const char *testme =
"<HTML><BODY><P>Einmal ist eine Muecke nachts "
"durch den Wald gesummt\n"
"und hat sich etwas zu stechen gesucht.\n"
"Aber sie hat nur das Wiesel gefunden."
"</P></BODY></HTML>";

und

parse_html3(testme);

noch den folgenden Teil, der etwas dynamischen Speicher
anfordert, beschreibt und wieder freigibt:

{
size_t size = strlen(testme) + 1 + 2;
/* Speicher für zwei Zeichen mehr (einschließlich der
* Abschluss‐'\0') als für testme[...] gebraucht würde.
*/
char *etwasSpeicher = malloc (size);

if (etwasSpeicher != NULL) {
/* Speicher mit size-1 Xen belegen
*/
memset(etwasSpeicher,'X',size-1);
/* Abschluss‐'\0' setzen
*/
etwasSpeicher[size-1] = '\0';
printf("%s\n", etwasSpeicher);
free(etwasSpeicher);
}
}

Erscheinen in der Ausgabe des Aufrufs

parse_html3(testme);

ein paar X?
--
Bitte in die Adressierung auch meinen |Please put my full name also into
Vor- u. Nachnamen stellen z.B. |the recipient like
Friedhelm Waitzmann <***@example>, (Friedhelm Waitzmann) ***@example,
"Waitzmann, Friedhelm" <***@example>
Claus Reibenstein
2013-11-05 18:29:55 UTC
Permalink
Post by Friedhelm Waitzmann
4 char *res = (char*)malloc(strlen(s) + 1);
¯¯¯¯¯¯¯

Der Cast ist nicht nur überflüssig, sondern kaschiert bestenfalls das
fehlende #include <stdlib.h>.

Gruß
Claus
Georg Bauhaus
2013-11-05 17:07:51 UTC
Permalink
Post by t***@gmail.com
if (s[i] == '>') {
++i;
while (s[i] != '<') {
Das Zeichen '>' ist nicht nur am Tag-Ende, oder auch freistehend
im Text, sondern auch in Attributwerten, in Kommentaren,
in CDATA-Abschnitten, oder in processing instructions
erlaubt. Von daher würde ich überlegen, ob ggf. falsche
Treffer nicht zuviel Ärger und Aufwand verursachen werden.
Insofern es ja schon HTML-Parser für C gibt ... Es sei denn,
das ganze dient der Übung.
Alexander Langer
2013-11-05 18:00:26 UTC
Permalink
Post by Georg Bauhaus
Das Zeichen '>' ist nicht nur am Tag-Ende, oder auch freistehend
im Text, sondern auch in Attributwerten, in Kommentaren,
in CDATA-Abschnitten, oder in processing instructions
erlaubt. Von daher würde ich überlegen, ob ggf. falsche
Treffer nicht zuviel Ärger und Aufwand verursachen werden.
Insofern es ja schon HTML-Parser für C gibt ... Es sei denn,
das ganze dient der Übung.
Richtig, der Parser ist fehlerhaft. Sowas kann man auch nicht in eine
kleine Funktion pressen. Ich empfehle eine fertige Bibliothek zu nutzen.
Auch im Rahmen einer Übung wäre die Aufgabenstellung bekloppt.
t***@gmail.com
2013-11-06 11:09:10 UTC
Permalink
Danke Männer!!! Alles sehr hilfreich.

Gruss Thomas
t***@gmail.com
2013-11-11 12:28:36 UTC
Permalink
Hallo,

habe auch eine Funktion geschrieben, die jetzt JavaScript parst:

char* delete_js(char *s) {

int i, j;
char *js_beg = "<script ", *js_end = "</script>";

char *res = malloc(strlen(s) + 1);
char *pos_beg = strstr(s, js_beg);
char *pos_end = strstr(s, js_end);

int for_len = strlen(s) - strlen(pos_beg);
int pos = strlen(s) - strlen(pos_end) + strlen(js_end);

for (i = 0, j = 0; i <= for_len; i++) {

if (i == for_len) {

while (i < pos) i++;

pos_beg = strstr(s + pos, js_beg);
pos_end = strstr(s + pos, js_end);

if (pos_beg != NULL && pos_end != NULL) {
for_len = strlen(s) - strlen(pos_beg);
pos = strlen(s) - strlen(pos_end) + strlen(js_end);
}

} else {
res[j++] = s[i];
}
} // end for

s += pos;
//printf("%d\n", j);
int x = 0;
while (s[x]) {
res[j++] = s[x], x++;
}

return res;
}

Vielleicht braucht jemand so etwas. Kann man einfach in parse_html3() einbauen.

o-o

Thomas
Georg Bauhaus
2013-11-11 14:47:31 UTC
Permalink
Post by t***@gmail.com
Hallo,
char* delete_js(char *s) {
...
Post by t***@gmail.com
return res;
}
Vielleicht braucht jemand so etwas. Kann man einfach in parse_html3() einbauen.
Wo wird's denn eingesetzt werden?
t***@gmail.com
2013-11-11 21:41:31 UTC
Permalink
Naja, kann man als Filter in jedem Html-Parser einsetzen. ZUm Beispiel in der weiter oben Funktion parse_html3():

char* parse_html3(char *s) {

size_t i, j;
char *res = malloc(strlen(s) + 1);

s = delete_js(s);

...
...

return res;
}
Georg Bauhaus
2013-11-12 08:48:52 UTC
Permalink
Nicht so gern in der momentanen Fassung. Probier's mal mit dieser Eingabe:

<html>
<head>
<title>Ganz schön lang</title>
<script
type="text/javascript">
// <![CDATA[
Spy.go();
// ]]>
</script>
</head>
<body>
</body>
</html>

Oder dieser:

html>
<head>
<title>Ganz schön lang</title>
<Script type="text/javascript">
// <![CDATA[
Spy.go();
// ]]>
</script>
</head>
<body>
</body>
</html>

Auch mit dieser:

<html>
<head>
<title>Ganz schön lang</title>
<Script title="Rumpelstilzchen">
<script type="text/javascript">
// <![CDATA[ </script>
Spy.go();
// ]]>
</script>
</head>
<body>
</body>
</html>
t***@gmail.com
2013-11-21 11:09:27 UTC
Permalink
Post by Georg Bauhaus
<html>
<head>
<title>Ganz schön lang</title>
<script
type="text/javascript">
// <![CDATA[
Spy.go();
// ]]>
</script>
</head>
<body>
</body>
</html>
html>
<head>
<title>Ganz schön lang</title>
<Script type="text/javascript">
// <![CDATA[
Spy.go();
// ]]>
</script>
</head>
<body>
</body>
</html>
<html>
<head>
<title>Ganz schön lang</title>
<Script title="Rumpelstilzchen">
<script type="text/javascript">
// <![CDATA[ </script>
Spy.go();
// ]]>
</script>
</head>
<body>
</body>
</html>
Den zweiten Fall habe ich gelöst.

char* recplace(char* item, char* line) {

int i, length;
char* s_ptr;

length = strlen(item);
while ( (s_ptr = strstr(line, item)) != NULL) {

for (i = 0; i < length; i++) {
s_ptr[i] = tolower(s_ptr[i]);
}

s_ptr += strlen(item);
//recplace(item, s_ptr); /* RECURSIVE */
}

return line;
}
Helmut Schellong
2013-11-22 08:17:20 UTC
Permalink
***@gmail.com wrote:
[...]
Post by t***@gmail.com
Den zweiten Fall habe ich gelöst.
char* recplace(char* item, char* line) {
int i, length;
char* s_ptr;
length = strlen(item);
while ( (s_ptr = strstr(line, item)) != NULL) {
for (i = 0; i < length; i++) {
s_ptr[i] = tolower(s_ptr[i]);
}
s_ptr += strlen(item);
//recplace(item, s_ptr); /* RECURSIVE */
}
return line;
}
Ich verwende bei Parsern niemals solch ein Konzept,
sondern ich verwende einen switch(){}.
--
Mit freundlichen Grüßen
Helmut Schellong ***@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
t***@gmail.com
2013-11-23 13:36:02 UTC
Permalink
Post by Helmut Schellong
[...]
Den zweiten Fall habe ich gel�st.
char* recplace(char* item, char* line) {
int i, length;
char* s_ptr;
length = strlen(item);
while ( (s_ptr = strstr(line, item)) != NULL) {
for (i = 0; i < length; i++) {
s_ptr[i] = tolower(s_ptr[i]);
}
s_ptr += strlen(item);
//recplace(item, s_ptr); /* RECURSIVE */
}
return line;
}
Ich verwende bei Parsern niemals solch ein Konzept,
sondern ich verwende einen switch(){}.
--
Mit freundlichen Gr��en
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
Dann musst Du es auch zeigen! Nörgeln kann ich auch.
Helmut Schellong
2013-11-24 10:12:12 UTC
Permalink
[...]
Post by t***@gmail.com
Post by Helmut Schellong
Ich verwende bei Parsern niemals solch ein Konzept,
sondern ich verwende einen switch(){}.
Dann musst Du es auch zeigen! Nörgeln kann ich auch.
for (/*...*/; p<e; ++p) {
switch (z) {
case START : if (*p=='"') { z=STRING; continue; }
/*...*/
break;
case STRING: if (*p=='\'&&p[1]=='"') { ++p; continue; }
if (*p=='"') { z=START; continue; }
/*...*/
break;
/*...*/
}
/*...*/
}


switch (*p) {
ist auch möglich.
--
Mit freundlichen Grüßen
Helmut Schellong ***@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
t***@gmail.com
2013-11-26 20:42:07 UTC
Permalink
Post by Helmut Schellong
[...]
Post by Helmut Schellong
Ich verwende bei Parsern niemals solch ein Konzept,
sondern ich verwende einen switch(){}.
Dann musst Du es auch zeigen! N�rgeln kann ich auch.
for (/*...*/; p<e; ++p) {
switch (z) {
case START : if (*p=='"') { z=STRING; continue; }
/*...*/
break;
case STRING: if (*p=='\'&&p[1]=='"') { ++p; continue; }
if (*p=='"') { z=START; continue; }
/*...*/
break;
/*...*/
}
/*...*/
}
switch (*p) {
ist auch m�glich.
--
Mit freundlichen Gr��en
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
Cool!
t***@gmail.com
2013-11-26 20:43:47 UTC
Permalink
Post by Helmut Schellong
[...]
Post by Helmut Schellong
Ich verwende bei Parsern niemals solch ein Konzept,
sondern ich verwende einen switch(){}.
Dann musst Du es auch zeigen! N�rgeln kann ich auch.
for (/*...*/; p<e; ++p) {
switch (z) {
case START : if (*p=='"') { z=STRING; continue; }
/*...*/
break;
case STRING: if (*p=='\'&&p[1]=='"') { ++p; continue; }
if (*p=='"') { z=START; continue; }
/*...*/
break;
/*...*/
}
/*...*/
}
switch (*p) {
ist auch m�glich.
--
Mit freundlichen Gr��en
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
Nicht schlecht;-)
t***@gmail.com
2013-11-13 12:00:50 UTC
Permalink
Muss also wieder Hand anlegen;-). Danke für die Tipps.

o-o

Thomas
Stefan Ram
2014-01-10 12:25:23 UTC
Permalink
Post by t***@gmail.com
hier ist meine Funktion. Sie erhält als Parameter einen
HTML-String (char *s) und soll den normalen Text aus dem HTML
Das klassische HTML basiert auf SGML. Wer SGML parsen will,
sollte zunächst ISO 8879:1986 lesen. XHTML und HTML5 haben
jeweils wieder eine andere Grundlage.

Loading...