JavaScriptで文字列のUTF-8バイト数を得る関数を書く

UnicodeのコードポイントとUTF-8 エンコードのバイト列の対照表

 Code Points            1st Byte  2nd Byte  3rd Byte  4th Byte

   U+0000..U+007F       00..7F
   U+0080..U+07FF       C2..DF    80..BF
   U+0800..U+0FFF       E0        A0..BF    80..BF
   U+1000..U+CFFF       E1..EC    80..BF    80..BF
   U+D000..U+D7FF       ED        80..9F    80..BF
   U+D800..U+DFFF       ******* ill-formed *******
   U+E000..U+FFFF       EE..EF    80..BF    80..BF
  U+10000..U+3FFFF      F0        90..BF    80..BF    80..BF
  U+40000..U+FFFFF      F1..F3    80..BF    80..BF    80..BF
 U+100000..U+10FFFF     F4        80..8F    80..BF    80..BF

上記は perlunicode - Perl における Unicode サポート - perldoc.jp から拾ったのだけど、最近の仕様と合っているのか The Unicode Consortium のドキュメントをあたってみた。

Table 3-7 の表が一致するので大丈夫と思う。

JavaScriptで文字列のUTF-8バイト数を得る関数

対照表から下記のように書ける。

function get_utf8_bytes(str) {
    var count = 0;
    for (var i = 0; i < str.length; ++i) {
        var cp = str.charCodeAt(i);

        if (cp <= 0x007F) {
            // U+0000 - U+007F
            count += 1;
        } else if (cp <= 0x07FF) {
            // U+0080 - U+07FF
            count += 2;
        } else if (cp <= 0xD7FF) {
            // U+0800 - U+D7FF
            count += 3;
        } else if (cp <= 0xDFFF) {
            // U+10000 - U+10FFFF
            //
            // 0xD800 - 0xDBFF (High Surrogates)
            // 0xDC00 - 0xDFFF (Low Surrogates)
            count += 2;
        } else if (cp <= 0xFFFF) {
            // U+E000 - U+FFFF
            count += 3;
        } else {
            // undefined code point in UTF-16
            // do nothing
        }
    }
    return count;
}