読者です 読者をやめる 読者になる 読者になる

Text::ASCIITableを 2文字幅の文字に対応させる

perl


このようにText::ASCIITableはデフォルトでは半角文字だけで日本語等の
全角幅の文字を含めるとテーブルにずれが生じてしまいます。

#!perl
use strict;
use warnings;

use utf8;
use Text::ASCIITable;

binmode STDOUT, ":utf8";

my $japanese_table = Text::ASCIITable->new({headingText => '価格表',});

$japanese_table->setCols('名前', '値段', '在庫');
$japanese_table->addRow('りんご(apple)', '80円', '1000個');
$japanese_table->addRow('バナナ(banana)', '120円', '8500本');
$japanese_table->addRow('スイカ(watermelon)', '500円', '35玉');

print $japanese_table;

上記のプログラムを実行すると以下のようになり、ずれます。
コピペだとずれるので, 画像にしてます。
(端末エミュレータMacOSXの iTerm2 Monaco + Osakaフォント)


修正

Text::ASCIITableは文字長を取得するために, length関数を使っています。
ASCIIなので、文字長 = 文字幅になるのですが、日本語のような言語では
そうならないので、length関数を上書きすることで対応を行いました。
ビルトイン関数を書き換えるのはどうかとも思うのですが、とりあえず
一番簡単な対応方法だと思います。コードは以下のとおりです。
文字幅の取得はこちらを参照しました.

#!perl
use strict;
use warnings;

use utf8;
use Unicode::EastAsianWidth;

BEGIN {
    no warnings 'redefine';
    *CORE::GLOBAL::length = sub {
        my $str = shift;
        my $width = 0;

        while ($str =~ m/(?:(\p{InFullwidth}+)|(\p{InHalfwidth}+))/go) {
            $width += ($1 ? length($1) * 2 : length($2));
        }
        return $width;
    };

    require Text::ASCIITable;
};

binmode STDOUT, ":utf8";

my $japanese_table = Text::ASCIITable->new({
    headingText => '価格表',
});

$japanese_table->setCols('名前', '値段', '在庫');
$japanese_table->addRow('りんご(apple)', '80円', '1000個');
$japanese_table->addRow('バナナ(banana)', '120円', '8500本');
$japanese_table->addRow('スイカ(watermelon)', '500円', '35玉');

print $japanese_table;

結果は以下のようになります。(環境は上記と同じです)


ハングルでもうまくいきます。

考察

Text::ASCIITableを 2文字幅の文字(東アジア限定)に対応させる方法を
示しました。修正にあたり思ったのですが、Text::ASCIITableはドキュメントに
'using ASCII Charsets'と書いているから, Text::ASCIITableモジュールで対応
させるのは正しくないかもしれません.


世界の文字で全角・半角が分かれてるのってアジアだけなんですかね ?
UNICODEでEAST_ASIAN_WIDTHっていうトピックが
わざわざあるのは、東アジア限定だからなのかな?それだったら、
Text::ASCIIWidthの length部分を上記の関数に書き換えるだけで対応可能です。
ただ今後文字が増えて、アジア地域以外にも全角文字導入ということになると、
別モジュール(Text::ASCIITable::EastAsian)みたいにした方がいいかもしれません。