wc
コマンドは通常1バイト1文字として文字数を数えます。
最近の wc
だと wc -m
でマルチバイト文字を考慮することができるようですが、もっと自由にわかりやすく自分なりにカスタマイズできたらいいなとも思います。
Qiita で検索すると人口が多いプログラミング言語の書き方が出てきますが、ここでは Perl で書いてみたいと思います。
Perl で書くマルチバイト版 wc
の mbwc
説明するよりまず実例を書いてみます。わかりやすく必要最小限で。
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
などで回してわかりやすく表示することになるでしょう
- 上記の適当丸呑みバージョンでは複数のファイルをコマンドライン引数に指定する方法にはうまく対応できていません。この場合は自前で