にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

Python3で文字列をUTF-16のコード列(整数のリスト)に変換

Unicode基本多言語面(BMP)外、つまりUnicodeスカラ値がU+10000以降の文字について、(UTF-16の)サロゲートペアのコードを求めたい、という需要があった。これは、Win32 APIのSendInput関数がUnicodeの値をUnsigned Shortでしか指定できないので、BMP外の文字はサロゲートペアで入力しないといけないということに対応するため。

Python3においては文字列(str型)はUnicode文字列を指すので、以下文字列とだけ書く。Python2の場合は適当に読み替えてください。

文字列をUTF-16のコードポイント(整数)のリストに変換

def str_to_utf16codepoints(s):
    bs = s.encode(encoding='utf_16_be')
    return [int.from_bytes(bs[n:n+2], 'big') for n in range(0, len(bs), 2)]

文字列をUTF-16-BE(ビッグエンディアン)の文字列に変換し、2バイトごとに区切って整数に変換する。

str_to_utf16codepoints("sushi寿司🍣")

[115, 117, 115, 104, 105, 23551, 21496, 55356, 57187]

を返す。

UTF-16のコードポイントのリスト→文字列

上の逆変換。

def utf16codepoints_to_str(ns):
    bs = b''.join([n.to_bytes(2, byteorder="big") for n in ns])
    s = bs.decode('utf_16_be')
    return s

要素技術

文字列→UTF-16-BE (bytes型)に変換

str.encode()を使う。返り値はバイト列(bytes型)になる。

"🍣".encode(encoding='utf_16_be')

バイト列を整数(int型)に変換

Python 3.2以降だとint.from_bytes()が使えるらしい。1つ目の引数はバイト列、2つめはエンディアンの指定。

int.from_bytes(b'\xff\xf0', 'big')

みたいな感じでいける。

さもなくば

def int_from_bytes(bytes):
    val = 0
    for b in bytes:
        val = (val << 8) + b
    return val

とかでできると思う。