Главная » Программирование для UNIX » Стандартный ввод и вывод: vis

0

Многие  программы читают только с одного входа и пишут на один выход;  для  таких  программ вполне достаточно устройств  стандартного ввода-вывода, и этого почти всегда хватает для начала.

Рассмотрим программу vis,  которая копирует  стандартный ввод  на стандартный вывод, при этом все непечатаемые символы выводятся в виде  \nnn, где nnn – восьмеричное значение символа. Эта программа незаменима для  обнаружения странных или  нежелательных  симво лов,  попавших в файлы. Например, каждый символ возврата на одну позицию vis выведет как \010 (это восьмеричное значение символа):

$ cat  x

a b c

$ vis <x

1        Керниган Б.,  Ритчи Д. «Язык программирования  Си»,  3-е  издание, Невский Диалект, 2002.

abc\010\010\010     

$

Для  того чтобы  сканировать несколько файлов при помощи этой руди ментарной версии vis, объедините файлы командой cat:

$ cat  file1 file2 … | vis

$ cat  file1 file2 … | vis | grep  ‘\\’

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

Кстати, может показаться, что эту задачу решит и sed, ведь команда l

выводит непечатаемые символы в понятном виде:

$ sed  -n  l x

abc777    

$

Вероятно, вывод sed выглядит даже понятнее, чем вывод vis. Но дело в том, что sed предназначена только для обработки текстовых файлов:

$ sed  -n  l /usr/you/bin

$                                       Абсолютно ничего!

(Так было на PDP-11; что касается системы на VAX, там выполнение sed прерывалось, видимо из-за того,  что входные данные интерпретировались как слишком длинная текстовая строка). Так что  sed использовать нельзя, поэтому направим свои усилия на создание новой программы.

Самыми простыми подпрограммами ввода  и вывода являются getchar и putchar. Каждый вызов getchar извлекает следующий символ из стан дартного ввода, которым может быть  файл, конвейер или  терминал (по  умолчанию) – программа этого  не  знает. Аналогичным  образом putchar(c) помещает символ c в стандартный вывод, по умолчанию это также терминал.

Функция printf(3) осуществляет преобразование формата вывода. Порядок вызова printf и putchar  произволен; выходные данные будут  появляться в порядке обращения к подпрограммам. Есть  и соответствующая функция scanf(3) для  преобразования формата входных данных; она считывает ввод и разбивает его на строки, числа и т. д.,  как указано. Порядок вызова scanf  и getchar также произволен.

Приведем первую версию vis:

/*  vis:  сделать  видимыми  странные символы  (версия  1)  */

#include <stdio.h>

#include <ctype.h>

main()

{

int c;

while  ((c  =  getchar())  !=  EOF) if  (isascii(c)  &&

(isprint(c) ||  c==’\n’ || c==’\t’  ||  c==’  ‘)) putchar(c);

else

printf("\\%03o",  c); exit(0);

}

Функция getchar возвращает следующий байт ввода  или  значение EOF, если достигнут конец файла (или  ошибку). Вспомните, что EOF  – это не байт  файла (см.  главу 2).  Значение EOF гарантированно отличается от любого  значения, которое может содержать отдельный байт, поэтому его  можно отличить от настоящих  данных; переменная c объявлена как int, а не char, так что она сможет содержать значение EOF. Строка

#include <stdio.h>

должна присутствовать в начале каждого исходного файла. Она заставляет компилятор  Си читать заголовочный файл  (/usr/include/stdio.h) стандартных функций и  символов, который содержит описание EOF. Далее  в  тексте  будет   встречаться краткая  запись имени файла –

<stdio.h>.

Файл <ctype.h> – это еще один заголовочный файл, который описывает  машинонезависимые  тесты, определяющие  свойства символов. В первой версии vis использованы isascii и isprint, – они определяют, принадлежит ли  введенный  символ набору  ASCII  (значение меньше

0200) и является ли  он печатаемым; остальные  тесты  перечислены в табл. 6.1. Обратите внимание на то, что символ новой  строки, знак табуляции  и  пробел   не   являются   «печатаемыми»  по  определениям

<ctype.h>.

Таблица 6.1. Макросы проверки символов <ctype.h>

Имя                      Что проверяет isalpha(c)           буква: a–z  A–Z isupper(c)           верхний регистр: A–Z islower(c)           нижний регистр: a–z isdigit(c)           цифра: 0–9

isxdigit(c)          шестнадцатеричная цифра: 0–9  a–f  A–F isalnum(c)           буква или цифра

isspace(c)          пробел, табуляция, символ новой строки, вертикальная табуляция, перевод страницы, возврат каретки

ispunct(c)         не  алфавитно-цифровой,  не  управляющий и  не  пробельный символ


Имя

Что проверяет

isprint(c) iscntrl(c) isascii(c)

печатаемый символ: любой графический управляющие символы: 0  <=  c  <  040  ||  c  ==  0177

ASCII-символ: 0 <= c  <= 0177

Вызов  exit в конце vis не обязателен для корректного исполнения программы, но благодаря ему любой пользователь, запустивший vis, увидит  код  нормального  завершения (обычно  ноль)  по завершении программы. Есть и другой способ получить код завершения – выход из ma– in посредством return 0; тогда  кодом  завершения программы является величина, возвращаемая main. Если  return или exit не присутствуют в программе явно, то код завершения предсказать невозможно.

Чтобы откомпилировать программу, написанную на Си, поместите исходный текст  в  файл с  именем, оканчивающимся на  .c,  например vis.c,  скомпилируйте ее при  помощи  cc  –  компилятор поместит результат в файл a.out (a означает ассемблер), теперь запустите его:

$ cc  vis.c

$ a.out

hello  worldctlg hello  world\007 ctld

$

Можно переименовать a.out, когда он заработает, а можно указать параметр –o команды cc:

$ cc  -o  vis vis.c               Вывод в  vis,  а не в  a.out

Упражнение 6.1.  Было решено, что знаки табуляции трогать не надо (то есть не представлять их как \011 или  >, или  же \t), так как главной задачей программы vis  является  поиск по-настоящему аномальных символов. Можно предложить и другую модель – однозначно идентифицировать каждый символ ввода: знаки табуляции, пробелы в конце строки, неграфические символы и т. д.  Измените vis  так, чтобы  для  таких символов, как табуляция, обратная косая черта, возврат на одну позицию, разрыв страницы и т. д., выводилось их  условное Си-пред ставление: \t, \\, \b, \f и т. д.,  а пробелы в конце строк помечались. Можно ли  сделать это однозначно? Сравните то,  что получится, с командой sed,

$ sed  -n  l

~

Упражнение 6.2.  Измените vis  так, чтобы   она  свертывала длинные строки до некоторой разумной длины. Как  это повлияет на однозначность, упомянутую в предыдущем упражнении? ~

Источник: Керниган Б., Пайк Р., UNIX. Программное окружение. – Пер. с англ. – СПб: Символ-Плюс, 2003. – 416 с., ил.

По теме:

  • Комментарии