amiga-news ENGLISH VERSION
.
Links| Forum| Kommentare| News melden
.
Chat| Umfragen| Newsticker| Archiv
.

amiga-news.de Forum > Programmierung > php, XML und Umlaute [ - Suche - Neue Beiträge - Registrieren - Login - ]

-1- [ - Beitrag schreiben - ]

08.10.2009, 16:16 Uhr

Mad_Dog
Posts: 1944
Nutzer
Hallo,

Ich habe mal wieder ein kleines php-Problem, daß mich verzweifeln lässt.

Folgendes Grundproblem: Es sollen Textstrings aus einer XML-Datei gelesen werden. Diese Text-Strings stehen zwischen Tags. z.B. so:
xml code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<sonderzeichen_test>
	<dummy type="string">Età dei bambini</dummy>
	<test type="string">località</test>
	<deutsch type="string">Blöde Umlaute</deutsch>
</sonderzeichen_test>


Für das echte Programm sind diese XML-Datein noch etwas komplexer.
Zum parsen benutze ich die "eingebaute" SAX-Api von php5. Da es im echten Programm Probleme gibt, habe ich mir eine stark vereinfachte Testklasse geschrieben, die einfach nur Text ausgibt:
php code:
<?php

/*
 *   xml_file_test
 *
 */

class xml_file_test
{
	// Konstruktor

	function __construct($filename)
	{
		$xml_parser = xml_parser_create("ISO-8859-1");

		//  Alle Event-Funktionen befinden sich innerhalb dieses Objekts
		xml_set_object($xml_parser,$this);

		xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
		xml_set_element_handler($xml_parser, "startElement", "endElement");
		xml_set_character_data_handler($xml_parser, "characterData");

		xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING,"ISO-8859-1");
		//xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
		
		if (!($fp = fopen($filename, "rb")))
		{
    		die("could not open XML input $filename");
		}

		while ($data = fread($fp, 4096))
		{
    		if (!xml_parse($xml_parser, $data, feof($fp)))
    		{
        		die(sprintf("XML error: %s at line %d",
            	        xml_error_string(xml_get_error_code($xml_parser)),
                	    xml_get_current_line_number($xml_parser)));
    		}
		}

		xml_parser_free($xml_parser);
	}

	// Methoden

	// Behandelt Start-Tag
	function startElement($parser, $name, $attribs)
	{
    	echo "xml_file_test::startElement()n";
		echo "name = $namen";

    	// Attribute dieses Elements durchgehen
    	if (count($attribs))
    	{
        	foreach ($attribs as $k => $v)
        	{
            	echo "t$k = $vn";
        	}
    	}

	}

	// Behandelt End-Tag
	function endElement($parser, $name)
	{
		echo "xml_file_test::endElement()n";  
	}

	// Behandelt Daten innerhalb eines Tags
	function characterData($parser, $data)
	{
		echo "xml_file_test::characterData()n";
    	echo "data = $datan";
	}

}
?>


Und ein kleines Testprogramm, welches diese Klasse aufruft:
php code:
<?php

require_once("xml_file_test.inc.php");

$filename = "xml_testfile.xml";

$x = new xml_file_test($filename);

?>


Das Proble, was sich hier einstellt, ist folgendes:

Die Strings, die zwischen den Tags stehen, also z.b. "Blöde Umlaute" werden fälschlicherweise zerhackt - und zwar genau dort wo Umlaute oder Akzente auftreten. So wird z.B. statt "Blöde Umlaute" "Bl" und "öde Umlaute" ausgegeben.

Woran liegt das?

Die Ausgabe des Testprogramms sieht wie folgt aus:

code:
xml_file_test::startElement()
name = SONDERZEICHEN_TEST
xml_file_test::characterData()
data =

xml_file_test::startElement()
name = DUMMY
        TYPE = string
xml_file_test::characterData()
data = Et
xml_file_test::characterData()
data = à dei bambini
xml_file_test::endElement()
xml_file_test::characterData()
data =

xml_file_test::startElement()
name = TEST
        TYPE = string
xml_file_test::characterData()
data = localit
xml_file_test::characterData()
data = à 
xml_file_test::endElement()
xml_file_test::characterData()
data =

xml_file_test::startElement()
name = DEUTSCH
        TYPE = string
xml_file_test::characterData()
data = Bl
xml_file_test::characterData()
data = öde Umlaute
xml_file_test::endElement()
xml_file_test::characterData()
data =

xml_file_test::endElement()


