行数を数える #5
前項でストリーム中の行数を数える関数としては完成していると思う。
しかし問題がある。
Windowsのように'\r'と'\n'の連接(以下CRLF)を一個の改行とする環境において、
CRLFを読み込んだ後に一個の'\n'としてgetcharやgetcが返す問題である。
UNIX系にはないいわゆるテキストモードは実体がCRLFであっても'\n'として扱える便利さがあるが、
複数の種類の改行が混在したときにこの変換が問題を引き起こす。
'\r', '\r', '\n'
という入力があった場合、以前述べた改行の定義に従えば2個の改行、すなわち行数は2行である。
ところがCRLFが'\n'に変換されてしまうために、この行数は1行として解釈されてしまう。
これはカウントする側では対応しきれないため、
もし、テキストモードが存在する環境において複数種類の改行が混在する可能性があるのなら、
ストリームを開くときにバイナリモードで開くことでこの問題を回避できる。
標準入力の場合は最初から、それも多分テキストモードで開かれているため、
Cにおいては非標準な関数setmodeでモードを変更する手がある。
#include <stdio.h> #include <fcntl.h> #include <io.h> ...snip int main(void) { setmode(fileno(stdin), O_BINARY); printf("%lu\n", count_eol(stdin)); return 0; }
非標準関数なのでこれをコンパイルする場合にC言語の規格の標準を厳密適用すると警告かエラーになると思う。
この問題は複数の種類の改行が混在しないデータでは起こらないし、
UNIX系ではそもそもテキストモード/バイナリモードという区別自体がない。
CRLFが'\n'に変換されるのならカウントする側で'\r'か'\n'が返された時点でカウントすればいい、とはできないのは、
どの文字も一文字として扱う直感的に普通な環境ではCRLFによる改行を2個の改行と間違えるためである。