Идеи реализации

Более или менее понятно, что такое «символ». Это буква, цифра, знак пунктуации или какой-нибудь специальный значок (вроде пробела, знака конца строки).

Самое время теперь договориться, что мы будем понимать под терминами «строка» и «слово».

Традиционным признаком строки является специальный символ конца строки. Так что файл можно считать разделённым на строки, если в нём присутствуют такие символы. Таким образом, подсчёт строк сводится к подсчёту символов конца строки.

[Примечание]Примечание

Тут имеется тонкость. Если вдруг последний символ в файле не является символом конца строки, считать ли такую «неполноценную», незавершённую строку? Традиция говорит: нет. Уважающие себя текстовые редакторы всегда вставляют завершающий символ после последней строки файла. Программы обработки текстов (например, компиляторы) жалуются, если такой символ отсутствует.

Если непустой файл претендует на звание текстового, он должен завершаться символом конца строки. Отступление от этого правила следует рассматривать как хулиганство, как вызов всем людям доброй воли.

[Примечание]Примечание

В некоторых операционных системах присутствуют иные соглашения о признаке конца строки. Например в ОС Microsoft DOS, Microsoft Windows признаком завершения строки является пара символов — «возврат каретки» и «конец строки». В Perl соответствующие литералы — \r\n.

Итак, каждая строка должна завершаться символом конца строки, поэтому подсчёт этих символов равносилен подсчёту строк.

С подсчётом слов дело обстоит сложнее.

Словом будем называть непрерывную последовательность непробельных символов. Под пробельными символами будем понимать символ пробела, табуляции, перехода на  новую строку, а также некоторые другие, которые используются в текстах крайне редко. Главное, что проверить, является ли символ в переменной $c пробельным, можно сопоставлением с регулярным выражением \s:

if($c=~m/\s/)
{
	
}

Если бы знать, что файл начинается с непробельного символа, а после каждого слова следует ровно один пробельный символ, можно было бы подсчитывать эти пробельные символы, и тогда мы получили бы количество слов. Но, во-первых, файл может начинаться с пробелов, и, кроме того, слова могут отделяться друг от друга несколькими пробелами. В этой ситуации признаком очередного слова (вернее, конца слова) является смена непробельного символа на пробельный.

Во время подсчёта символов при считывании очередного символа соответствующий счётчик безусловно увеличивался:

$chars++;

При подсчёте строк очередной символ тоже приводил к увеличению счётчика строк, но уже при условии, что новый символ является символом конца строки:

$lines++ if $c eq "\n";

В обоих этих случаях для обновления соответствующего счётчика достаточно было знать прежнее значение счётчика, а также только что считанный символ. Для обновления счётчика слов уже недостаточно знать текущее значение этого счётчика и считанный символ. Нужно знать также, какой символ был считан в последний раз, чтобы определить, произошла ли смена непробелов на пробелы.

Поступим следующим образом. Если очередной символ — непробельный, мы в честь этого события поднимем флаг (присвоим переменной $flag значение 1). Соответственно опустим флаг в противном случае, но прежде, если флаг был поднят, увеличим счётчик слов:

if($c=~m/\s/)
{
	$words++ if $flag;
	$flag=0;
}
else
{
	$flag=1;
}

Естественно, перед началом чтения файла, флаг нужно опустить. Последнее слово в файле не будет потеряно при наших подсчётах, так как непустой файл, как мы уже выяснили, должен завершаться символом конца строки, который является пробельным.

Такова стратегия обработки каждого из файлов. Перебор файлов осуществляется в цикле по элементам массива @ARGV:

for my $filename(@ARGV)
{
	
}

В теле этого цикла делается попытка открыть файл с именем $filename, а в случае неудачи выдаётся предупреждение и осуществляется переход к следующему файлу (признаком неудачи служит ложное значение, возвращённое командой open):

unless(open $fd, '<', $filename)
{
	warn "Невозможно открыть файл «$filename»: $!\n";
	next;
}

Команды, взаимодействующие с операционной системой, могут завершиться неудачей по многим разным причинам. Например, команда open может не справиться с открытием файла, указанного в командной строке, из-за отсутствия этого файла:

Или для файла запрещено чтение:

В любом случае подобные команды в случае ошибки присваивают переменной $! строку с пояснением причины ошибки.

Информатика-54© А. Н. Швец