Bin etwas ratlos :(

--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

08.10.2009, 17:28 Uhr

DrNOP
Posts: 4118
Nutzer
Zitat:
Original von Mad_Dog:
Die Strings, die zwischen den Tags stehen, also z.b. "Blöde Umlaute" werden fälschlicherweise zerhackt - und zwar genau dort wo Umlaute oder Akzente auftreten. So wird z.B. statt "Blöde Umlaute" "Bl" und "öde Umlaute" ausgegeben.

Woran liegt das?

Das war jetzt fast ein wenig zuviel Information für diese Frage... ;)

Ich hatte mal das gleiche Problem mit Expat. Falls dein Parser darauf basiert ist's klar woher es kommt, und andernfalls kann er ja immer noch gleich implementiert sein:

Es scheint eine Eigenschaft von Expat zu sein, Entities einzeln zurückzugeben. Ah, ich seh' grade, bei dir ist es wieder anders als beim Expat: Ich bekam damals drei Teile zurück: "Bl", "ö", "de Umlaute".

Jedenfalls scheint der Parser bei Umlauten etwas genauer hinschauen zu müssen und trennt den String deshalb an dieser Stelle. Immerhin sind das die Dinge, die er je nach eingestellter Zeichencodierung anders konvertieren muß. Und Umlaute etc. kannst du ja auch als Entity "ö" und so schreiben.

Der einzige Ausweg den ich gefunden habe war, alles zusammenzusetzen was mir der Parser zwischen startElement und endElement liefert, auf das endElement zu "horchen" und den Ergebnisstring erst mit dem endElement abzuschließen.

In deinem Beispiel scheinst du lokale Zeichen aus verschiedenen Sprachen zu benutzen? Wäre es da nicht besser, von "ISO-8859-1" auf "UTF-8" umzustellen?
--
Signaturen mit mehr als zwei Zeilen gehen mir auf den Wecker

[ - Antworten - Zitieren - Direktlink - ]

08.10.2009, 18:32 Uhr

akl
Posts: 265
Nutzer
@Mad_Dog:

Ich würde einfach mal behaupten, das Problem liegt in "echo" - denn im Gegensatz zum XML-Parser, der anscheinend richtig konfiguriert ist, weiss "echo" vermutlich nichts von ISO-8859-1.

Die Lösung könnte sein:

echo htmlentities($data);

[ - Antworten - Zitieren - Direktlink - ]

08.10.2009, 20:25 Uhr

Holger
Posts: 8116
Nutzer
@Mad_Dog:
Ich mach nur selten was mit php, allerdings scheint der SAX-Parser ja sprachunabhängig einige Gemeinsamkeiten zu haben. Da würde ich mal darauf tippen, dass es schlicht keine Garantie gibt, dass zusammenhängender Text in genau einem characterData()-Aufruf resultiert.

Das Aufsplitten kann Dir also nicht nur, wie hier, aufgrund interner Behandlung der Zeichen (hängt vermutlich mit dem verwendeten regulären Ausdruck zusammen) passieren, sondern auch dann, wenn der Text zu groß für den verwendeten Puffer wird.

Du musst also damit leben, dass es mehr als einen Aufruf geben kann, und den Handler so schreiben, dass er damit klarkommt.

Viel Spaß ;)

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

08.10.2009, 20:28 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von akl:
Ich würde einfach mal behaupten, das Problem liegt in "echo" - denn im Gegensatz zum XML-Parser, der anscheinend richtig konfiguriert ist, weiss "echo" vermutlich nichts von ISO-8859-1.

Ausgeschlossen. Echo würde nicht mehrmals "xml_file_test::characterData()" ausgeben. Das weist schon auf mehrere Funktionsaufrufe hin.
--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

08.10.2009, 20:52 Uhr

Mad_Dog
Posts: 1944
Nutzer
Zitat:
Original von Holger:
Zitat:
Original von akl:
Ich würde einfach mal behaupten, das Problem liegt in "echo" - denn im Gegensatz zum XML-Parser, der anscheinend richtig konfiguriert ist, weiss "echo" vermutlich nichts von ISO-8859-1.

Ausgeschlossen. Echo würde nicht mehrmals "xml_file_test::characterData()" ausgeben. Das weist schon auf mehrere Funktionsaufrufe hin.

Eben. Und genau das hat mich gewundert.
Ich bin davon ausgegangen, daß "Blöde Umlaute" eben ein Textelement (oder in DOM gesprochen: Ein Textknoten) sein müsste und habe wie blöde nach einem Fehler (Zeichencodierung usw.) gesucht.

Als Workarround habe ich dann in etwa das gemacht, was DrNop vorgeschlagen hat: Ein Attribut in der Klasse definiert, welches als Pufferspeicher herhalten muß. Wenn ein neuer Tag gefunden wird, wird dieser Puffer geleert (d.h. auf einen leeren String gesetzt). Der Handler für characterData klebt alle Strings, die er findet zunächst an den Inhalt dieses Puffers. Wenn dann ein End-Tag erkannt wird, wird der inhalt des Puffers als Text übernommen. Für solche einfachen XML-Dateien wie im Beispiel funktioniert das zumindest.

