UnicodeとUTF-8の変換式

(2021年9月28日)

UnicodeとUTF-8の変換式

v = (2ⁿ-1)×2⁷ⁿ⁺⁷-2⁶(2-¹u₆)[n=0] + (2⁷+⁶u₀)+(2⁷+⁶u₆)×2⁸+…+(2⁷+⁶u₆ₙ)×2⁸ⁿ
u = ¹v₆×2⁶[n=0] + ⁶⁻ⁿv₈ₙ×2⁶ⁿ + (⁶v₀+⁶v₈×2⁶+…+⁶v₈ₙ₋₈×2⁶ⁿ⁻⁶)

を導出します。

ここで,uはUnicode,vはUTF-8,n=[(log₂u-1)/5][2⁷≦u]=[log₂v/8]はUTF-8のバイト数-1,ⁱwₙ=[w/2ⁿ]%2ⁱはw=u,vの第n+1~n+iビット,%は剰余,[ ]は数値の整数部分,または,真なら1偽なら0(アイバーソンの記法)。

ただし,vからuを計算する式(第二式)は,UTF-8でないコードに対しても値を計算します。例えば,v=0xD7A68FはUTF-8でないコードですが,この式はu=0x798Fと計算してしまいます。vがUTF-8の必要条件を満たすか否かは,以下の不等式で判定できます(カンマはAND):

( n=0, v<2⁷ ) OR ( 0<n<4, 0≤⁸v₈ₙ-2⁸+2⁷⁻ⁿ<2⁶⁻ⁿ, 0≤⁸v₈ₖ-2⁷<2⁶ for k=0,...,n-1, 0xC280[n=1]+0xE0A080[n=2]+0xF0908080[n=3]≤v )
検算(JavascriptによるUnicodeとUTF-8の変換): 値/文字を入力してEnterキーを押すと,計算/表示します。UTF-8の真理値はvがUTF-8の必要条件を満たしているか否かです。
Unicode u = = 0x = 文字
UTF-8 v = = 0x
参考:UTF-8文字コード表

(導出)

UTF-8では,Unicodeを次のように1~4バイトで表します:

  • 1バイトコードは先頭ビットが0(0x0~7)
  • 2バイトコードは先頭ビットが110(0xC~D)
  • 3バイトコードは先頭ビットが1110(0xE)
  • 4バイトコードは先頭ビットが1111 0(0xF0~F7)
  • n>1バイトコードの2バイト目以降は10(0x8~B)

それゆえ,

  • 1バイトコードは7ビットでUnicodeの0から2⁷-1=127(0x7F),
  • 2バイトコードは11ビットでUnicodeの128から2¹¹-1=2047(0x7FF),
  • 3バイトコードは16ビットでUnicodeの2048から2¹⁶-1=65535(0xFFFF),
  • 4バイトコードは21ビットでUnicodeの65536から2²¹-1=2097151(0x1FFFFF)

を表します。

これより,UTF-8は,uの第n+1~n+iビットをとりだした値ⁱuₙ=[u/2ⁿ]%2ⁱを用いて,

  • 2⁷≦u<2¹¹に対しては,

    (2⁷+2⁶+⁵u₆)×2⁸ + (2⁷+⁶u₀)

  • 2¹¹≦u<2¹⁶に対しては,

    (2⁷+2⁶+2⁵+⁴u₁₂)×2¹⁶ + (2⁷+⁶u₆)×2⁸ + (2⁷+⁶u₀)

  • 2¹⁶≦u<2²¹に対しては,

    (2⁷+2⁶+2⁵+2⁴+³u₁₈)×2²⁴ + (2⁷+⁶u₁₂)×2¹⁶ + (2⁷+⁶u₆)×2⁸ + (2⁷+⁶u₀)

となります。

したがって,3つの場合をまとめて,2⁷≦u<2²¹に対して,n=[(log₂u-1)/5]-[2⁶≦u][u<2⁷]をUTF-8のバイト数-1として,

v = (2⁶+…+2⁷⁻ⁿ)×2⁸ⁿ+(2⁷+⁶u₀)+(2⁷+⁶u₆)×2⁸+…+(2⁷+⁶u₆ₙ)×2⁸ⁿ

2⁶+…+2⁷⁻ⁿ=(2ⁿ-1)×2⁷ⁿ⁺⁷, n≧1より,

= (2ⁿ-1)×2⁷ⁿ⁺⁷+(2⁷+⁶u₀)+(2⁷+⁶u₆)×2⁸+…+(2⁷+⁶u₆ₙ)×2⁸ⁿ.

