LaTeX 単語数,文字数集計プログラム

論文の投稿規定には一般に文字数(和文),語数(欧文)の制限が付く。自分の論文についてこれらを集計したいと思い,インターネットでツールを探索してみたが以外と少ない。あっても LaTeX で書いた論文を対象にすることは難しそうであった。考えてみれば,文字数,単語数とひとことで言ってもそのカウント基準は千差万別であり,汎用的なものを用意するのは現実的でないかも知れない。

Microsoft Word などのワードプロセッサは編集中文書の文字数を表示する機能を備えているけれども,数字や記号などどのレベルで集計しているのか,それをコントロールできるのか,その仕様がよくわからない。また,TeX 原稿テキストだとこの方法は意味がない。

そこで自作することに。とはいっても,大半は UNIX のテキストユーティリティを使い回すのが主体である,日本語対応 dvi2tty で dvi ファイルから抽出したテキストをネタにするところがポイントである。自前で書いたプログラムは,Perl 言語による簡単な単語・文字カウンタとキリル文字変換フィルタだけである。

LaTeX 原稿はノンブルや行番号などを出力するマークアップをしていたり,ロシア語などは dvi 上は必ずしも英字で埋め込まれるわけではないので,dvi2tty の出力テキストはそのままでは正確な単語数を把握するのに不都合がある。そのため事前に原稿を sed で書き換えて救ってやる必要があった。

参考までに単語・文字カウンタを掲載しておく。欧文は単語数,和文は文字数を集計する。UTF-8 テキストを前提としている。ちょっと乱暴だけど,Unicode コードポイント U+3000 以上の文字を和文として扱う仕様である。数字を単語として扱いたくない,などの必要があれば,事前に sed で数字を置換しておくなどの工夫をしておく。このプログラムそのものは LaTeX 専用というわけではない。

