この記事はJavaScriptでlenghtプロパティを使う時はサロゲートペアに気をつけようというテーマで書いています。
サロゲートペアが含まれているとlenghtプロパティで取得した数と、実際の文字数が違ってしまうんですね。
例えばこんな感じです。
const chars = '楽しい😀';
console.log(chars.length); //5
4文字のはずが、lenghtプロパティで調べると5と判定されます。
絵文字を使うことはあまりないかもですが、漢字でもいくつかサロゲートペアがあるので気をつけたいところです。
有名なところではお魚のホッケとか。
サロゲートペアとは
サロゲートペアがなんだか分からないとあれなんで、簡単に説明をしておきます。
まず、コンピューターは文字をそのまま理解することができません。
そこで、符号化文字集合と言われる、文字と文字に割り当てた番号を対応させてコンピューターに理解させるわけです。
例えば、「あ」→「12354」みたいな感じです。
この文字コードにはUTF-16というのがあり、基本的に2byte(0 ~ 65,535)に対応しているのですが、それでは世の中の文字に対応しきれなくなってしまいました。
そこで、一部の文字は4byte(2byte×2)で対応することにしました。
ここで大事なのが、2byte×2というところです。
以下のコードで見てみましょう。
// 通常の文字列 --------------------//
let str1 = 'あいうえお';
let code1 = [];
for (let i = 0; i < str1.length; i++) {
code1.push(str1.charCodeAt(i));
}
console.log(code1); //[12354, 12356, 12358, 12360, 12362]
// サロゲートペアありの文字列 --------------------//
let str2 = '😀';
let code2 = [];
for (let i = 0; i < str2.length; i++) {
code2.push(str2.charCodeAt(i));
}
console.log(code2); //[55357, 56832]
スマイルマーク1つですが、コンソールで見ると2byteの数字が2つ配列に入っていることがわかります。
これが、冒頭で「楽しい😀」をlenghtプロパティで調べた時に「5」と表示された理由です。
以下のサイトでサロゲートペアの一覧を見る事ができます。
lenghtプロパティで文字数をカウントする時のサロゲートペア対策
文字数カウントでサロゲートペア対策をする方法として2つ紹介します。
- ①splitでサロゲートペアを基準に分割してカウントする
- ②正規表現でサロゲートペアを文字として配列に入れてカウントする
では1つずつ解説します。
①splitでサロゲートペアを基準に分割してカウントする
let str = 'プログラミング🔁は楽しい😀ね!';
let SurrogatePair = str.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g).length - 1;
let num = str.length - SurrogatePair;
console.log(num); // 15
こんな感じです。
str変数にサロゲートペアが含まれる可能性がある文字列が入るようにします。
ただ、このままだとサロゲートペアの分だけlengthプロパティの数値が実際の文字数よりも増えてしまいます。
そこで、サロゲートペアを基準に分割して、その数を指標にlengthプロパティから引き算するという計算方法です。
ちょっとわかりにくいので、もう少し詳しいのを載せておきます。
let str = 'プログラミング🔁は楽しい😀ね!';
console.log(str.length); // 17
let SurrogatePair = str.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g);
console.log(SurrogatePair); // ['プログラミング', 'は楽しい', 'ね!']
let SurrogatePairNum = SurrogatePair.length - 1;
console.log(SurrogatePairNum); // 2
let num = str.length - SurrogatePairNum;
console.log(num); // 15
splitで分割すると、要素数はサロゲートペアの数+1になります。
なので、そこから1を引いてた数を、lengthプロパティで取得した数から引くという感じです。
②正規表現でサロゲートペアを文字として配列に入れてカウントする
let str = 'プログラミング🔁は楽しい😀ね!'.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\s\S]/g) || [];
console.log(str); // ['プ', 'ロ', 'グ', 'ラ', 'ミ', 'ン', 'グ', '🔁', 'は', '楽', 'し', 'い', '😀', 'ね', '!']
console.log(str.length); // 15
こっちはシンプルですね。
文字列をいきなりlengthプロパティにかけるとサロゲートペアは2カウントされてしまいますが、配列の要素にしてしまえば、その要素数をカウントするだけです。
サロゲートペアの文字を使うことはあまりないと思いますが、思わぬバグが起きないためにも、このような知識は持っておくとよいかと。