🍇

読みやすい改行のための軽量な分かち書き器をPHPで

2024/12/31に公開

BudouXという汎用の日本語分かち書きライブラリがあります。

https://developers-jp.googleblog.com/2023/09/budoux-adobe.html

以前これのJava版実装をPHPに移植したことがありました。

https://zenn.dev/tadsan/articles/50c0c2b9fd443e

アルゴリズムがとても簡単なのでPHPでも簡単に動かせるので便利です。

ところで、先述のGoogle社のブログにはこんなことが書いてあります。

今後の展開

現在 BudouX は ICU (International Components for Unicode) の一部になっており、ウェブに限らずさまざまなプラットフォームで利用され始めています。

おや。ICUというのは、PHPでいえばIntlモジュールのことです。PHPでもBudouXが使えるのではないのでしょうか!

ICUにBudouXがやってきた!

改めて、ICUとはユニコードコンソーシアムが開発しているUnicodeおよび国際化と地域化のためのライブラリです。

2024年12月時点での最新版は2024年10月24日のICU 76で、BudouXがマージされたのは2023年4月13日にリリースされたICU 73です。

2023-04-13: ICU 73 updates to CLDR 43 locale data with various additions and corrections. ICU 73 improves Japanese and Korean short-text line breaking, reduces C++ memory use in date formatting, and promotes the Java person name formatter from tech preview to draft. For details, see Downloading ICU > ICU 73.

https://icu.unicode.org/download/73

Line breaking with Japanese phrase-based breaking is now using the BudouX machine learning implementation for better quality. (ICU-22100, see ICU 71 ICU-21699 for context)

使用条件を満たしているか調べる

WebサーバまたはコマンドラインからPHPを実行して、INTL_ICU_VERSIONを見てみましょう。

以下はmacOSでHomebrewを使ってインストールしたPHP 8.4.2です。

% php -r 'var_dump([INTL_ICU_VERSION, INTL_ICU_DATA_VERSION]);'
array(2) {
  [0]=>
  string(4) "76.1"
  [1]=>
  string(4) "76.1"
}

ばっちりですね!


ところで、一般的なサーバ環境ではICU 73以上を使えるのでしょうか…?

ためしにDebianのlibicu-devパッケージを見てみましょう。

https://packages.debian.org/search?keywords=libicu-dev

実際に手元のDebian 12(Bookworm)でビルドしたPHPで試してみるとこうなります。

% php -r 'var_dump([INTL_ICU_VERSION, INTL_ICU_DATA_VERSION]);'

array(2) {
  [0] =>
  string(4) "72.1"
  [1] =>
  string(4) "72.1"
}

残念… ではあるのですが、実際にはフレーズベースの分かち書きはICU 71の時点で取り入れられていて、ICU 73で入ったのは「BudouXを使ったよりよい品質の実装」ので、BudouXと完全互換に同じ分割結果になることにこだわらなければ、使えます。


とても残念なことに、PHPのオンラインサンドボックス環境としてよく使われている3v4l.orgはICU 63がリンクされているので、日本語の分かち書き検証には使えません。また、最近よく使われているブラウザ上のWasmランタイムでPHPを動かせるPHP Playground(php-play.dev)は、そもそもICU(intl)がリンクされていません。

使い方

IntlBreakIteratorクラスを使います。

https://php.net/IntlBreakIterator

$iter = IntlBreakIterator::createLineInstance('ja-u-lw-phrase');
$iter->setText('BudouX: 読みやすい改行のための軽量な分かち書き器');
var_dump(iterator_to_array($iter->getPartsIterator()));
// => array(8) {
//   [0] =>
//   string(8) "BudouX: "
//   [1] =>
//   string(6) "読み"
//   [2] =>
//   string(9) "やすい"
//   [3] =>
//   string(9) "改行の"
//   [4] =>
//   string(9) "ための"
//   [5] =>
//   string(9) "軽量な"
//   [6] =>
//   string(15) "分かち書き"
//   [7] =>
//   string(3) "器"
// }

IntlBreakIterator::createLineInstance('ja-u-lw-phrase')というのは呪文です。いやちゃんと意味はあるのですが… いまのところはこういうものだと思ってください。

$iter->getPartsIterator()イテレータ、つまり「foreachできるオブジェクト」を返します。そのままforeachに渡してもいいですし、よくわからんという人はiterator_to_array()関数に渡せば配列に変換できます。

Intl vs BudouX for PHP

PHP実装とIntl、どちらを使えばよいのでしょうか…?

https://github.com/zonuexe/budoux-php

ICU 71以上が入っている環境ならICUを使った方がいいんじゃないかと思います。パフォーマンス30〜40倍くらい違うので…

計測コードは以下の通り。

https://github.com/zonuexe/bench-budoux

Discussion