サロゲートペアが問題の本質

対策を色々と考えて調べていると、この絵文字の問題は「サロゲートペア」と呼ばれる Unicodeの文字を扱う上での方式が、問題の本質と判って来ました。

 

Unicodeは16bitの文字コードですが、コードを16進数で書くと4桁になります。しかし16bitで区別できるのは最大6万5536字です。 例えば特殊漢字を含めた漢字は7万字以上で、これらを扱える様に考えられた方式がサロゲートペアです。

この方式は16bitコードを2つ組み合わせて拡張した文字に割り当て、それらの文字は32bitになります。 その結果、16bitコードの文字と32bitコードの文字が文字列中に共存するので、これに正しく対処できないシステムは文字コードの読取り間違いを生じて、文字化け等に陥ってしまいます。

 

JavaScriptでこの問題を扱う上で、以下のページは参考になります。

 

    JavaScriptでのサロゲートペア文字列のメモ

 

このページから戴いた「𩸽」(ホッケ)の文字の例です。

「今日は𩸽を食|べました」

のカーソル位置で「Both-WH」を実行して「通常表示画面」に戻って来ると、

「今日は𩸽を食█ました」

これは32bit文字を検索メソッドが2回処理し「べ」を間違って削除しています。 絵文字に限らず、サロゲートペアの文字全体に「Both-WH」が対処する必要があります。

 

「絵文字による文字化け対策用」のフィルターは「Both-WH」以下の点で不適当だと判って来ました。

 

 ◎「\u2600」~「\u27BF」の記号文字がフィルターに含まれている(過剰)

 ◎「\ud83e」系列のWin絵文字等がフィルターから多数が抜け落ちている(欠落)

 ◎「\ud867」系列を始めとしたサロゲートペア文字が抜け落ちている(欠落)

 

これらの問題は、サロゲートペアの「\ud800〜\udBFF」で始まる文字系列をフィルターすれば、全て解決します。 しかしWin絵文字も特殊漢字も全忌避というフィルターは、もっと改善の余地があるはずと考えて方策を探しました。

 

 

サロゲートペアの文字を1文字として判定する

「Both-WH」でサロゲート文字(Win10絵文字を含む)が問題になるのは、アクティブ行でマーク文字の位置を調べる際に、検索メソッドがサロゲート文字を2文字として(2回のループで)判定するからです。

 

これを1回のチェックで判定できるコードを作れば、マーク文字の位置を間違わずに調べられるはずです。 ネット上の記事を探すと、使えそうな方法がありました。

 

    文字列を1文字ずつ配列化(サロゲートペアを考慮)

 

要するに、文字列を1文字ずつの配列に変えてしまうのです。 サロゲート文字も配列の単位としては1個なので、配列をチェックしながらカウントすれば、サロゲート文字が行内にあっても、正しくカーソル位置を求められるはずです。

 

下は上記ページのサンプルで、「match()」メソッドを使って、サロゲート文字を含んだ文字列を1文字ずつの配列に変えるものです。

 

"🌕には𩸽".match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\s\S]/g)

 

以下が得られる配列で、どの文字も配列の1要素に収まります。

 

["🌕", "に", "は", "𩸽"]

 

下は、これを「Both-WH」に応用した、マーク文字の位置を調べるコードです。

 

var real = activeline.textContent.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\s\S]/g );
var real_result =0;
if( real != null ){
    for( var k=0; k <=real.length; k++) {
        if( real[k] !="█" ){ real_result += 1; }
        else if( real[k] =="█" ){ break;} } }

 

変数「real」は、1文字ずつの配列に分解したアクティブ行です。

変数「real_result」は、行先頭からマーク文字までのステップをカウントします。

 

テストすると、Win10絵文字や特殊漢字があっても、マーク文字まで正しくカーソルが移動できる事が判りました。

 

もう少し調べると、正規表現でサロゲートペアの文字数を1文字に数える「u」フラグがある事が判りました。 これについて紹介した記事では「 .match(/./ug); 」で済むというのでテストしてみると、これも問題なく使える様です。

 

var real = activeline.textContent.match(/./ug );
var real_result =0;
if( real != null ){
    for( var k=0; k <=real.length; k++) {
        if( real[k] !="█" ){ real_result += 1; }
        else if( real[k] =="█" ){ break;} } }

 

 

それでもカーソル位置を狂わす特殊文字

サロゲート文字の問題は、上記のコード導入によって大幅に改善されました。 もはや、これまでのフィルターは不要になったのですが、実はここから更に難題が待っていました。 Win10文字のチェック時に問題になっていたズレが2文字以上の文字の問題です。 これは簡単に書けないので次ページに。