というわけで、ord篇はこちら。
404 Blog Not Found:perl, python & ruby - chr() vs. Unicodeとりあえずchrが長くなったのでordは別entryということで。
文字から数値へ(ord)
まずはPerlの例。
#!/usr/local/bin/perl
use strict;
use warnings;
use utf8;
binmode STDOUT, ':utf8';
sub say { print @_, "\n" };
say ord "\x{61}";
say ord "\x{3b1}";
say ord "\x{5F3E}";
say ord "\x{2A6B2}";
97 945 24382 173746
Pythonでは
まずはその前に前回の予習。
pythonグループ - faerie の日記の Python - 0x10000 以上の Unicode 文字を扱うには--enable-unicode=ucs4 付きでビルドすればいいらしい。
これはPythonとは思えない美しくない仕様。これではBytecodeもScriptもPortableではなくなってしまうではないか!
少なくともMac OS Xの/usr/bin/python (v2.3.5)は、--enable-unicode=ucs4 ではなかった。
ついでに、eval("u\'\\U%08X\'" % n) より (r'\U%08X' % n).decode("unicode_escape") のほうが良い。
こちらはなるほど。
以上を踏まえて、以下はデフォルトの仕様、すなわちunicode objectの内部表現はUTF-16だという前提。まずは以下をそのまま実行してみる。
print ord(u'\U00000061') print ord(u'\U000003b1') print ord(u'\U00005F3E') print ord(u'\U0002A6B2')
案の定、こうなる。
97
945
24382
Traceback (most recent call last):
File "uniord.py", line 10, in ?
print ord(u'\U0002A6B2')
TypeError: ord() expected a character, but string of length 2 found
とりあえず、以下のようにすれば動くようにはなる。
def uniord(c):
if len(c) == 1:
return ord(c)
else:
return 0x10000 + (ord(c[0]) - 0xD800) * 0x400 + (ord(c[1]) - 0xDC00)
print uniord(u'\U00000061')
print uniord(u'\U000003b1')
print uniord(u'\U00005F3E')
print uniord(u'\U0002A6B2')
とはいえ、これはあくまで一文字だけの例。実際には、文字列から本来の文字を一つ一つ取り出さなくてはならない。Perlのsplit //, $strないしunpack 'U*', $str相当のことはどうやったらよいだろう。とりあえず、泥臭い回答。
def uniunpack(ustr):
result = []
i = 0
while i < len(ustr) :
o = ord(ustr[i])
if 0xD800 <= o and o < 0xDC00:
i += 1
o -= 0xD800
o *= 0x400
o += 0x10000 + (ord(ustr[i]) - 0xDC00)
result.append(o)
i += 1
return result
str = u'\u0061\u03b1\u5F3E\U0002A6B2'
print str.encode('utf-8')
print map(ord, str)
print uniunpack(str)
aα弾𪚲 [97, 945, 24382, 55401, 57010] [97, 945, 24382, 173746]
pythonはpushでなくてappendなのね:)
rubyでは
ありがたいことに、String.unpack('U*')でいける。成瀬さんありがとう!
$KCODE = 'u' require 'jcode'
str = [0x61, 0x3b1, 0x5F3E, 0x2A6B2].pack('U*')
p str
str.unpack('U*').each{ |ord| p ord }
"aα弾𪚲" 97 945 24382 173746
dotの連鎖とblockの美しさはいささかも損なわれていない。
ただし、欠点もある。文字列リテラルでPerlの\x{2A6B2}、Pythonの\u03b1ないし\U0002A6B2という表記法が用意されていないのだ。このあたり、Rubyistのみなさんはどうなさっているのだろうか?str = 'aα弾' + [0x2A6B2].pack('U')とか、それともstr = "aα弾#{ [0x2A6B2].pack('U') }"とか。
やはりなんだかんだ言って、rubyはperlishに優しいが、Unicodeにはちょっと冷たい。
Dan the Man with Too Many Characters to Juggle in Too Many Languages
3042がU+3042のこととみなすのが難しいというのもあるのかもしれません。