Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

wide charset und normal charset

8 views
Skip to first unread message

Nils Müller

unread,
Apr 4, 2012, 12:19:16 PM4/4/12
to
Hallo,

ich habe folgendes Problem. Ich möchte eine Zeichenkette ab einer bestimmten Position
abschneiden. In unterem Beispiel, nach dem siebten Zeichen:

my $orig_name = "おもしろネタ速報";
my $name_size_max = "7";

my $name = wide_char($orig_name);

sub wide_char
{
my $orig_name = $_[0];
my $name = decode('UTF-8', $orig_name);
$name_size_max = int($name_size_max / 2)+1;
$name = substr($name,0,$name_size_max);
return $name;
}

Meine obige Routine funktioniert soweit. Falls ich aber lateinische
Zeichen mit in der Zeichenkette habe, dann funktioniert das nicht
mehr korrekt > my $orig_name = "testおもしろネタ速報";

Ich weiss auch, dass es an der Rundung mittels int() liegt. Aber
ich finde keine Lösung für das Problem. Hat jemand eine Idee?

Bjoern Hoehrmann

unread,
Apr 4, 2012, 2:21:47 PM4/4/12
to
* Nils Müller wrote in de.comp.lang.perl.misc:
>my $name_size_max = "7";
>
>my $name = wide_char($orig_name);
>
>sub wide_char
>{
> my $orig_name = $_[0];
> my $name = decode('UTF-8', $orig_name);
> $name_size_max = int($name_size_max / 2)+1;
> $name = substr($name,0,$name_size_max);
> return $name;
>}
>
>Meine obige Routine funktioniert soweit.

"Wide character" ist ein überholter Begriff aus den 1990ern; du scheinst
irgendwie davon auszugehen, dass diese Zeichen doppelt so breit sind wie
"normale" Zeichen. Das ist nicht der Fall, ein "ö" in $name ist genauso
"breit" wie ein "o" zumindest was substr und length und derlei angeht.
Das liegt vor allem an decode(), würdest du das nicht benutzen dann wäre
ein "ö" doppelt so lang wie ein "o", das Euro-Zeichen drei mal so lang,
und diverse asiatische Schriftzeichen vier mal so lang. In bytes, wohl-
gemerkt, und ohne das decode() würde Perl grob gesagt die Zeichenkette
als Byte-Kette interpretieren. Wenn du $name_size_max einfach auf `7`
lässt, dann funktioniert das Skript wie "gewünscht".
--
Björn Höhrmann · mailto:bjo...@hoehrmann.de · http://bjoern.hoehrmann.de
Am Badedeich 7 · Telefon: +49(0)160/4415681 · http://www.bjoernsworld.de
25899 Dagebüll · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/

Nils Müller

unread,
Apr 4, 2012, 2:42:12 PM4/4/12
to
Am 04.04.2012 20:21, schrieb Bjoern Hoehrmann:

> "Wide character" ist ein überholter Begriff aus den 1990ern; du scheinst
> irgendwie davon auszugehen, dass diese Zeichen doppelt so breit sind wie
> "normale" Zeichen. Das ist nicht der Fall, ein "ö" in $name ist genauso
> "breit" wie ein "o" zumindest was substr und length und derlei angeht.
> Das liegt vor allem an decode(), würdest du das nicht benutzen dann wäre
> ein "ö" doppelt so lang wie ein "o", das Euro-Zeichen drei mal so lang,
> und diverse asiatische Schriftzeichen vier mal so lang. In bytes, wohl-
> gemerkt, und ohne das decode() würde Perl grob gesagt die Zeichenkette
> als Byte-Kette interpretieren. Wenn du $name_size_max einfach auf `7`
> lässt, dann funktioniert das Skript wie "gewünscht".

Wenn ich decode() nicht nutze, dann erhalte ich: "おもしろ06" (ohne "")


$orig_name = "おもしろネタ速報";
Ausgabe von length($orig_name): 24

my $name = decode('UTF-8', $orig_name);
Ausgabe von length($name): 8


Mit lateinischen Schriftzeichen stimmt zumindest die Addition der vier
lateinischen Zeichen:

$orig_name = "testおもしろネタ速報";
Ausgabe von length($orig_name): 28

my $name = decode('UTF-8', $orig_name);
Ausgabe von length($name): 12

weitere Ideen?

Bjoern Hoehrmann

unread,
Apr 4, 2012, 5:39:32 PM4/4/12
to
* Nils Müller wrote in de.comp.lang.perl.misc:
>weitere Ideen?

Du hast diese beiden Zeichenketten:

my $s1 = "\x{304A}\x{3082}\x{3057}\x{308D}" .
"\x{30CD}\x{30BF}\x{901F}\x{5831}";

my $s2 = "\x{0078}\x{0078}\x{0078}\x{0078}" .
"\x{304A}\x{3082}\x{3057}\x{308D}" .
"\x{30CD}\x{30BF}\x{901F}\x{5831}";

`substr $s1, 0, 7` gibt dir die ersten sieben Zeichen von $s1,
"\x{304A}\x{3082}\x{3057}\x{308D}\x{30CD}\x{30BF}\x{901F}".

`substr $s2, 0, 7` gibt dir die ersten sieben Zeichen von $s2,
"\x{0078}\x{0078}\x{0078}\x{0078}\x{304A}\x{3082}\x{3057}".

Wenn das nicht die gewünschten Ergebnisse sind, dann willst du nicht die
ersten sieben Zeichen einer Zeichenkette haben, sondern was anderes, was
du uns aber noch nicht erklärt hast.

Florian Weimer

unread,
Apr 5, 2012, 2:16:12 AM4/5/12
to
* Nils Müller:

> Am 04.04.2012 20:21, schrieb Bjoern Hoehrmann:
>
>> "Wide character" ist ein überholter Begriff aus den 1990ern; du scheinst
>> irgendwie davon auszugehen, dass diese Zeichen doppelt so breit sind wie
>> "normale" Zeichen. Das ist nicht der Fall, ein "ö" in $name ist genauso
>> "breit" wie ein "o" zumindest was substr und length und derlei angeht.
>> Das liegt vor allem an decode(), würdest du das nicht benutzen dann wäre
>> ein "ö" doppelt so lang wie ein "o", das Euro-Zeichen drei mal so lang,
>> und diverse asiatische Schriftzeichen vier mal so lang. In bytes, wohl-
>> gemerkt, und ohne das decode() würde Perl grob gesagt die Zeichenkette
>> als Byte-Kette interpretieren. Wenn du $name_size_max einfach auf `7`
>> lässt, dann funktioniert das Skript wie "gewünscht".
>
> Wenn ich decode() nicht nutze, dann erhalte ich: "おもしろ06" (ohne "")
>
> $orig_name = "おもしろネタ速報";
> Ausgabe von length($orig_name): 24

Wenn Du UTF-8 im Quellcode verwendest, solltest Du "use encoding"
verwenden.

> my $name = decode('UTF-8', $orig_name);

Dann fällt jedenfalls das "decode" weg.

> Ausgabe von length($name): 8

Was ist an diesen "8" nun falsch?

Geht es Dir darum, daß das "16" sein sollte, weil die Zeichen zwei
Bildschirmspalten belegen? Die Funktion dazu heißt wcwidth
bzw. wcswidth, auf CPAN findet sich auf die Schnelle Text::CharWidth,
keine Ahnung, ob das etwas taugt.

Nils Müller

unread,
Apr 5, 2012, 9:14:11 AM4/5/12
to
Am 04.04.2012 23:39, schrieb Bjoern Hoehrmann:
> * Nils Müller wrote in de.comp.lang.perl.misc:
>> weitere Ideen?
>
> Du hast diese beiden Zeichenketten:
>
> my $s1 = "\x{304A}\x{3082}\x{3057}\x{308D}" .
> "\x{30CD}\x{30BF}\x{901F}\x{5831}";
>
> my $s2 = "\x{0078}\x{0078}\x{0078}\x{0078}" .
> "\x{304A}\x{3082}\x{3057}\x{308D}" .
> "\x{30CD}\x{30BF}\x{901F}\x{5831}";
>
> `substr $s1, 0, 7` gibt dir die ersten sieben Zeichen von $s1,
> "\x{304A}\x{3082}\x{3057}\x{308D}\x{30CD}\x{30BF}\x{901F}".
>
> `substr $s2, 0, 7` gibt dir die ersten sieben Zeichen von $s2,
> "\x{0078}\x{0078}\x{0078}\x{0078}\x{304A}\x{3082}\x{3057}".
>
> Wenn das nicht die gewünschten Ergebnisse sind, dann willst du nicht die
> ersten sieben Zeichen einer Zeichenkette haben, sondern was anderes, was
> du uns aber noch nicht erklärt hast.

Nutze ich z.B. nur "use UTF-8" im Skript und möchte mir die ersten zwei Zeichen
anzeigen lassen, erhalte ich als Ausgabe:
F06 (von: おもしろネタ速報)
te (von: testおもしろネタ速報)

