Shogo's Blog

Aug 25, 2017 - 2 minute read - perl

Perl 5.26 & Unicode 9.0 で変わる書記素クラスタ(grapheme cluster)のお話

WEB+DB PRESS Vol.100が発売されましたね。 記念すべき Vol.100 おめでとうございます!

WEB+DB PRESS の連載「Perl Hackers Hub」今回のテーマは「【第46回】Perl 5.26で変わること」です。 Perl 5.26 で追加になった機能、アップグレードの際に気をつけなければならないところ( 特に @INC 問題とか )などに触れられているので、 Perl Monger の方はぜひ読むとよいと思います。

追加された機能のひとつとして Unicode 9.0 サポートが挙げられているのですが、以下のような簡単な紹介に留まっています。

Unicode 9.0にはオリンピックで活躍するであろう金銀 銅メダルの絵文字などが追加されました。

Unicode 9.0 で変わるのはそれだけではありません! Unicode 9.0 での 書記素クラスタ(grapheme cluster) の扱いを少し前に調査したので紹介します。

書記素クラスタ(grapheme cluster)とは

書記素クラスタ(grapheme cluster)とは、人間にとって自然な1文字を表すものです。

たとえば “é” という文字は一見1文字に見えますが、 length で文字数をカウントすると2文字としてカウントされます。

$ perl -Mutf8 -E 'say length "é"'
2

これは length がUnicodeのコードポイント数を数えており、 “é"が"e”(U+0065) + アクセント記号(U+0301) の2つのコードポイントで構成されているためです。

他にも異字体セレクタというのがあったり、 絵文字シーケンスというのがあったりして、 コードポイントの数=文字数とは限りません。

これらの文字たちを1文字として数えるための概念が書記素クラスタ(grapheme cluster)です。

Unicode 9.0での変更点

Unicode 8.0以前も書記素クラスタはあるのですが、 “👨‍👨‍👦"のような家族の絵文字が3文字とカウントされてしまったり、 “🇯🇵🇯🇵🇯🇵"のように国旗が連続していると1文字にカウントされてしまったりと、 問題がありました。 Unicode 9.0からはこれらの問題が解決されています。

詳しくは書記素クラスタに関する Unicode 9.0 以降と 8.0 以前の違いを参照してください。

Perlでの書記素クラスタ

書記素クラスタの使い方

Perlでは正規表現\Xが書記素クラスタにマッチします (Misc in perlrebackslash)。

use utf8;
use 5.24.1;
binmode(STDOUT, ":utf8");

my $str = "eé";
my @characters;

@characters = split //, $str;
say "splitを使った場合: ", join " ", @characters;

# 書記素クラスタ
@characters = $str =~ /\X/g;
say "書記素クラスタを使った場合: ", join " ", @characters;

文字数をカウントは「Perlで文字列の出現回数を調べる」の 方法が使えます。

use utf8;
use 5.24.1;

my $str = "eé";

my $length =()= $str =~ /\X/g;
say $length;

(後から知ったことですが、この =()= を使ったハックには 画像検索してはいけない名前 がついているらしいです)

Perl5.24とPerl5.26の違い

Perl 5.26 は Unicode 8.0 をサポートしているので、書記素クラスタの問題点が改善されています。 たとえば、Perl 5.24では “🇯🇵🇯🇵🇯🇵"が1文字としてカウントされてしまいますが・・・

use utf8;
use 5.24.1;

my $length =()= "🇯🇵🇯🇵🇯🇵" =~ /\X/g;
say $length; # => 1

Perl 5.26 では3文字としてカウントされます。

use utf8;
use 5.26.0;

my $length =()= "🇯🇵🇯🇵🇯🇵" =~ /\X/g;
say $length; # => 3

FYI: 他の言語の対応状況

Ruby

Unicode 絵文字にまつわるあれこれ (絵文字の標準とプログラム上でのハンドリング)で紹介されているように、 Ruby 2.4.0から対応してます。 Ruby 2.4.0は2016年12月25日に正式リリースされているので、この記事を書いている現在(2017年8月25日)は安心して使えます。

Python

残念ながら標準の正規表現ライブラリ re は書記素クラスタに対応していません。 サードパーティーのregexがUnicode 10に対応しているらしいので、 そちらを使うと書記素クラスタを扱えるようです。

Golang

https://github.com/google/re2/wiki/Syntax extended Unicode sequence (NOT SUPPORTED)

正規表現は対応していません、残念・・・

x/text に書記素クラスタを扱う機能を追加しようというIssueはありますが、今のところ進捗はないようです。

参考