Книга: UNIX — универсальная среда программирования
6.1 Стандартные входной и выходной потоки: программа vis
Разделы на этой странице:
6.1 Стандартные входной и выходной потоки: программа vis
Многие программы читают только из одного входного потока и пишут в один выходной поток: для таких программ полностью подходят функции ввода-вывода, использующие лишь стандартные входной и выходной потоки, и для того чтобы начать работу, этого почти всегда достаточно.
Проиллюстрируем изложенное с помощью программы vis
, которая копирует свой стандартный входной поток в стандартный выходной, изображая при этом все непечатаемые символы в виде nnn, где nnn — восьмеричное значение символа. Vis
полезна для обнаружения "посторонних" или нежелательных символов, которые могут попасть в файлы. Например, vis
будет печатать каждый символ "шаг назад" как 10, что является его восьмеричным значением:
$ cat x abc
$ vis < x
abc101010 ___
$
Чтобы просмотреть несколько файлов с помощью этой элементарной версии vis
, вы можете использовать cat
для сбора файлов
$ cat файл1 файл2 ... | vis
...
$ cat файл1 файл2 ... | vis | grep ''
...
и избежать тем самым выяснения способа доступа к файлам из программы.
Между прочим, может показаться, что подобную работу следует выполнить с привлечением sed
, поскольку команда '1'
выдает на экран непечатаемые символы в наглядном виде:
$ sed -n 1 x
abc???___
$
Результат выполнения программы sed
, вероятно, вам покажется яснее, чем результат выполнения vis
. Но применение sed
к нетекстовым файлам бессмысленно:
$ sed -n 1 /usr/you/bin
Ничего в ответ!
$
(Так получилось на PDP-11; в одной из систем для VAX sed
аварийно завершилась, возможно, потому, что ввод был воспринят как очень длинная текстовая строка.) Таким образом, sed
нам не подходит, и мы вынуждены писать новую программу.
Простейшие функции ввода и вывода getchar
и putchar
. При каждом вызове getchar
появляется очередной символ из стандартного входного потока, которому может быть поставлен в соответствие файл, конвейер или терминал (последнее принимается по умолчанию). Программа "не знает", что конкретно он собой представляет. Аналогично putchar(c)
помещает символ в стандартный выходной поток, который по умолчанию также связан с терминалом.
Функция printf(3)
выполняет форматное преобразование при выводе. Вызовы printf
и putchar
могут следовать в любом порядке; выходной поток отразит порядок этих вызовов. Для форматного преобразования входного потока предусмотрена функция scanf(3)
; она читает входной поток и разбивает его, как требуется, на строки, числа и т.п. Вызовы scanf
и getchar
также могут чередоваться.
Приведем первую версию vis
:
/* vis: make funny characters visible (version 1) */
возвращает из входного потока очередной байт или значение EOF, когда встречает конец файла (или ошибку). Между прочим, EOF не является байтом из файла; вспомните: во второй главе объяснялось, что такое "конец файла". Значение EOF отличается от значения любого байта, поэтому его трудно спутать с реальными данными; переменная с описана как
#include <stdio.h>
#include <ctype.h>
main() {
int c;
while ((c = getchar()) != EOF)
if (isascii(c) &&
(isprint(с) || c=='n' || c=='t' || c==' '))
putchar(c);
else
printf("%03o", c);
exit(0);
}
Getcharint
(целая), а; не как char
(символьная), так что она может хранить значение EOF. Строка
#include <stdio.h>
должна находиться в начале каждого исходного файла. Это заставляет компилятор Си читать файл макроопределений (/usr/include/stdio.h
), в котором специфицированы стандартные функции и имена, в том числе и EOF
. Мы будем использовать <stdio.h>
как краткую запись полного имени файла.
Файл <ctype.h>
— еще один файл макроопределений в /usr/include
, который задает машинно-независимые макрокоманды (макросы) для классификации символов. Чтобы выяснить, принадлежит ли входной символ набору ASCII (т.е. его значение меньше 0200) и печатается ли он, мы использовали здесь isascii
и isprint
. Остальные макросы перечислены в табл. 6.1. Отметим, что <ctype.h>
определяет символы "перевод строки", "табуляция" и пробел как непечатаемые.
isalpha(c) |
Буква принадлежит алфавиту: a-z A-Z |
isupper(c) |
Прописная буква: A-Z |
islower(с) |
Строчная буква: a-z |
isdigit(c) |
Цифра: 0-9 |
isxdigit(c) |
Шестнадцатеричная цифра: 0-9 a-f A-F |
isalnum(c) |
Буква или цифра |
isspace(c) |
Пробел, символ табуляции, символ перевода строки, символ вертикальной табуляции, символ перевода страницы, символ возврата |
ispunct(c) |
Не буквенно-цифровой символ, не управляющий, не пробел |
isprint(c) |
Печатаемый: любой графический символ |
iscntrl(c) |
Управляющий символ: 0 <= с < 040 || с == 0177 |
isascii(c) |
Символ ASCII: 0 <= с <= 0177 |
Таблица 6.1: Макросы классификации символов <ctype.h>
Вызов exit
в конце vis
не является необходимым для корректной работы программы, но гарантирует тому, кто эту программу вызвал, получение нормального кода ее завершения (обычно нуля). Другой способ возврата кода завершения выполнить в теле функции main
оператор return 0
; возвращаемое значение main
и есть код завершения программы. Если нет явно указанных return
или exit
, код завершения не определен.
Для компиляции программы на Си поместите исходный текст в файл, имя которого оканчивается на .с
, например vis.с
, оттранслируйте его с помощью сс
и запустите на выполнение результат, оставляемый компилятором в файле с именем a.out
('а' — ассемблер):
$ сс vis.с
можно переименовать после первого запуска или сделать это сразу с помощью флага
$ a.out
hello worldctl^g
hello world07
ctl-d
$
a.out-о
команды сс
:
$ сс -о vis vis.с
Результат в vis, а не в a.out
Упражнение 6.1
Мы решили, что символы табуляции не следует делать видимыми, изображая их как 11
, ?
или t
, поскольку главное назначение vis
— поиск действительно аномальных символов. Можно принять альтернативное решение и недвусмысленно идентифицировать каждый символ в выходном потоке: символы табуляции, неграфические символы, пробелы в конце строки и т.п. Модифицируйте vis
так, чтобы символы табуляции, обратная дробная черта, "шаг назад", перевод страницы и др. печатались в традиционном, принятом в Си представлении: t
, , b,
f
и т.д., причем пробелы в конце строки должны быть помечены. Можете сделать это недвусмысленным образом? Сравните ваш вариант с приведенным ниже:
$ sed -n 1
Упражнение 6.2
Модифицируйте vis
так, чтобы она приводила длинные строки к строкам некоторой разумной длины. Как это согласуется с требованием недвусмысленности результата из предыдущего упражнения?
- Стандартные потоки: stdin, stdout, stdeir, stdaux, stdprn.
- 10. FUTURE REVISIONS OF THIS LICENSE
- Программа «Тайный покупатель»
- 1.2 Процесс, контекст процесса и потоки
- Программа Victoria
- Стандартные списки
- Подпрограмма обработки прерывания
- Магическая программа, или Беспорядок по расписанию
- Где найти стандартные программы Windows?
- Что делать, если вместо русских букв в программах – непонятные символы?
- Как называется программа, которая устанавливает Windows за 5 минут?
- Программа shmget