Lasse ich mir die ersten drei Zeichen anzeigen erhalte ich als Ausgabe:
お (von: おもしろネタ速報)
tes (von: testおもしろネタ速報)

Benutze ich "nicht" "use utf-8" sondern meine "sub wide_char" Routine, dann
erhalte ich bei der Ausgabe des ersten UND zweiten Zeichens:

t

Möchte ich die ersten drei oder vier Zeichen anzeigen lassen:
おも
te

Ist dies verständlicher? Ich kann auch gerne mal ein Screenshot machen und
via email zusenden...

Wolf Behrenhoff

unread,
Apr 5, 2012, 10:01:39 AM4/5/12
to
Am 05.04.2012 15:14, schrieb Nils Müller:
> Nutze ich z.B. nur "use UTF-8" im Skript und möchte mir die ersten zwei Zeichen
> anzeigen lassen, erhalte ich als Ausgabe:
> F06 (von: おもしろネタ速報)
> te (von: testおもしろネタ速報)
>
> Lasse ich mir die ersten drei Zeichen anzeigen erhalte ich als Ausgabe:
> お (von: おもしろネタ速報)
> tes (von: testおもしろネタ速報)
>
> Benutze ich "nicht" "use utf-8" sondern meine "sub wide_char" Routine, dann
> erhalte ich bei der Ausgabe des ersten UND zweiten Zeichens:
> お
> t
>
> Möchte ich die ersten drei oder vier Zeichen anzeigen lassen:
> おも
> te
>
> Ist dies verständlicher? Ich kann auch gerne mal ein Screenshot machen und
> via email zusenden...

Hallo,

ich glaube, du musst mal ein vollständiges lauffähiges (aber kurzes!)
Programm hier zeigen. Ich verstehe dein Problem auch nicht ganz,
insbesondere nicht, was die wide_char tun soll.

use Encode;

my $s1 = "\x{304A}\x{3082}\x{3057}\x{308D}" .
"\x{30CD}\x{30BF}\x{901F}\x{5831}";
#oder: my $s1 = decode("UTF-8", "おもしろネタ速報");
#oder: use utf8; my $s1 = "おもしろネタ速報";

print encode("UTF-8", substr($s1, 2, 4)), "\n";

--> bei mir funtioniert dieses Programm ganz wunderbar, zeigt also 4
Zeichen an ab dem 3. Zeichen.

- Wolf

Nils Müller

unread,
Apr 5, 2012, 12:51:44 PM4/5/12
to
Am 05.04.2012 16:01, schrieb Wolf Behrenhoff:

> ich glaube, du musst mal ein vollständiges lauffähiges (aber kurzes!)
> Programm hier zeigen. Ich verstehe dein Problem auch nicht ganz,
> insbesondere nicht, was die wide_char tun soll.

Das Problem, das Skript läuft innerhalb einer ncurses Umgebung. Somit ist
das Skript weder kurz (50Kb) noch ohne ncurses lauffähig.

Ich habe mal ein paar Snapshots gemacht.

Hier sieht man den ganzen Text:
http://dl.dropbox.com/u/4616166/snap.png

Hier wird einfach ab Position 10 abgeschnitten:
$name_size_max = 10;
$name = substr($name,0,$name_size_max);

http://dl.dropbox.com/u/4616166/snap1.png



Auf diesem Bild nutze ich folgende Routine:
$name_size_max = 10;
my $name = decode('UTF-8', $orig_name);
$name_size_max = int($name_size_max / 2)+1;
$name = substr($name,0,$name_size_max);

http://dl.dropbox.com/u/4616166/snap2.png


Anmerkung:
- Das "+" Zeichen am Ende der Zeichenkette setze ich damit man sieht,
dass der Name gekürzt wurde.

- Wie man auf dem Bild "Snap1" sieht, wird anstelle des chinesischen Symbols eine
"06" ausgegeben und die Farbe des "+" Zeichens ist nicht korrekt (obwohl ich das
"+" inklusive Farbcode erst später anfüge!).

- Auf dem Bild "Snap2" soll ab dem 10 Zeichen getrennt werden, aber "testお" sind
keine 10 Zeichen.

Peter J. Holzer

unread,
Apr 6, 2012, 6:48:23 AM4/6/12
to
On 2012-04-05 16:51, Nils Müller <tras...@arcor.de> wrote:
> Am 05.04.2012 16:01, schrieb Wolf Behrenhoff:
>
>> ich glaube, du musst mal ein vollständiges lauffähiges (aber kurzes!)
>> Programm hier zeigen. Ich verstehe dein Problem auch nicht ganz,
>> insbesondere nicht, was die wide_char tun soll.
>
> Das Problem, das Skript läuft innerhalb einer ncurses Umgebung. Somit ist
> das Skript weder kurz (50Kb) noch ohne ncurses lauffähig.

