【Python】日本語全角／半角変換ライブラリmojimoji v0.0.12で報告されているIssueと拙作Habachenでの対応について

<font color="Red">この記事には古い内容が含まれています。以下で挙げている issue は、 mojimoji 最新版である v0.0.13 で解決されています（2024/01/13 追記）。</font>

## はじめに

- mojimoji とは

https://qiita.com/ikuyamada/items/fea6c8f81e7cac7cf318

https://github.com/studio-ousia/mojimoji

- Habachen とは

https://qiita.com/Hizuru/items/6b91d00fbe13ebb43867

https://github.com/Hizuru3/python-habachen

mojimoji と Habachen は、いずれもネイティブ拡張を用いた CPython 上で動作する日本語の全角／半角変換ライブラリです。 Habachen は私が開発しています。

以前書いた [Habachen の紹介記事](https://qiita.com/Hizuru/items/6b91d00fbe13ebb43867)ではそのパフォーマンスに焦点を当てていますが、元々は mojimoji の不満点を解消した CPython 用の日本語文字列変換ライブラリを自前で作り上げることが目的で[^1]、高速化は二の次でした。 

Habachen では、全角／半角に加えひらがな／カタカナの相互変換も可能になっている他、 mojimoji に見られたいくつかの問題点も解消されています。

## Issues

2023年10月現在、mojimoji では以下の issue が立てられています。

<font color="Red">2024年1月現在、更新に伴い close 済みです（2024/01/13 追記）。</font>

### バックスラッシュが変換されない
https://github.com/studio-ousia/mojimoji/issues/21

タイトルの通りです。

- Habachen の場合

期待通り、半角バックスラッシュ (U+005C) と全角バックスラッシュ (U+FF3C) が相互に変換されます[^2]。

```python
>>> import habachen
>>> habachen.han_to_zen('\\')
'＼'
>>> habachen.zen_to_han('＼')
'\\'
```

### めっちゃ環境依存なバグ
https://github.com/studio-ousia/mojimoji/issues/23

上のリンクで貼られていたコードでは再現できなかったのですが、以下のコードを Google Colab 上で実行してみたところ、おそらく報告されているものと同じ不具合により、セッションがクラッシュしました。

```
!pip install mojimoji
```
```python
import mojimoji

def bug_test(text):
    text = mojimoji.zen_to_han(text)
    text = mojimoji.han_to_zen(text)
    return text

bug_test("ﾞﾝﾗｲﾌﾚｷ")
```

どうやら文字列全体が半角の濁点から始まる場合に `han_to_zen()` 関数内で未定義動作を踏んでしまっているようです。今回の試行でのクラッシュは、未初期化変数の参照[^3]とメモリブロックの範囲外アクセスが連鎖して malloc ヘッダが破壊されたことにより発生したものと見られます。

- Habachen の場合

以下のコードは、 Google Colab 上で正常に動作します。

```
!pip install habachen
```
```python
import habachen

def bug_test(text):
    text = habachen.zen_to_han(text)
    text = habachen.han_to_zen(text)
    return text

bug_test("ﾞﾝﾗｲﾌﾚｷ")
```

### Cannot install on Python 3.12
https://github.com/studio-ousia/mojimoji/issues/24

本文で書かれている通り、 Cython によって生成される C++ コード内で古い API が使われているために Python 3.12 でインストールできないという問題です。 mojimoji では Python 3.3 以降非推奨である文字列表現形式 (Py_UNICODE*) がバッファとして利用されており、これと紐づけられている関数群が Python 3.12 で軒並み廃止されたことが影響しています。

なお、この issue に対しては現在[プルリクエスト](https://github.com/studio-ousia/mojimoji/pull/25)が出されています。

- Habachen の場合

Python 3.12 でもインストールできます。

---

リンク追加要請の [issue](https://github.com/studio-ousia/mojimoji/issues/22) もありましたが、ここでは割愛します。

## 最後に

mojimoji はシンプルで扱いやすいライブラリですが、~~現在~~バージョン 0.0.12 ではいくつか基本的な部分での問題が確認されています[^4]。 Habachen では、ここで取り上げた問題は解消されており、パフォーマンスも改善されているので、高速な全角／半角・ひらがな／カタカナ変換ライブラリを求めている方は、一度 Habachen を試していただければと思います。

本記事では CPython のネイティブ拡張を扱いましたが（C/C++ 難しい…）、最後にオススメの Pure Python 文字列変換ライブラリのリンクを貼っておきます。 Pure Python 実装ライブラリの利点としては、 C コンパイラが利用できない環境でもインストールできること、 Python 本体のバージョンアップによる内部 API 変更の影響を受けにくいことが挙げられます。

- jaconv

https://github.com/ikegami-yukino/jaconv/

- Utsuho

https://github.com/juno-rmks/utsuho/

[^1]: 全角／半角変換自体はそれほど複雑な処理ではないのですが、それなりに速いものを CPython 拡張モジュールとしてバグなく実装するのは結構面倒くさかったりします。
[^2]: 環境によっては、半角バックスラッシュが半角の円マークとして表示されるかもしれませんが、コードポイント上は同じものを指しています。
[^3]: この時点で未定義動作となります。未定義動作なので、環境や条件によっては別の不具合が生じたり、問題が表出しなかったりしても不思議ではありません。
[^4]: 作者の方は他のことで忙しいのか、[issue #21](https://github.com/studio-ousia/mojimoji/issues/21) が立てられてから1年以上更新されて~~いないようです~~いなかったのですが、先日アップグレードされたようです（2024/01/13 追記）。