2
1

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#で異体字・IVS・JIS X 0208/0212/0213を扱うライブラリ「KanjiVariants」を作った

2
Posted at

.NET向けの漢字ライブラリ KanjiVariants を作り、NuGetで公開しました。

KanjiVariantsは、MJ縮退マップを利用した異体字・代替文字の検索や置換、JIS文字集合への収録判定、常用漢字・教育漢字・人名用漢字、常用漢字の音訓、送り仮名、MJ文字情報などを扱うためのライブラリです。

NuGet:

dotnet add package KanjiVariants --version 1.0.0

GitHub:

ドキュメント:


作った理由

業務で漢字を扱っていると、

  • この文字はJIS X 0208に入っているのか
  • JIS X 0213なら第1面か第2面か
  • この異体字を別の文字へ置き換えられるか
  • IVS付き文字をどう扱うか
  • 常用漢字か
  • 人名用漢字か
  • 小学校の何年生で習う漢字か
  • 常用漢字表にその読みが載っているか
  • MJ文字情報ではどう定義されているか

といった判定が必要になることがあります。

一つ一つ調べることはできますが、Unicode、JIS、MJ文字情報、常用漢字表など複数の情報源をまたぐことになり、実装も確認もかなり大変です。

そこで、これらを.NETからまとめて扱えるライブラリとしてKanjiVariantsを作りました。


対応環境

KanjiVariants 1.0.0は net6.0 をターゲットにしています。

そのため、.NET 6以降のアプリケーションから利用できます。

NuGet上でも、

  • .NET 6
  • .NET 7
  • .NET 8
  • .NET 9
  • .NET 10

などから利用可能なパッケージとして認識されます。

.NET Framework 4.8には対応していません。


JIS文字集合への収録判定

Kanji.IsSupported を使用すると、指定した漢字がJIS文字集合に含まれているかを判定できます。

using KanjiVariants;

bool supported = Kanji.IsSupported("漢", CharacterSet.JisX0208);

Console.WriteLine(supported);

現在対応している CharacterSet は以下です。

public enum CharacterSet
{
    JisX0208 = 0,
    JisX0213Plane1 = 1,
    JisX0213Plane2 = 2,
    JisX0213 = 3,
    JisX0212 = 4
}

JIS X 0208

bool supported =
    Kanji.IsSupported("漢", CharacterSet.JisX0208);

JIS X 0208全体を対象にします。

第1水準・第2水準はJIS X 0208内部の分類なので、APIとしては分離していません。


JIS X 0213

第1面、第2面を個別に判定できます。

bool plane1 =
    Kanji.IsSupported("漢", CharacterSet.JisX0213Plane1);

bool plane2 =
    Kanji.IsSupported("漢", CharacterSet.JisX0213Plane2);

どちらかに含まれているかだけ知りたい場合は、

bool supported =
    Kanji.IsSupported("漢", CharacterSet.JisX0213);

と書けます。


JIS X 0212

JIS X 0212にも対応しています。

bool supported =
    Kanji.IsSupported("丂", CharacterSet.JisX0212);

JIS X 0212はJIS X 0208の上位集合ではなく、独立した文字集合として扱っています。


異体字・代替文字の検索

KanjiVariantsの中心機能の一つが、MJ縮退マップを利用した代替候補の検索です。

var alternatives =
    Kanji.GetAlternatives("髙", CharacterSet.JisX0208);

foreach (var item in alternatives)
{
    Console.WriteLine(item);
}

指定した文字集合で利用可能な代替候補を取得できます。


文字列全体の置換

文章中の文字を対象文字集合に合わせて置換する場合は KanjiText.Replace を使用します。

string result =
    KanjiText.Replace(
        "髙橋さん",
        CharacterSet.JisX0208);

Console.WriteLine(result);

ただしKanjiVariantsは、

一般的な旧字体・異体字をすべて新字体へ変換する辞書

ではありません。

通常の置換は、使用しているMJデータ上で一意な変換先が定義されている場合に行います。


IVS / SVS

KanjiVariantsではIVS/SVS付き文字も扱えます。

例えば、

var character = KanjiCharacter.Parse("辻󠄀");

のように、基底文字とVariation Selectorを一つの論理文字として扱います。

KanjiCharacter は次のような構造です。

public readonly record struct KanjiCharacter
{
    public Rune BaseCharacter { get; }
    public Rune? VariationSelector { get; }
}

登録されていないVariation Sequenceは、単純にVariation Selectorを捨てて扱うことはしません。


