4
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?

More than 3 years have passed since last update.

多国語対応の Text::VisualPrintf をアップデートした

Last updated at Posted at 2020-01-24

日本語と printf の相性

Perl に限らないが、プログラムの中で printf 系の関数を使って日本語を出力しようとすると、大概の場合思ったような結果を得られない。今時は GUI や HTML で表示したりすることが多いので、それほど問題にならないのかもしれないが、やはり必要になることはある。printf を使わずに済ませることも工夫次第で可能だが、やはりあれば便利だ。

Text::VisualPrintf

Text::VisualPrintf は、マルチバイト文字を処理するための Perl モジュールだ。最初に作ったのは2011年で MBprintf という名前だったが、2017年に Text::VisualWidth::PP を使うように修正した際に、今の名前に変更した。なぜそうしたかというと、やってる処理がほとんど同じで、より優れた部分があったからだ。ただ、足りない部分もあったので、それを提案して採り入れてもらえたので乗り換えた。

use Text::VisualPrintf qw(vprintf vsprintf);

のように読み込むと vprintfvsprintf という2つの関数が使えるようになる。

使い方については、解説するまでもあるまい。実際のコードの一部を載せておこう。

subst.pm
vprintf("%3d: %${from_max}s => %-${to_max}s",
        $i + 1, $from_re // '', $to // '');

出力例はこんな感じだ。揃っていないと大変読みにくい。

スクリーンショット 2020-01-24 13.19.07.png

ちなみに、内部で利用している Text::VisualWidth::PP には Unicode の結合文字に対応する修正を入れてもらってあるので、日本語に限らず大方の言語は正しく処理できるはずだ。

実装

実装はかなり手抜きだ。Perl の sprintf は、文字幅を考慮しないので、マルチバイト文字を含むパラメータは、含まない文字列に置き換えてしまう。そして、sprintf で出力した結果を変換して元の文字列を復活させる。

アルゴリズムは次の通り。

  1. まず、制御文字を含めてフォーマット文字列で使われていない文字2つ選ぶ。普通は^A^B が選ばれるはずだ。これを $a, $b とする。
  2. 置換の対象となる引数を $a で始まってそれに $b の繰り返しが続く文字列に返還する。
  3. sprintf の結果の /$a$b*/ に対応する部分を、順番にセーブしておいた文字列に変換する。

こうすることで、変換結果が最悪1カラムになってしまったとしても間違いなく変換することが可能だ。

Text::VisualPrintf::IO

今回、このモジュールを作ってみた。やっていることは単純で、重要なのはこの1行だけだ。

*IO::Handle::printf  = \&Text::VisualPrintf::printf;

IO::Handle の printf メソッドを置き換えているので、次のように使えるようになる。

use Test::More;
use Text::VisualPrintf::IO qw(printf);

open OUT, ">", \(my $out) or die;
OUT->printf("%10s\n", "あいうえお");
close OUT;
is( decode('utf8', $out),  "あいうえお\n", 'FH->printf' );

IO::Handle の実装を変えると IO::File 等でも使える。残念ながら

printf OUT "%10s\n", "あいうえお";

のような使い方はできない。vprintf というメソッドも設定してあるので、次のように使うことはできる。

vprintf OUT "%10s\n", "あいうえお";

いい考えがあればおしえてほしい。

ただ、どちらかと言うと vsprintf を使って普通に出力する方がお勧めだ。

インストール

Text::VisualPrintf

Perl プログラマなら cpanm はインストールしてあるだろうから、これで。

$ cpanm Text::VisualPrintf

いないと思うけど、ない人はこれでどうぞ。

$ curl -sL http://cpanmin.us | perl - Text::VisualPrintf

SEE ALSO

2020.02.06 追記

Truncation

この記事を書いた時には、与えた文字列が短くなるような操作には対応していなかったのだが、最新バージョンでは対応するようになっている。ただし、最低でも2バイトは残ってくれないと駄目だ。

短縮するために Text::ANSI::Fold モジュールを使用したので、perl v5.14 以上の対応となった。

Text::VisualPrintf::IO

Text::VisualPrintf::IO は、import パラメータを受け取るように変更した。printfvprintf を指定することができる。

2020.10 追記

その後のアップデートで、1バイトの短縮に対応し、2020年9月にリリースした 3.06 からは、0バイトへの短縮、つまり対応する要素が消滅してしまう場合にも対応している。

4
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
4
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?