#!/usr/bin/perl
#  -*- coding: utf-8; mode: cperl; -*-
#
#  artclcount: 論文の文字数と単語数を計算する
#
#             2007 (c), isao yasuda, All Rights Reserved.
#
#  USAGE
#  -----
#  artclcount [-p] < text
#    -p: 欧文句読点,括弧類 .:;,!?<>()[]'"`~_ をカウントしない。
#
#  CAUTION
#  -------
#  LaTeX dvi出力を処理する場合は,ノンブル,ヘッダ,注セパレータなども
#  すべて対象となる。これを外すには事前にページスタイル調整,sed 
#  による置換を行っておくこと。
#
use utf8;           # UTF-8 hundling
use Getopt::Std;    # コマンドライン引数処理
use File::Basename; # ファイル名の取得
binmode STDIN,  ":utf8";
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8";
%opts = ('p' => 0); # p option: not include punctuations.
Getopt::Std::getopts('p', \%opts) || usage();
my($kcharsc,$charsc,$wordc,$numberc,$nonumberc,$charscall,$lno);
my($buffer1,$buffer2,$chr,$word,$pattern,$opattern);
$kcharsc=$charsc=$wordc=$numberc=$lno=0;
$pattern='[\.:;,!\?\<\>\(\)\[\]\'\"\`\~\_\s+]';
# 文字としてカウントしない記号のパターン
if ($opts{'p'}) {
    $opattern='[\.:;,!\?\<\>\(\)\[\]\'\"\`\~\_\s+]';
} else {
    $opattern='\s+';
}
while (<STDIN>) {
    # UTF-8 デコード,行末改行除去
    utf8::decode($_);
    $buffer1 = $_;
    chomp($buffer1);
    $buffer2 = "";
    # 漢字文字数カウント
    foreach $chr (split(//, $buffer1)) {
        if ($chr ge "\x{3000}") {
            $kcharsc++;
            $buffer2 .= " ";
        } else {
            $buffer2 .= $chr;
        }
    }
    # 欧文単語カウント(記号 .,:;!? は対象外)
    foreach $word (split(/$pattern/, $buffer2)) {
        if ($word) {
            $wordc++;
            # 数字要素カウント
            if ($word =~ /[0-9]+/) {
              $numberc++;
            }
        }
    }
    foreach $chr (split(//, $buffer2)) {
        $charsc++ unless ($chr =~ /$opattern/g);
    }
    $lno++;
}
$nonumberc = $wordc - $numberc;
$charscall = $kcharsc + $charsc;
print "** Words: $wordc\n";
print "**  - Ordinary: $nonumberc\n";
print "**  - Number:   $numberc\n";
print "** Chars: $charscall\n";
print "**  - Kanji:    $kcharsc\t[with codepoint >= U+3000]\n";
print "**  - Other:    $charsc\t[Ascii, Russian, etc.";
if ($opts{'p'}) {
    print "; not counting .,:;!?<>()[]\'\"`~]\n";
} else {
    print "]\n";
}
printf STDERR "Words/Characters counter processed $lno lines.\n";
# usage
sub usage {
    my($prog) = basename($0);
    die <<"EOM";
Usage: $prog \<-p\>
 -p  not count puctuations and signs .,:;!?\<\>\(\)\[\]\'\"\`\~
 * 2006-2007 (c), isao yasuda, All Rights Reserved.
 * absolutely no warranty.
EOM
}

簡単に使い方を記しておく。上記プログラムを artclcount という名で格納するとする。以下の説明では「対象文書」はカウント処理用に複製した TeX 原稿を指している。

  1. 対象文書 (EUC) に対し,\pagestyle{empty} を指定する。これはノンブルなど余計な情報をカウント対象から外すためである。
  2. Babel パッケージで外国語を記述している場合,対象文書に対し,\selectlanguage 命令の直後に \language=255 を指定することにより,ハイフネーション処理がなされないようにしておく。分綴されると一単語が二語と扱われるためである。カウント処理を想定して,予めもとの TeX 原稿にこれらの切替をコメントとして書いておき,sed で一括変換するのがよいと思う。例えば原稿は次のようなマクロで言語切替を行うようにしておき,カウント処理用対象文書でコメントを外すようにすればよい。
    \newcommand{\rutxt}[1]{%
      \begingroup\selectlanguage{russian}%
    % ハイフネーション抑止
    % カウント処理の前に次行をコメント解除する。
    %\language=255%
      #1\endgroup\relax}%
  3. ロシア語単語数を計算する必要がある場合,できるだけ T2A エンコーディングで組むとよい。OT2 だと dvi 抽出テキストが空白で出力される文字があり,計算上取り扱いが面倒になる。OT2 原稿ならば対象文書の文字 ёжйъьэюя (大文字,小文字とも) を c (合字として扱われることがない) に変換しておく。
  4. LaTeX で対象文書をコンパイルする。このとき生成される dvi ファイル名を target.dvi とする
  5. dvi2tty (日本語対応版) で target.dvi からテキストを抽出し,count.txt に出力する。このとき,dvi2tty オプションに -w132 -l (一行132文字整形,改頁^Lマーキング) を指定しておくとよいと思う。
    % dvi2tty -w132 -l -o count.txt target.dvi
  6. count.txt を UTF-8 にコード変換し,GNU sed で傍点,脚注セパレータ,改頁^Lマーキングを削除し,その結果を count-sed.txt に出力する。このあたりのテキスト整形は count.txt を眺めて決める。
    nkf -w count.txt |\
    sed -e 's/ \. //g' \
        -e 's/___*//g' \
        -e 's/\^L$//g' > count-sed.txt
  7. 本カウントプログラムで単語数,文字数を計算する。
    % artclcount < count-sed.txt
    ** Words: 2516 .. 全単語数 (欧文)
    **  - Ordinary: 1917 .. 通常単語数 (欧文)
    **  - Number:   599 .. 数値含む単語数
    ** Chars: 25762 .. 全文字数
    **  - Kanji:    14360 [with codepoint >= U+3000] .. 漢字
    **  - Other:    11402 [Ascii, Russian, etc.] .. 欧文