/9分で読めます

UTF-8とは?現代Webを支えるEncodingを解説

UTF-8は、1つのpageにEnglish、日本語、Thai、Chinese、Arabic、mathematical symbols、emojiを同時に載せられるencodingです。ここではその仕組みと、modern webのdefaultになった理由を解説します。

UTF-8とは?

UTF-8Unicode Transformation Format - 8-bitの略で、Unicode standardのすべてのcharacterを表せるvariable-width character encodingです。

各characterを1から4 bytesで保存します。A、z、5、!のようなbasic ASCII charactersは1 byte、accented letters、日本語、Thai characters、Chinese characters、emojiは必要に応じて複数bytesを使います。

UTF-8は1992年にKen ThompsonRob Pikeによって設計されました。現在ではwebsites、APIs、source code、databases、configuration filesで主流のtext encodingです。

UnicodeとUTF-8の違い

UnicodeとUTF-8は混同されがちですが、同じ問題の別の部分を解決します。

  • Unicodeはcharacter setです。各characterにunique code pointを割り当てる巨大なcatalogで、たとえばAはU+0041、Euro signはU+20ACです。
  • UTF-8はencodingです。Unicode code pointsを、computersが保存・送信・読み取りできるbytesへ変換する実用的な方法です。

たとえるなら、Unicodeはcharactersとnumbersのdictionaryで、UTF-8はそのnumbersをbytesへ詰めるdelivery formatです。UTF-16やUTF-32のような他のUnicode encodingsもありますが、UTF-8はcompactでASCIIとのbackward compatibilityがあるためWeb standardになりました。

UTF-8 Encodingの仕組み

UTF-8はvariable-width byte patternを使います。必要なbytes数はUnicode code pointの値で決まります。

Code Point RangeBytesByte Pattern
U+0000 - U+007F10xxxxxxxA, z, 5, !
U+0080 - U+07FF2110xxxxx 10xxxxxxé, ñ, ü
U+0800 - U+FFFF3あ, €, 中, ✓
U+10000 - U+10FFFF411110xxx 10xxxxxx 10xxxxxx 10xxxxxxemojiやrare historic scripts

各byteのleading bitsは、decoderにそのbyteの種類を伝えます。

  • 先頭が0: single-byte ASCII character。
  • 先頭が110: 2-byte characterのfirst byte。
  • 先頭が1110: 3-byte characterのfirst byte。
  • 先頭が11110: 4-byte characterのfirst byte。
  • 先頭が10: continuation byteであり、characterの開始ではありません。

この設計によりUTF-8はself-synchronizingです。programがbyte streamの途中から読み始めても、10で始まらないbyteを見つけるまで進めば、次のcharacter boundaryを回復できます。

Step-by-Step Encoding Examples

1-byte: "A" (U+0041)

Code point: U+0041 = 65 = 1000001 in binary
Range: U+0000-U+007F -> 1 byte
Pattern: 0xxxxxxx

Fill in: 0 1000001
Byte:    01000001 = 0x41

"A" in UTF-8 = 0x41 (identical to ASCII)

2-byte: accented e (U+00E9)

Code point: U+00E9 = 233 = 11101001 in binary
Range: U+0080-U+07FF -> 2 bytes
Pattern: 110xxxxx 10xxxxxx

Split bits: 00011  101001
Fill in:    11000011 10101001
Bytes:      0xC3     0xA9

"e with acute" in UTF-8 = 0xC3 0xA9

3-byte: Euro sign (U+20AC)

Code point: U+20AC = 8364 = 10000010101100 in binary
Range: U+0800-U+FFFF -> 3 bytes
Pattern: 1110xxxx 10xxxxxx 10xxxxxx

Split bits: 0010  000010  101100
Fill in:    11100010 10000010 10101100
Bytes:      0xE2     0x82     0xAC

"Euro sign" in UTF-8 = 0xE2 0x82 0xAC

4-byte: grinning face emoji (U+1F600)

Code point: U+1F600 = 128512
Range: U+10000-U+10FFFF -> 4 bytes
Pattern: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Split bits: 000  011111  011000  000000
Fill in:    11110000 10011111 10011000 10000000
Bytes:      0xF0     0x9F     0x98     0x80

"grinning face emoji" in UTF-8 = 0xF0 0x9F 0x98 0x80

UTF-8が主流になった理由

UTF-8がdominant encodingになったのには、実用的な理由があります。

  • ASCIIとのbackward compatibility - 既存のASCII documentsはそのままvalid UTF-8です。
  • Space efficient - English textは1 characterあたり1 byteで、他のscriptsも必要なbytesだけを使います。
  • Byte-order issuesがない - UTF-16やUTF-32と違い、UTF-8ではbyte-order switchingが不要です。
  • Self-synchronizing - errorsやrandom seekingの後でも、programsはcharacter boundariesを回復できます。
  • C-style stringsと相性がよい - ordinary textに予期しないnull bytesが入りません。
  • Universal - basic Latinから日本語、Thai text、emojiまで、すべてのUnicode characterを表せます。