ここで,0<u<2⁷,すなわち,n=0の時,この式は

2⁷+⁶u₀ = 2⁷+u-¹u₆×2⁶=u+2⁶(2-¹u₆)

なので,0<u<2²¹に対して,

v = (2ⁿ-1)×2⁷ⁿ⁺⁷-2⁶(2-¹u₆)[n=0] + (2⁷+⁶u₀)+(2⁷+⁶u₆)×2⁸+…+(2⁷+⁶u₆ₙ)×2⁸ⁿ


逆に,Unicodeは,vの第n+1~n+iビットをとりだした値ⁱvₙ=[v/2ⁿ]%2ⁱを用いて,

  • n=1に対しては,

    ⁵v₈×2⁶ + ⁶v₀

  • n=2に対しては,

    ⁴v₁₆×2¹² + ⁶v₈×2⁶ + ⁶v₀

  • n=3に対しては,

    ³v₂₄×2¹⁸ + ⁶v₁₆×2¹² + ⁶v₈×2⁶ + ⁶v₀

となります。ここで,n=[log₂v/8]はUTF-8のバイト数-1。

したがって,3つの場合をまとめて,n>0に対して,

u = (⁶v₀+⁶v₈×2⁶+…+⁶v₈ₙ₋₈×2⁶ⁿ⁻⁶) + ⁶⁻ⁿv₈ₙ×2⁶ⁿ

ここで,n=0,すなわち,0<v<2⁷の時,この式は(カッコ内の級数は生じないと解釈して)

⁶v₀ = v - ¹v₆×2⁶ 

なので,0<v<2²¹に対して,

u = ¹v₆×2⁶[n=0] + ⁶⁻ⁿv₈ₙ×2⁶ⁿ + (⁶v₀+⁶v₈×2⁶+…+⁶v₈ₙ₋₈×2⁶ⁿ⁻⁶)


最後に,vがUTF-8の必要条件を満足しているかどうかの判定。

  • n=0に対しては,先頭ビットが0,すなわち,v<2⁷
  • n=1に対しては,先頭ビットが110,2バイト目以降は10,かつ,u≧2⁷,すなわち,
    2⁷+2⁶≤⁸v₈<2⁷+2⁶+2⁵,2⁷≤⁸v₀<2⁷+2⁶,2¹⁵+2¹⁴+2⁷ +2⁷⁺²≤v
  • n=2に対しては,先頭ビットが1110,2バイト目以降は10,かつ,u≧2¹¹,すなわち,2⁷+2⁶+2⁵≤⁸v₁₆<2⁷+2⁶+2⁵+2⁴,2⁷≤⁸v₈<2⁷+2⁶,2⁷≤⁸v₀<2⁷+2⁶,2²³+2²²+2²¹+2¹⁵+2⁷ +2¹¹⁺²≤v
  • n=3に対しては,先頭ビットが11110,2バイト目以降は10,かつ,u≧2¹⁶,すなわち,2⁷+2⁶+2⁵+2⁴≤⁸v₂₄<2⁷+2⁶+2⁵+2⁴+2³,2⁷≤⁸v₁₆<2⁷+2⁶,2⁷≤⁸v₈<2⁷+2⁶,2⁷≤⁸v₀<2⁷+2⁶,2³¹+2³⁰+2²⁹+2²⁸+2²³+2¹⁵+2⁷ +2¹⁶⁺²⁺²≤v

まとめて,0<n<4に対して,

0≤⁸v₈ₙ-(2⁷⁻ⁿ+…+2⁷)<2⁶⁻ⁿ,0≤⁸v₀-2⁷<2⁶,…, 0≤⁸v₈ₙ-₈-2⁷<2⁶,(2⁷⁻ⁿ+…+2⁷)2⁸ⁿ+(2⁰+…+2⁸ⁿ⁻⁸)2⁷+2⁹[n=1]+2¹³[n=2]+2²⁰[n=3]≤v

2⁷⁻ⁿ+…+2⁷=2⁸-2⁷⁻ⁿなので,

0≤⁸v₈ₙ-2⁸+2⁷⁻ⁿ<2⁶⁻ⁿ,0≤⁸v₈ₖ-2⁷<2⁶ for k=0,...,n-1,0xC280[n=1] + 0xE0A080[n=2] + 0xF0908080[n=3]≤v

Q.E.D.

コメント

連絡フォーム

名前

メール *

メッセージ *