Ich frage mich allerdings, ob das so sein soll, daß "Blöde Umlaute" als mehrere Textelemente gewertet wird, oder ob das ein Bug des SAX-Parsers ist, denn dieses Verhalten tritt unabhängig von der verwendeten Zeichencodierung auf.


--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

09.10.2009, 09:40 Uhr

DrNOP
Posts: 4118
Nutzer
Zitat:
Original von Mad_Dog:
Ich frage mich allerdings, ob das so sein soll, daß "Blöde Umlaute" als mehrere Textelemente gewertet wird, oder ob das ein Bug des SAX-Parsers ist, denn dieses Verhalten tritt unabhängig von der verwendeten Zeichencodierung auf.

Wie gesagt, beim Expat ist es genauo und wird dort nicht als Fehler, sondern als Feature gewertet.
--
Signaturen mit mehr als zwei Zeilen gehen mir auf den Wecker

[ - Antworten - Zitieren - Direktlink - ]

09.10.2009, 10:06 Uhr

Thore
Posts: 2266
Nutzer
Mal eine Frage die in eine andere Richtung geht...
speicherst Du den xml Text als ISO-8859-1 ab oder als UTF-8?

Was anderes:
xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1);

mal ausprobiert?

[ - Antworten - Zitieren - Direktlink - ]

09.10.2009, 12:01 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von Mad_Dog:
Ich bin davon ausgegangen, daß "Blöde Umlaute" eben ein Textelement (oder in DOM gesprochen: Ein Textknoten) sein müsste und habe wie blöde nach einem Fehler (Zeichencodierung usw.) gesucht.

Ja das ist ja genau der Unterschied zwischen dem SAX-Parser und einem DOM-Builder. Es ist genau die Aufgabe des DOM-Builders, dafür zu sorgen, dass alle aufeinanderfolgenden Text-Segmente in genau einem Knoten resultieren.

Der SAX-Parser ist eine Ebene tiefer angesiedelt. Dafür benötigt er weniger Speicher und hat weniger Latenzen.
Zitat:
Als Workarround habe ich dann in etwa das gemacht, was DrNop vorgeschlagen hat: ...
Das ist dann die richtige Vorgehensweise, nicht nur ein workaround.
Zitat:
Ich frage mich allerdings, ob das so sein soll, daß "Blöde Umlaute" als mehrere Textelemente gewertet wird, oder ob das ein Bug des SAX-Parsers ist, denn dieses Verhalten tritt unabhängig von der verwendeten Zeichencodierung auf.
Man könnte es als Fehler werten, der allerdings nur die Performance betrifft, da mehr call-back Aufrufe getätigt werden als nötig.

Aber probier ruhig mal ein XML-File ohne Umlaute aber mit extrem großen Text-Elementen. Du wirst mit ziemlicher Sicherheit ab einer bestimmten Größe auch da mehrere Rückrufe bekommen.
Das muss nicht mal an einer festen Grenze passieren, da die restlichen XML-Daten höchstwahrscheinlich in den gleichen Puffer geladen werden.


--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

09.10.2009, 12:09 Uhr

akl
Posts: 265
Nutzer
@Holger:

Ok, stimmt.

Dann ist die Portierung fehlerhaft, denn es ist mehr als unsinnig, wenn PHP die Einstellung eines Zeichensatzes explizit erlaubt und fordert (es ist sogar festgelegt, dass der Ausgabezeichensatz per Default dem Eingabezeichensatz entspricht) und sich dann nicht daran hält...

Was ist das überhaupt für eine Version und für welche Plattform?

[ - Antworten - Zitieren - Direktlink - ]

09.10.2009, 12:11 Uhr

akl
Posts: 265
Nutzer
@Mad_Dog:

>Ich bin davon ausgegangen, daß "Blöde Umlaute" eben ein Textelement
>(oder in DOM gesprochen: Ein Textknoten) sein müsste und habe wie
>blöde nach einem Fehler (Zeichencodierung usw.) gesucht.

Muss characterData() denn einen Knoten zwangsläufig immer in einem Stück liefern? Offensichtlich doch nicht.

[ - Antworten - Zitieren - Direktlink - ]


-1- [ - Beitrag schreiben - ]


amiga-news.de Forum > Programmierung > php, XML und Umlaute [ - Suche - Neue Beiträge - Registrieren - Login - ]


.
Impressum | Datenschutzerklärung | Netiquette | Werbung | Kontakt
Copyright © 1998-2024 by amiga-news.de - alle Rechte vorbehalten.
.