Variation Selectorを基底文字へフォールバック

必要な場合だけ、明示的にフォールバックを許可できます。

string result =
    KanjiText.Replace(
        text,
        CharacterSet.JisX0208,
        KanjiFallbackOptions.AllowVariationSelectorFallback);

通常は安全側に倒し、

IVS付き文字
↓
勝手に基底文字へ変換

とはしない設計にしています。


常用漢字

常用漢字かどうかを判定できます。

bool isJoyo = JoyoKanji.IsJoyo("漢");

情報を取得する場合は、

var entry = JoyoKanji.Get("漢");

if (entry is not null)
{
    Console.WriteLine(entry.Character);

    foreach (var reading in entry.Readings)
    {
        Console.WriteLine(
            $"{reading.Type}: {reading.Reading}");
    }
}

と書けます。


読みから常用漢字を検索

var entries =
    JoyoKanji.FindByReading("こう");

foreach (var entry in entries)
{
    Console.WriteLine(entry.Character);
}

ひらがな・カタカナの違いは吸収します。

JoyoKanji.FindByReading("コウ");

でも検索できます。

ただし、長音や読みの推測を行う曖昧検索ではなく、正規化後の完全一致です。


表外読み

常用漢字表に掲載されている読みかどうかも判定できます。

bool listed =
    JoyoReading.IsListedReading("漢", "かん");

表外読みかどうかは、

bool hyogai =
    JoyoReading.IsHyogaiReading("漢", "あや");

で確認できます。

ここでいう「表外読み」は、

常用漢字表に掲載されていない読み

という意味です。

その読み自体が誤りであることを判定するAPIではありません。


教育漢字

教育漢字かどうか、何年生の配当かを取得できます。

bool isEducation =
    EducationKanji.IsEducationKanji("学");

KanjiGrade? grade =
    EducationKanji.GetGrade("学");

Console.WriteLine(grade);

学年別一覧も取得できます。

var firstGrade =
    EducationKanji.GetByGrade(KanjiGrade.Grade1);

foreach (var character in firstGrade)
{
    Console.WriteLine(character);
}

現在のデータでは、

1年生  80字
2年生 160字
3年生 200字
4年生 202字
5年生 193字
6年生 191字

を収録しています。


小学校・中学校・高等学校ごとの音訓配当

漢字そのものの学年配当とは別に、常用漢字の「読み」がどの学校段階に配当されているかも参照できます。

var readings =
    JoyoKanjiEducation.GetReadings("漢");

foreach (var item in readings)
{
    Console.WriteLine(
        $"{item.Reading.Reading}: {item.Stage}");
}

段階は、

public enum SchoolStage
{
    Elementary,
    JuniorHigh,
    HighSchool
}

です。


人名用漢字

人名用漢字も判定できます。

bool jinmeiyo =
    JinmeiyoKanji.IsJinmeiyoKanji("凜");

常用漢字または人名用漢字として名前に利用できる範囲を判定する場合は、

bool usable =
    JinmeiyoKanji.IsNameUsableKanji("凜");

を使用できます。


送り仮名

文化庁「送り仮名の付け方」に掲載されている語を検索できます。

var entries =
    Okurigana.Find("行う");

foreach (var entry in entries)
{
    Console.WriteLine(entry);
}

通則番号から取得することもできます。

var rule1 =
    Okurigana.GetByRule(1);

これは任意の日本語単語について送り仮名の正誤を判定する形態素解析APIではありません。

原典に掲載されている語・例外・許容表記等を参照するためのAPIです。


MJ文字情報

MJ文字情報一覧表の情報も参照できます。

var entry =
    MjCharacter.GetByMjGlyphName("MJ000001");

if (entry is not null)
{
    Console.WriteLine(entry.MjGlyphName);
    Console.WriteLine(entry.CorrespondingUcs);
    Console.WriteLine(entry.ImplementedUcs);
}

Unicode文字からMJ文字を検索することもできます。

var matches =
    MjCharacter.Find("髙");

foreach (var match in matches)
{
    Console.WriteLine(match.Entry.MjGlyphName);
    Console.WriteLine(match.MatchKind);
}

一致理由はFlagsで保持しています。

[Flags]
public enum MjCharacterMatchKind
{
    None = 0,
    ImplementedUcs = 1 << 0,
    CorrespondingUcs = 1 << 1,
    Ivs = 1 << 2,
    Svs = 1 << 3,
    CompatibilityIdeograph = 1 << 4
}

