use utf8 in Modulen (hat auch was mit httputils zu tun)

Begonnen von viegener, 03 Juni 2016, 14:28:07

Vorheriges Thema - Nächstes Thema

viegener

Hi,
ich habe gerade ein Problem bei bestimmten telegram-Nutzern mit Umlauten. https://forum.fhem.de/index.php/topic,38328.msg457470.html#msg457470
Leider ist das utf8-Thema ja bei perl etwas undurchsichtig und auch fehlerbehaftet(alte Versionen).

Ich verstehe inzwischen auch das Problem, denn length liefert für Strings mit multibyte-chars unterschiedliche Werte zurück, je nachdem ob ein String intern eben als UTF8 (unicode chars als Länge) oder nicht (Länge in bytes) geflaggt ist.

Jetzt ist es so, dass es Module gibt, die use utf8; im Header (package scope) stehen haben und meinem Verständnis nach damit das Verhalten auch für andere Module ändern, wenn sie geladen werden, da diese Anweisung ja für den entsprechenden Scope gültig sind. http://perldoc.perl.org/utf8.html

Ich vermute nun, dass das Problem beim Schreiben in httputils auftritt:

syswrite gibt die Anzahl der Bytes zurück, length/substr prüft Anzahl der Zeichen (wenn utf8 geflaggt) und siehe da es passt nicht mehr...

2016.06.02 09:12:25 1: PERL WARNING: substr outside of string at FHEM/HttpUtils.pm line 403.
2016.06.02 09:12:25 1: PERL WARNING: Use of uninitialized value $data in numeric eq (==) at FHEM/HttpUtils.pm line 404.


Je nachdem ob use utf8 aktiv ist oder nicht verhält sich fhem hier anders, jetzt gibt es verschiedene Möglichkeiten und Kombinationen:

a) httputils könnte geändert werden dass entweder der socket mit :utf8 geöffnet wird oder ein use bytes in den funktionen verwendet wird

b) Ich kann use utf8 auch in meinem Modul verwenden (habe ich bisher noch nicht ausprobiert)

c) Ich kann eine Lösung in meinem Modul machen, die dafür sorgt, dass perl die Daten als Bytestring nimmt (muss ich noch recherchieren)

Ich habe ältere Threads zu dem Thema gefunden aber momentan versuche ich ja auch noch zu verstehen wie die Lösung aussehen könnte...



Kein Support über PM - Anfragen gerne im Forum - Damit auch andere profitieren und helfen können

rudolfkoenig

Muss zugeben, dass ich das Problem in der verlinkten Diskussion nicht verstanden habe.
Laut perldoc:
Zitat
       The "use utf8" pragma tells the Perl parser to allow UTF-8 in the
       program text in the current lexical scope [...]
       Do not use this pragma for anything else than telling Perl that your
       script is written in UTF-8.
Ich gehe davon aus, dass die interne Darstellung der _eingelesenen_ Daten davon unabhaengig ist.

Generell gilt (bis jetzt) in FHEM, dass die Module alle Daten im "one-byte" utf-8 Format halten/weitergeben sollten, so dass syswrite/gzip/etc sie ohne eine Konvertierung verarbeiten kann. Wenn jemand die Anzahl der dargestellten Buchstaben berechnen moechte, dann muss man die Daten evtl. konvertieren.

justme1968

das ganze ist leider ein größeres problem. ich hatte schon mal einen längeren beitrag dazu recherchiert aber dann doch nicht gepostet.

ein use utf8 im quelltext bewirkt nur das der quelltext an sich (und eventuell darin enthaltene statischen strings) als utf8 geparsed werden. die so erzeugten internen sind sind dann automatisch mutlibyte strings die als utf8 kodiert markiert sind.

das hat aber keine auswirkung auf das lesen und schreiben und auf dynamisch erzeugte strings. selbst wenn es etwas indirekt zufällig etwas bewirk wäre es die falsche lösung.

für strings die mit einem encoding versehen sind  zählt length die anzahl an zeichen (das was dargestellt werden würde). das ist das was man braucht um strings bei der ausgabe auszurichten.

für strings die nicht mit einem encoding versehen sind zählt length die anzahl an bytes. das können auch mehrere für ein zeichen sein. das ist das was man braucht um bei der ein und ausgabe zu zählen was z.b. übers netz geht.

beides anforderungen widersprechen sich zum teil.

