Javascriptの ‘文字列’.length の罠

Javascriptから少し離れると忘れてしまう問題。
最近、立て続けで見かけたので備忘録として書きます。

先に結論だけ書くと、現状では完全な解決は難しいということです。

lengthについて

MDNによる length について書かれたページです。
こちらの内容をほぼなぞったものになります。

String length – JavaScript | MDN
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/length

Javscript で文字列の長さを取得したいときはまず length を使うことを考えるでしょう。

const mojiretsu1 = 'mojiretsuhadoushutokusuruka';
console.log(mojiretsu1.length);
// 27
取得できました。
文字列を日本語に置き換えても問題なく取得できます。
const mojiretsu2 = '文字列はどう取得するか';
console.log(mojiretsu2.length);
// 11

何も問題はないように見えます。

全てはうまくいっています。

要素は言葉を切り分ける

MDNにある説明を引用します。

String length – JavaScript | MDN

細かい説明はMDNを見ていただくとして、ここで重要なのは length が文字の数ではなくコードユニットの数で表されることです。

以下に例を示します。

const emoji = '🏴󠁧󠁢󠁥󠁮󠁧󠁿';
console.log(emoji.length);
// 14

なんだかよく分からないこの謎の旗 🏴󠁧󠁢󠁥󠁮󠁧󠁿 の length は14ものコードユニットを内包します。
多すぎる、修正が必要でしょう・・・。

これを見てあなたはすぐに「絵文字禁止にしよう」と思うかもしれません。
しかし、それはベストな解決方法ではないことにすぐに気付くでしょう。

const umaiYasuiHayai = '𠮷野家';
console.log(umaiYasuiHayai.length);
// 4

𠮷野家.length は4。
𠮷の length は2となっているため、このような結果となります。

length はどう生きるか

MDNにある対応例を引用します。

function getCharacterLength (str) {
    return [...str].length;
}
console.log(getCharacterLength('🏴󠁧󠁢󠁥󠁮󠁧󠁿'));
// 7
console.log(getCharacterLength('𠮷野家'));
// 3

これにより ‘𠮷野家’ に対しては3を得ることが可能になります。
しかし、謎の旗は7となってしまい、依然、正しい文字数を得ることができません。

見ての通り、この方法は完璧ではありませんが、いまのところ、これはいくらかマシな方法といえます。
厳しい現実ですが、私たちは常に前を見据える必要があるでしょう。

未来へ

将来の話になりますが、Intl.Segmenter という新しい仕様によりこの問題は解決される見込みです。内容は省きますが、これを書いている現在では一部の環境でしか対応していません。Polyfill は存在するようですので、導入可能な環境であればそちらを利用できるでしょう。

また、length の話からは逸れますが、’文字列’.split() 等でも同様の問題を抱えています。厄介な問題ではあるのですが、Javascript を常に触っていることでもない限り忘却しがちなところも輪にかけて厄介な点です。その度に ‘文字列’.length には期待して、がっかりさせられ、また忘れて期待してしまうのです。

噫無情。

というほどでもないですが、なるべく早く解決されることを期待したいところです。