Ruby
irb> '冬'.bytes.map{ |n| n.to_s(16) } => ["f0", "af", "a0", "9a"] irb> '冬'.bytesize => 4 irb> '羽󠄀'.bytes.map{ |n| n.to_s(16) } => ["e7", "be", "bd", "f3", "a0", "84", "80"] irb> '羽󠄀'.bytesize => 7
冬の正字冬
は"\u{2f81a}"
で 4 byte、(Prettier の bug (直した。Correctly format CJK sentences with Variation Selector. by ne-sachirou · Pull Request #8511 · prettier/prettier) で本文中に正しく書けないが) 羽の正字は"\u7fbd\u{e0100}"
で 7 byte だ。
Ruby の標準 encoding は UTF-8 だからそのまま byte 數を數へれば好い。
TypeScript
追記 2020-10-16
非標準だが unescape
函數を使ふ方法が有ると教えて頂いた。
cf. 文字列と UTF-8 バイト列の相互変換: Days on the Moon
console.info("羽󠄀".length); // 3 console.info(unescape(encodeURIComponent("羽󠄀")).split("")); // [ // 'ç', '¾', // '½', 'ó', // ' ', '\x84', // '\x80' // ] console.info(unescape(encodeURIComponent("羽󠄀")).length); // 7
類似の方法として UTF-8 に變換した長さを知るだけなら以下も有るらしい。encodeURIComponent
函數が文字列を UTF-8 として扱ってくれるからだ。
console.info(encodeURIComponent("羽󠄀").replace(/%../g, "_").length); // 7
又新しい API として TextEncoder が有ると教わった。Internet Exploler 以外ではもう使へる。
console.info(new TextEncoder().encode("羽󠄀")); // Uint8Array(7) [ // 231, 190, 189, // 243, 160, 132, // 128 // ]
JavaScript の内部 encoding は UTF-16 だからこれを UTF-8 に變換する。Unicode の code point そのものである UTF-32 に變換し、そこから UTF-8 にする。
const isHighSurrogate = (codePoint: number): boolean => 0xd800 <= codePoint && codePoint <= 0xdbff; const isLowSurrogate = (codePoint: number): boolean => 0xdc00 <= codePoint && codePoint <= 0xdfff; const utf16ToUtf32 = (str: string): Array<number> => { const utf32Str: Array<number> = []; let highSurrogate: number | undefined; for (const ch of str.split("")) { const codePoint = ch.codePointAt(0); if (highSurrogate) { // if (!isLowSurrogate(codePoint)) { // throw new Error(`Surrogate pair not closed: ${codePoint.toString(16)}`); // } utf32Str.push( 0x10000 + (highSurrogate - 0xd800) * 0x400 + (codePoint - 0xdc00) ); highSurrogate = undefined; } else if (isHighSurrogate(codePoint)) { highSurrogate = codePoint; } else { utf32Str.push(codePoint); } } // if (highSurrogate) { // throw new Error("Surrogate pair not closed"); // } return utf32Str; }; const utf32ToUtf8 = (utf32Str: Array<number>): Array<number> => { const utf8Str: Array<number> = []; for (const utf32Ch of utf32Str) { // if (utf32Ch < 0 || 0x10ffff < utf32Ch) { // throw new Error(`Not a UTF-32 char: ${utf32Ch.toString(16)}`); // } if (utf32Ch <= 0x7f) { utf8Str.push(utf32Ch); } else if (utf32Ch <= 0x7ff) { utf8Str.push(0xc0 | (utf32Ch >> 6)); utf8Str.push(0x80 | (utf32Ch & 0x3f)); } else if (utf32Ch <= 0xffff) { utf8Str.push(0xe0 | (utf32Ch >> 12)); utf8Str.push(0x80 | ((utf32Ch >> 6) & 0x3f)); utf8Str.push(0x80 | (utf32Ch & 0x3f)); } else { utf8Str.push(0xf0 | (utf32Ch >> 18)); utf8Str.push(0x80 | ((utf32Ch >> 12) & 0x3f)); utf8Str.push(0x80 | ((utf32Ch >> 6) & 0x3f)); utf8Str.push(0x80 | (utf32Ch & 0x3f)); } } return utf8Str; }; const utf8Length = (str: string): number => utf32ToUtf8(utf16ToUtf32(str)).length;
これで、
console.info( "UTF-16", "冬".split("").map((c) => c.codePointAt(0).toString(16)) ); console.info( "UTF-32", utf16ToUtf32("冬").map((n) => n.toString(16)) ); console.info( "UTF-8", utf32ToUtf8(utf16ToUtf32("冬")).map((n) => n.toString(16)) ); console.info(utf8Length("冬")); console.info( "UTF-16", "羽󠄀".split("").map((c) => c.codePointAt(0).toString(16)) ); console.info( "UTF-32", utf16ToUtf32("羽󠄀").map((n) => n.toString(16)) ); console.info( "UTF-8", utf32ToUtf8(utf16ToUtf32("羽󠄀")).map((n) => n.toString(16)) ); console.info(utf8Length("羽󠄀"));
UTF-16 [ 'd87e', 'dc1a' ] UTF-32 [ '2f81a' ] UTF-8 [ 'f0', 'af', 'a0', '9a' ] 4 UTF-16 [ '7fbd', 'db40', 'dd00' ] UTF-32 [ '7fbd', 'e0100' ] UTF-8 [ 'e7', 'be', 'bd', 'f3', 'a0', '84', '80' ] 7
cf. UTF-32、UTF-16、UTF-8 の相互変換 - Qiita
Scala
JVM の内部 encoding は UTF-16 だが、簡單に encoding を變換出來る。
scala> "冬".getBytes("UTF-8").map("%02x".format(_)) res: Array[String] = Array(f0, af, a0, 9a) scala> "冬".getBytes("UTF-8").length res: Int = 4 scala> "羽󠄀".getBytes("UTF-8").map("%02x".format(_)) res: Array[String] = Array(e7, be, bd, f3, a0, 84, 80) scala> "羽󠄀".getBytes("UTF-8").length res: Int = 7
PostgreSQL
example=# select octet_length('冬'); octet_length -------------- 4 (1 row) example=# select octet_length('羽󠄀'); octet_length -------------- 7 (1 row)