1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#・VB.NETで異体字を扱うライブラリItaijiを作っている

Posted at

C#・VB.NETで異体字を扱うライブラリItaijiを作っています。
対応環境は以下です。依存ライブラリはありません。

  • .NET 5.0+
  • .NET Framework 3.5+

リポジトリはこちらです。

まだ0.1.0ですが、nugetパッケージをpublishしてみました。

0.xのため、予告なく破壊的変更を加える場合があります。
また、使用は自己責任でお願いします。

制作動機

unicodeには 異体字 の仕組みがあります。異体字は、unicode上で字の形状を表す仕組みです。
主に人名など、字の形状を区別する必要がある場合に使用されます。

ある文字の後ろに異体字セレクタという専用のキャラクターを付与すると、字の形が変わります。

image.png

文字列ではあるので、charstring で問題なく扱えはするのですが、後述の問題のためにさまざまな面倒を引き起こします。
もっと簡単に扱うためにこのライブラリを作るに至りました。

サロゲートペア

面倒の1つはサロゲートペアです。
.NETは文字列を内部的にUTF-16、すなわち、1文字を基本16bitで扱います。
しかし、絵文字や一部の使用頻度の低い漢字などは、16bit(0xFFFF)を超えるコードポイントが割り当てられています。

unicodeは一部の領域をこれらの文字用に予約し、2つのコードポイントのペアでこれらの文字を表現することにしました。
つまり、1文字が1charで表されたり、2charで表されたりすることになります。

このため、stringを単純にcharの配列として扱うと、サロゲートの扱いに悩まされることになります。

var str = "夕飯は𩸽"; //ほっけ  U+29E3D
foreach(var ch in str)
{
    //サロゲートペアが含まれていると、char単位ではうまくいかない
    Console.WriteLine(ch); // '夕', '飯', 'は', 0xD867, 0xDE3D
}
Console.WriteLine(str.Length); // 5!

そして、異体字セレクタ自体もサロゲートペアで表されます(漢字が主に使うものは)
見えている文字は1つでも、内部では ベース文字 2char + 異体字セレクタ 2charの4charで表されている可能性もあります。

image.png

救世主、System.Text.Rune……?

サロゲートペアの問題は、別に今に始まったことではありません。
サロゲートペアを気にせずコードポイントを扱うための手段として、.NET Core 3.1から System.Text.Rune という構造体が追加されています。

var str = "夕飯は𩸽"; //ほっけ  U+29E3D
foreach(var rune in str.EnumerateRunes())
{
    //Runeであれば、サロゲートペアを正しく扱える
    Console.WriteLine(rune); // '夕', '飯', 'は', "𩸽"
}
Console.WriteLine(str.EnumerateRunes().Count()); // 4

ただ、.NET Framework向けには、バックポートパッケージすらも提供されていません。
.NET的には、いい加減.NET Frameworkから脱却してほしいということかもしれませんが・・・

また、Runeを使っても、異体字セレクタは1つのRuneとして扱われるため、
異体字を扱うには、前のRuneをキャッシュしておくなど、少し手間が要ります。

Itaijiの解決策

そこでItaijiは、KanjiChar という構造体を作って、「ベースのRune」と「異体字セレクタのRune」をセットで管理するようにしました。

using Itaiji;
var str = "辻󠄀さんの夕飯は𩸽"; //辻は異体字 𩸽 ほっけ  U+29E3D
foreach(var kanji in str.EnumerateKanji())
{
    //異体字セレクタを正しく扱える
    Console.WriteLine(kanji); // "辻󠄀", 'さ', 'ん', 'の', '夕', '飯', 'は', "𩸽"
}
Console.WriteLine(str.EnumerateKanji().Count()); // 8

また、Rune をバックポートすることで、.NET Framework対応を実現しています。

2系統の異体字

異体字にはじつは、2系統が存在します。Adobe-Japan1とMoji_Joho(Hanyo-Denshi)です。

Adobe-Japan1はその名の通り、Adobeが定めた文字集合です。対してMoji_Johoは戸籍や住基といったデータで使用される、いわば国が定めた文字集合です(実際の管理団体は民間団体であるようです)。
これら2系統の異体字は、別々にIVD(Ideographic Variation Database)に登録されています。

何が問題かというと、「同じ(にしか見えない)文字であっても、Adobe-JapanとMoji_Johoの2つが定めている場合がある」ということです。次の画像を見てください。

u8fbb-e1384478400894.png
https://moji.or.jp/mojikiban/aboutivs/

点が1つの「辻」はE0100とE0102の2つで定められています。つまり、unicodeには点が1つの「辻」を表す方法が2種類ある ということです。

そして、世の中のIME等の入力補助機能は、多くはAdobe-Japan1の異体字のみの対応である場合がほとんどです。

長くなりましたが、Moji_Johoの異体字を扱うことを想定したシステムでも、Adobe-Japan1の異体字が入力としてやってくる ということを現実的に想定しなければいけない場合が存在します。

Itaijiでは

Itaijiでは各コレクションのリストを持ち、字がどのコレクションに属するかを判定できるようにしました。

using Itaiji;

var str = "辻󠄀" // adobe-japanのツジ
if(str.HasInvalidIvsAsMojiJoho()){
    throw new Exception("扱えない異体字が含まれています。");
}

なお、Moji_Joho<->AdobeJapan1への自動変換はできません。
Adobe-Japan1と、Moji_Joho(Hanyo-Denshi)の間のコードポイントの対応は、残念ながら定められていません……

その他のこだわり

  • 一応、パフォーマンスを気にしています。見様見真似でアロケーションを抑えたり、コレクションのデータを圧縮したりなど
  • .NET Framework向けには Span の対応はあえてせず、依存なしにこだわりました

おわりに

まだ0.1.0のライブラリであり、Issue・プルリク・マサカリなどなど大歓迎です。
まだ自由研究の域を出ないライブラリではありますが、役立つ場面がどこかにきっとある……あるのではないでしょうか。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?