Kürze es auf etwas zusammen, das kurz genug ist, dass Du es hier posten
kannst. Ncurses dürften die meisten haben.


> Ich habe mal ein paar Snapshots gemacht.
>
> Hier sieht man den ganzen Text:
> http://dl.dropbox.com/u/4616166/snap.png
>
> Hier wird einfach ab Position 10 abgeschnitten:
> $name_size_max = 10;
> $name = substr($name,0,$name_size_max);

Unter der Annahme, dass $name hier ein (UTF-8-kodierter) Byte-String und
kein Character-String ist: Hier schneidest Du nach 10 *Bytes* ab,
nicht nach 10 *Zeichen*.

Also:

e3 81 8a e3 82 82 e3 81 97 e3 82 8d e3 83 8d e3 82 bf e9 80 9f e5 a0 b1
-------- -------- -------- -------- -------- -------- -------- --------
お も し ろ ネ タ 速 報
<--------------------------->
10 Bytes

Du gibst also vom 4. Zeichen nur mehr das erste Byte aus.


> http://dl.dropbox.com/u/4616166/snap1.png
>
>
>
> Auf diesem Bild nutze ich folgende Routine:
> $name_size_max = 10;
> my $name = decode('UTF-8', $orig_name);

Hie wandelst Du den Byte-String in einen Character-String um.
$name besteht nun aus 8 Zeichen, nicht aus 24 Bytes.

> $name_size_max = int($name_size_max / 2)+1;

$name_size_max hat nun den Wert 6.

> $name = substr($name,0,$name_size_max);

Du nimmst also die ersten 6 Zeichen von $name.

> http://dl.dropbox.com/u/4616166/snap2.png
>
>
> Anmerkung:
> - Das "+" Zeichen am Ende der Zeichenkette setze ich damit man sieht,
> dass der Name gekürzt wurde.
>
> - Wie man auf dem Bild "Snap1" sieht, wird anstelle des chinesischen Symbols eine
> "06" ausgegeben und die Farbe des "+" Zeichens ist nicht korrekt (obwohl ich das
> "+" inklusive Farbcode erst später anfüge!).

Das liegt wahrscheinlich daran, dass Du vom 4. Zeichen nur ein Byte
ausgibst. Danach gibt ncurses den Steuerungscode für "grüne
Vordergrundfarbe" aus, und der arme Terminal-Emulator, der erwartet,
dass da jetzt die nächsten zwei Bytes von ろ kommen, verschluckt sich
daran. Um zu erklären, warum da jetzt "06" steht und nichts anderes,
müsste man sich ansehen, was ncurses und der Terminal-Emulator da genau
machen.

> - Auf dem Bild "Snap2" soll ab dem 10 Zeichen getrennt werden, aber "testお" sind
> keine 10 Zeichen.

Klar. Du schneidest ja nach 6 Zeichen ab. Es sollten also 6 Zeichen
sein, warum man nur 5 sieht, kann ich nicht erklären. Bist Du sicher,
dass der Code, den Du oben gepostet hast, der ist, den Du für diese
Screenshots verwendet hast?


Ich habe eine Grundregeln für den Umgang mit Strings in Perl:

Wenn Strings einen Text (also eine Folge von Zeichen, nicht eine
Folge von Bytes) repräsentieren, dann sind sie immer Character-Strings.

Daraus folgen ein paar abgeleitete Regeln:

1) Source-Code ist in UTF-8 geschrieben und verwendet »use utf8;«. Auf
diese Art sind String-Konstanten immer richtig.

2) Jeder Text-Input, der von außen hereinkommt, wird so früh wie möglich
dekodiert: Beim Lesen von Files mittels I/O-Layer (z.B. »open(my $fh,
'<:encoding(UTF-8)', $filename)«, beim Lesen aus einer Datenbank
mittels entsprechender Optionen (leider für jede Datenbank anders),
etc. Wenn gar nichts anderes hilft, dann ein expliziter Aufruf von
decode()).

3) Ein Zeichen ist ein Zeichen ist ein Zeichen und mich interessiert
nicht, wie das intern abgespeichert ist. length() liefert mir die
Länge in Zeichen, substr($s, 0, 10) liefert mir die ersten 10
Zeichen, m/x(..)/ die ersten zwei Zeichen nach dem x; egal, ob das
lateinische oder chinesische Zeichen sind.
[Stolperstein: Normalisierungsformen: Um die muss man sich u.U. doch
kümmern]