fhem verwendet zur zeit intern für alles zwar das utf8 multibyte encoding, die strings werden aber nicht als utf8 markiert sondern sind einfach einer reihe von bytes.

das verhindert unter anderem das automatisch ausrichten, es verhindert das automatische konvertieren von einem encoding in ein anderes. es hat auch leider den nachteil das manchmal nicht klar ist welches encoding strings tatsächlich haben da perl beim start das interne default encoding scheinbar heuristisch bestimmt und es davon abhängt was als erstes verarbeitet wird. es kann sich von einem start zum nächsten ändern. 

es hat aber den vorteil das es z.b. beim zippen der durch fhemweb ausgelieferten daten nichts umkopiert werden muss und keine fehler produziert.

die eigentlich 'saubere' variante wäre überall echte mit dem jeweiligen encoding versehene strings zu verwenden und nur da eine reihenfolge von bytes wo es auch tatsächlich binäre daten sind. das wäre aber eine ziemliche umstellung die sich durch alle module zieht. damit würden auch umlaute in regex funktionieren. das ist dann aber wiederum von der perl version abhängig.

falls man die länge in zeichen (statt in bytes) braucht ist eine möglichkeit ist length() auf eine explizit mit decode_utf8 konvertierten version zählen zu lassen. du musst darauf achten das diese version nicht in readings oder sonst wo landet sonder die ursprüngliche byte liste.

in deinem fall ist aber glaube ich c) die aktuell einzige sinnvolle lösung.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

viegener

Zitat von: justme1968 am 03 Juni 2016, 15:58:04

in deinem fall ist aber glaube ich c) die aktuell einzige sinnvolle lösung.

gruss
  andre

