マルチバイト文字列を考慮した wc を Perl で書く

  • 2
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

wc コマンドは通常1バイト1文字として文字数を数えます。

最近の wc だと wc -m でマルチバイト文字を考慮することができるようですが、もっと自由にわかりやすく自分なりにカスタマイズできたらいいなとも思います。

Qiita で検索すると人口が多いプログラミング言語の書き方が出てきますが、ここでは Perl で書いてみたいと思います。

Perl で書くマルチバイト版 wcmbwc

説明するよりまず実例を書いてみます。わかりやすく必要最小限で。

mbwc
#!/usr/bin/perl

use strict;
use warnings;

use Encode;

my $buffer = do { local $/; <>; };

my $strings = decode("utf-8", $buffer);

my $lines = $strings =~ s/\n/\n/g || 1;
my $chars = length($strings);
my $bytes = length($buffer);

print "lines: $lines\n";
print "chars: $chars\n";
print "bytes: $bytes\n";

readline である <> が標準入力であるか第1引数にファイルパス指定をしているのかを吸収してくれるので楽です。

Encode モジュールを使って、decode 関数で UTF-8 バイト列を UTF-8 文字列にしてから文字数を数える length で数えています。バイト列を length に与えればバイト数を数えます。

Perl で文字の数を数える方法はいくつもありますが、グローバル置換 s///g が左辺に置換回数を返すことを利用して、改行文字を同じ改行文字に置換する回数を数えることで行数を数えています。改行の数を数える実装は wc でも同様のようです。

さらに機能追加をする指針

  • wc にあった単語を数える方法
    • 日本語に限定しても、英語のように空白で句切られたものを数えるようにはいきません。MeCab などの形態素解析エンジンを使って単語数を数えるとよさそうです
    • 日本語以外の任意の自然言語を対象とするのは相当骨が折れることでしょう(そういうライブラリはあるのかな)
  • UTF-8 以外の文字コード
    • decode 関数には多くの文字コードを与えることができるので、これをコマンドライン引数で取得して…というのが良さそうです(コマンドライン引数は Getopt::Long モジュールなどで簡単に扱えます)
    • 文字コードの自動判定は難しい題材なので、Perl であれば perldoc Encode::Guess などしてみるとよいでしょう。
  • コマンドライン引数に複数のファイルを指定する
    • 上記の適当丸呑みバージョンでは複数のファイルをコマンドライン引数に指定する方法にはうまく対応できていません。この場合は自前で for などで回してわかりやすく表示することになるでしょう