4) Die Umwandlung in Bytes für den Output erfolgt so spät wie möglich:
Im I/O-Layer, im Datenbank-Interface, oder notfalls mittels
explizitem Aufruf von encode() unmittelbar vor der Ausgabe.

hp

--
_ | Peter J. Holzer | Deprecating human carelessness and
|_|_) | Sysadmin WSR | ignorance has no successful track record.
| | | h...@hjp.at |
__/ | http://www.hjp.at/ | -- Bill Code on as...@irtf.org

Nils Müller

unread,
Apr 7, 2012, 3:32:52 AM4/7/12
to
Am 06.04.2012 12:48, schrieb Peter J. Holzer:

Ich habe mich mal mit dem Programmierer in Verbindung gesetzt, in der mein
Skript ausgeführt wird, hier seine Antwort:

I have to compute the length of string on screen (with a special function)
so "ë" and "ら" are both 2 bytes in memory, but the second is 2 chars on screen, the first
is one char on screen. I count the number of chars *on screen* for each char in
string. I think such function should exist in perl.

'ë': wide char, but 1 char on s creen, '速': wide char, 2 chars on screen, but same
number of bytes internally. so you can not just count the bytes or chars in string,
like your patch does. the problem is here :)

Peter J. Holzer

unread,
Apr 7, 2012, 5:33:43 AM4/7/12
to
On 2012-04-07 07:32, Nils Müller <tras...@arcor.de> wrote:
> Am 06.04.2012 12:48, schrieb Peter J. Holzer:
>
> Ich habe mich mal mit dem Programmierer in Verbindung gesetzt, in der mein
> Skript ausgeführt wird, hier seine Antwort:

Dein Script wird in einem Programmierer ausgeführt? ;-)


> I have to compute the length of string on screen (with a special function)
> so "ë" and "ら" are both 2 bytes in memory, but the second is 2 chars on screen, the first
> is one char on screen. I count the number of chars *on screen* for each char in
> string. I think such function should exist in perl.
>
> 'ë': wide char, but 1 char on s creen, '速': wide char, 2 chars on screen, but same
> number of bytes internally. so you can not just count the bytes or chars in string,
> like your patch does. the problem is here :)

Dafür hat Florian Weimer bereits in <878viai...@mid.deneb.enyo.de>
eine Lösung beschrieben. Hast Du das Posting gesehen?

Nils Müller

unread,
Apr 7, 2012, 8:29:50 AM4/7/12
to
Am 07.04.2012 11:33, schrieb Peter J. Holzer:

>> Ich habe mich mal mit dem Programmierer in Verbindung gesetzt, in der mein
>> Skript ausgeführt wird, hier seine Antwort:
> Dein Script wird in einem Programmierer ausgeführt? ;-)
Sehr gut :-D

> Dafür hat Florian Weimer bereits in <878viai...@mid.deneb.enyo.de>
> eine Lösung beschrieben. Hast Du das Posting gesehen?
Ich werde es mir noch mal ansehen, wobei das Problem nun wohl im Hauptprogramm
angegangen wird.

Ich bedanke mich recht Herzlich für die Hilfe und die Erläuterungen, dadurch
habe ich wieder etwas gelernt. Danke....

Helmut Wollmersdorfer

unread,
Apr 10, 2012, 4:38:18 PM4/10/12
to
On 04/07/2012 02:29 PM, Nils Müller wrote:
> Am 07.04.2012 11:33, schrieb Peter J. Holzer:
>

>> Dafür hat Florian Weimer bereits in<878viai...@mid.deneb.enyo.de>
>> eine Lösung beschrieben. Hast Du das Posting gesehen?

> Ich werde es mir noch mal ansehen, wobei das Problem nun wohl im Hauptprogramm
> angegangen wird.

Falls Du es doch in reinem Perl lösen willst oder musst, kannst Du die
entsprechenden Unicode-Properties per Regex verwenden:


$ uniprops -l | grep -i width
Block=Halfwidth_And_Fullwidth_Forms
InHalfwidthAndFullwidthForms
East_Asian_Width=A
East_Asian_Width=Ambiguous
East_Asian_Width=F
East_Asian_Width=Fullwidth
East_Asian_Width=H
East_Asian_Width=Halfwidth
East_Asian_Width=Neutral
East_Asian_Width=Na
East_Asian_Width=Narrow
East_Asian_Width=W
East_Asian_Width=Wide
Halfwidth_And_Fullwidth_Forms

Helmut Wollmersdorfer
0 new messages