Ja das war auch mein erster Ansatz, allerdings sollte das ja durch ein encode_utf8 erledigt werden und das führt interessanterweise bei zwei unterschiedlichen Nutzern zu unterschiedlichen Ergebnissen. Einmal werden dadurch die bereits vorhandenen Umlaute nochmals konvertiert (statt äüöß kommt äüöà an. Daher kam meine Vermutung, dass hier bei gleichem SoftwareStand die verwendeten Module (und damit vielleicht use utf8) eine Rolle spielt.

Deshalb muss ich wohl noch etwas weiter recherchieren.


Kein Support über PM - Anfragen gerne im Forum - Damit auch andere profitieren und helfen können

justme1968

auf den ersten blick schaut es nicht so aus als ob etwas zwei mal konvertiert wird, sondern das eine korrekte utf-8 bytefolge mit dem falschen encoding interpretiert wird.

die utf-8 bytefolge für ein ä z.b. ist c3 a4. wenn man das als latin-1 anschaut ist es genau das ä aus deinem beispiel. das gleiche gilt für ü, ö. ich vermute in deinem beispiel fehlt das letze zeichen/byte. ein ß sollte dort als ß erscheinen.

ich würde daher sagen das der string die richtige bytefolge hat aber im mit dem falschen browser encoding angeschaut wird. oder wenn sie im log stehen werden sie mit einem editor angeschaut der nicht auf utf-8 steht.




hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

viegener

#5
Hi Andre,
Ja, das war auch meine Auswertung. Der String enthält korrektes UTF-8 wird aber unter mir unbekannten Umständen als ISO8859-x interpretiert und dann natürlich beim encode in völlig andere Zeichen konvertiert. Ich konnte das bei mir ebenfalls erzwingen, wenn ich im Browser ein falsches Encoding erzwinge, aber auch nur dann. Bei den Benutzern tritt das Problem insbesondere auf, wenn das Versenden aus perl-code (!) erfolgt, daher die Vermutung mit use utf8. Hintergrund, wenn ein Stück perl-code im eval von fhem.pl ausgeführt wird, könnte ja die utf8-Einstellung eine Rolle spielen, denn das ist ja quasi "im sourcecode", ich konnte das aber bisher bei mir nicht nachstellen  :(

Probleme:
- Warum nimmt perl bei selbem Code im Modul manchmal an es ist schon UTF-8 und manchmal ISO8859-1/latin-1 (wenn nicht durch use utf8)?

- Wie kann man einen String unabhängig von dem obigen Problem sicher "encoden" ohne umzucodieren
--> Vermutlich muss ich soetwas wie is_utf8 einsetzen und dann entweder mit use bytes/use utf8 arbeiten

Ich habe mal einen ersten Test gemacht auf meiner Installation (mit einem dummy testdummy mit state "äöüß"):

{ use utf8;; my $msg=Value("testdummy");; return "msg= ".$msg."   length = ".length($msg)." is utf8=".(utf8::is_utf8($msg)?"on":"off");; }
liefert:
msg= äöüß length = 8 is utf8=off

{ use utf8;; my $msg=Value("testdummy");; utf8::upgrade($msg);;return "msg= ".$msg."   length = ".length($msg)." is utf8=".(utf8::is_utf8($msg)?"on":"off");; }
liefert:
msg= äöüß length = 8 is utf8=on

{ use utf8;; my $msg=encode_utf8(decode_utf8(Value("testdummy")));; return "msg= ".$msg."   length = ".length($msg)." is utf8=".(utf8::is_utf8($msg)?"on":"off");; }
liefert:
msg= äöüß length = 8 is utf8=off

Varianten mit nur decode oder nur encode führen bei mir zu falschen Strings.

Kein Support über PM - Anfragen gerne im Forum - Damit auch andere profitieren und helfen können

viegener

Zitat von: rudolfkoenig am 03 Juni 2016, 14:49:42
Generell gilt (bis jetzt) in FHEM, dass die Module alle Daten im "one-byte" utf-8 Format halten/weitergeben sollten, so dass syswrite/gzip/etc sie ohne eine Konvertierung verarbeiten kann. Wenn jemand die Anzahl der dargestellten Buchstaben berechnen moechte, dann muss man die Daten evtl. konvertieren.

Ja, das war auch meine Annahme und dann würde length ja die Anzahl Bytes liefern. Allerdings sieht es so aus, als ob auf bestimmten Installationen (Linux / z.T. gleiche perl-version) mein Modul sich anders verhält und unicode-Strings liefert, während bei mir "byte strings" mit demselben code kommen.

Also ist eigentlich die Ursache für diesen Unterschied ist eigentlich mein Problem (use utf8 nur als Vermutung)

Kein Support über PM - Anfragen gerne im Forum - Damit auch andere profitieren und helfen können

justme1968

welche erkenntnisse hast du denn inzwischen gewinnen können?

gruss
  andre

ps: ich habe übrigens auch bei exakt gleicher perl version bei mehreren starts manchmal unterschiedliches verhalten gesehen. vielleicht nimmt perl als internes default encoding was als erstes 'vorbei' kommt.

pps: ist dir eine einfache (ohne zusatzmodule auskommende) variante über den weg gelaufen um utf8 zeichen zu zählen? z.b. um bei sprintf vernünftig auszurichten.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

viegener

Zitat von: justme1968 am 18 Juni 2016, 23:15:28
welche erkenntnisse hast du denn inzwischen gewinnen können?

gruss
  andre

ps: ich habe übrigens auch bei exakt gleicher perl version bei mehreren starts manchmal unterschiedliches verhalten gesehen. vielleicht nimmt perl als internes default encoding was als erstes 'vorbei' kommt.

pps: ist dir eine einfache (ohne zusatzmodule auskommende) variante über den weg gelaufen um utf8 zeichen zu zählen? z.b. um bei sprintf vernünftig auszurichten.


Hallo Andre,
ich muss zugeben, ich bin nach 2 Wochen rumtesten immernoch ratlos gewesen. Die einzige Erkenntnis war, dass Encode und JSON sich in meinem Modul gegenseitig ins Gehege kommen, also die Verwendung beider Module bei ansonsten ähnlichen Umgebungen zu unerklärlichen Unterschieden führt.

Meine neueste Testversion, die bisher bei bisher unterschiedlichen Installation korrekt läuft geht zum Teil mit dem Holzhammer zu Werke, wie z.B.:


  my $json        = JSON->new->utf8;
  $ret = $json->utf8(0)->encode( $refkb );

  if ( utf8::is_utf8($ret) ) {
    utf8::downgrade($ret);
  }


Aber ich muss zugeben, die Verwendung von downgrade und is_utf8 halte ich für eine Bankrotterklärung meinerseits  :(

Ich befasse mich nun seit 20 Jahren mit Codepages und dabei auch Konvertierungen in Exoten und bidirektionale Sprachen, aber hier fehlt mir das Element zu sagen, wenn das reingeht, dann steht dies nacher drin..

Nach den bisherigen positiven Rückmeldungen der Tester werde ich das wohl produktiv schalten, aber es gefällt mir nicht...

Gruss,
Johannes
Kein Support über PM - Anfragen gerne im Forum - Damit auch andere profitieren und helfen können