文字情報をまとめて取得する

個別APIを一つずつ呼ばなくても、KanjiDiagnostics.Analyze を使うと、一文字について複数の情報をまとめて取得できます。

var result =
    KanjiDiagnostics.Analyze("髙");

Console.WriteLine($"U+{result.BaseCodePoint:X}");
Console.WriteLine($"JIS X 0208: {result.IsJisX0208}");
Console.WriteLine($"JIS X 0212: {result.IsJisX0212}");
Console.WriteLine($"JIS X 0213: {result.IsJisX0213}");
Console.WriteLine($"常用漢字: {result.IsJoyo}");
Console.WriteLine($"教育漢字: {result.IsEducationKanji}");
Console.WriteLine($"人名用漢字: {result.IsJinmeiyoKanji}");

さらに、

result.JoyoEntry
result.EducationGrade
result.ReadingEducation
result.MjMatches

なども参照できます。

文字を調査する用途では、このAPIが一番使いやすいと思います。


JIS判定と文字コード変換は別

KanjiVariantsのJIS関連APIは、

このUnicode文字が
JIS X 0208 / 0212 / 0213の対象か

を判定するためのものです。

Shift-JISやEUC-JPのバイト列を生成するエンコーダーではありません。

例えば、

JIS X 0208に入っている
=
そのまま任意のShift-JIS実装で必ず安全に変換できる

という意味ではありません。

文字集合への所属判定と、実際のエンコーディング処理は分けて考えています。


データ出典

KanjiVariantsでは、主に以下のデータを利用しています。

  • IPA MJ文字情報一覧表
  • MJ縮退マップ
  • Unicode Ideographic Variation Database
  • Unicode Standardized Variants
  • Unicode Unihan
  • ICU JIS X 0212対応表
  • 文化庁 常用漢字表
  • 文化庁 送り仮名の付け方
  • 文部科学省 学年別漢字配当表
  • 文部科学省 音訓の小・中・高等学校段階別割り振り表

生成済みデータには、これら外部データを加工したものが含まれます。

そのため、ライブラリ独自コードのMIT Licenseとは別に、外部データの出典・利用条件を LICENSE-NOTICES.md にまとめています。

詳細:


パフォーマンスについて

JIS文字集合の判定では、用途に応じて配列やbit tableを使用しています。

例えばJIS X 0208 / 0213はコードポイントによる直接参照、JIS X 0212はbit tableを使用しています。

MJ文字情報の検索ではDictionaryを利用しています。

.NET 8の FrozenDictionary についても比較しましたが、MJ文字図形名の検索では通常のDictionaryの方が速く、構築コスト・メモリ使用量も増えたため、1.0.0では採用していません。

新しいAPIを使うことよりも、実測結果を優先しています。


.NET Standard対応について

当初は netstandard2.1 への対応も検討しました。

しかしKanjiVariantsの公開APIでは、

System.Text.Rune

を使用しています。

Runeは netstandard2.1 の標準APIには含まれないため、公開APIを変更せずに単純にTargetFrameworkを下げることはできませんでした。

そのため1.0.0では、

net6.0

を維持しています。

.NET 6以降のアプリケーションでは利用できます。


NuGet公開

NuGet.org:

Visual StudioのNuGetパッケージマネージャーから、

KanjiVariants

で検索してインストールすることもできます。

CLI:

dotnet add package KanjiVariants --version 1.0.0

ソースコード・ドキュメント

GitHub:

APIドキュメント:

NuGet:


まとめ

KanjiVariants 1.0.0では、

  • 異体字・代替文字検索
  • MJ縮退
  • IVS / SVS
  • JIS X 0208
  • JIS X 0212
  • JIS X 0213
  • 常用漢字
  • 教育漢字
  • 人名用漢字
  • 常用漢字音訓
  • 小学校・中学校・高等学校ごとの音訓配当
  • 表外読み
  • 送り仮名
  • MJ文字情報
  • 文字診断

を.NETから扱えるようにしました。

日本語の文字処理では、「見た目は同じように見えるけれどUnicode上は別の文字」「Variation Selectorが付いている」「JIS文字集合によって収録範囲が違う」といった問題に遭遇することがあります。

そういった場面で、個別にデータを調べて実装する手間を少しでも減らせればと思っています。

まだ1.0.0なので、実際に使ってみて気になる点や不足している機能があれば、GitHub Issuesで教えてもらえると助かります。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?