UTF-8 vs UTF-16 vs UTF-32

Unicodeには主なencodingsが3つあります。tradeoffsは次の通りです。

FeatureUTF-8UTF-16UTF-32
Bytes per character1-42 or 4常に4
ASCII compatibleYesNoNo
Byte order issueNoYes, BOMが必要な場合ありYes, BOMが必要な場合あり
"Hello" size5 bytes10 bytes20 bytes
Common useWeb, Linux, macOS, JSONWindows APIs, Java, JavaScript internalsInternal processing
Web usageDominant非常にまれほぼ使われない

UTF-16は今も重要です。JavaScriptJavaはstringsの内部表現に16-bit code unitsを使うためです。そのためemojiやsupplementary charactersが含まれると、string lengthとvisible character countが直感とずれることがあります。

CodeでのUTF-8

多くのmodern languagesとbrowsersにはUTF-8 toolsが組み込まれています。

JavaScript

// Encode string to UTF-8 bytes
const encoder = new TextEncoder();
const bytes = encoder.encode("Hello €");
console.log(bytes);
// Uint8Array [72, 101, 108, 108, 111, 32, 226, 130, 172]

// Decode UTF-8 bytes back to string
const decoder = new TextDecoder("utf-8");
const text = decoder.decode(bytes);
console.log(text);  // "Hello €"

// Character length is not always byte length
"Hello".length;  // 5
new TextEncoder().encode("Hello").length;  // 5
new TextEncoder().encode("cafe").length;   // 4
new TextEncoder().encode("café").length;   // 5

Python

# Encode string to UTF-8 bytes
text = "Hello €"
utf8_bytes = text.encode("utf-8")
print(utf8_bytes)       # b'Hello \xe2\x82\xac'
print(len(utf8_bytes))  # 9 bytes

# Decode UTF-8 bytes to string
decoded = utf8_bytes.decode("utf-8")
print(decoded)  # "Hello €"

# Character vs byte length
len("café")                    # 4 characters
len("café".encode("utf-8"))    # 5 bytes

HTML

<!-- Always declare UTF-8 in HTML -->
<meta charset="UTF-8">

<!-- Put it early inside <head>, before text content that needs decoding -->

よくあるUTF-8 Problems

多くのencoding bugsは、bytesをwrong encodingで読んだり、bytesとcharactersを混同したりすることで起きます。

Mojibake (文字化け)

UTF-8 bytesをLatin-1やWindows-1252としてdecodeすると、読みやすいtextが奇妙なcharactersに変わることがあります。writerとreaderの両方がUTF-8を使うようにします。

Replacement characters

decoderがvalid UTF-8ではないbytesを見つけると、replacement character U+FFFDが表示されます。original file encodingを確認し、double-decodingを避けます。

BOM surprises

一部のtoolsはfilesの先頭にUTF-8 Byte Order Markを追加します。JSON、shell scripts、strict parsersを壊すことがあるため、可能ならUTF-8 without BOMで保存します。

Character length vs byte length

10-character stringが10 bytesとは限らず、もっと大きい場合があります。UIにはcharacter-aware limits、storageやnetwork constraintsにはbyte-aware limitsを使います。

Best Practices

encoding problemsを避けるには、次の習慣が役立ちます。

  • UTF-8をdefaultで使う。specific legacy requirementがある場合を除きます。
  • Encodingを宣言する。HTMLでは<meta charset="UTF-8">、HTTP headersではcharset=utf-8を使います。
  • Source filesをUTF-8で保存する。codeやdata filesでは、できればBOMなしを使います。
  • MySQLではutf8mb4を使う。emojiを含むfull Unicodeを保存できるようにします。
  • Raw UTF-8 bytesを不用意に分割しない。user-visible textではcharacters、code points、grapheme clusters単位で扱います。
  • Multilingual textでtestする。usersがreal-world names、addresses、messagesを貼り付ける前にbugsを見つけられます。

UTF-8 TextをEncode & Decode

無料のUTF-8 Converter toolで、byte sequencesの確認、textのencode、bytesのdecodeをbrowser内ですぐに行えます。

UTF-8 Converterを試す

参考資料

  1. Yergeau, F. (2003). UTF-8, a transformation format of ISO 10646. RFC 3629, IETF. https://datatracker.ietf.org/doc/html/rfc3629
  2. Pike, R. & Thompson, K. (2003). UTF-8 history. https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt
  3. The Unicode Consortium. The Unicode Standard. https://www.unicode.org/standard/standard.html
  4. Mozilla Developer Network. TextEncoder - Web APIs. https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
  5. W3Techs. Usage statistics of character encodings for websites. https://w3techs.com/technologies/overview/character_encoding