2.1.4. Стандартный ввод-вывод
2.1.4. Стандартный ввод-вывод
В стандартной библиотеке языка С определены готовые потоки ввода и вывода (stdin и stdout соответственно). Они используются функциями scanf(), printf() и целым рядом других библиотечных функций. Согласно идеологии UNIX, стандартные потоки можно перенаправлять. Это позволяет образовывать цепочки программ, связанных посредством каналов (конкретный синтаксис перенаправления потоков и работы с каналами можно узнать в документации к интерпретатору команд).
Есть также стандартный поток ошибок: stderr. Программы должны направлять предупреждающие сообщения и сообщения об ошибках в него, а не в поток stdout. Это позволяет отделять обычные выводные данные от разного рода служебных сообщений. К примеру, стандартный поток вывода можно направить в файл, а сообщения об ошибках, по-прежнему отображать на консоли. Запись в поток stderr осуществляется с помощью функции fprintf():
fprintf(stderr, ("Error: ..."));
Все три стандартных потока доступны низкоуровневым функциям ввода-вывода (read(), write() и т.д.) через дескрипторы файлов. В частности, поток stdin имеет дескриптор 0, stdout — 1, a stderr — 2.
При вызове программы иногда требуется одновременно перенаправить потоки вывода и ошибок в файл или канал. Синтаксис подобной операции зависит от используемого интерпретатора команд. В интерпретаторах семейства Bourne shell (включая bash, который по умолчанию установлен в большинстве дистрибутивов Linux) это делается так:
% program > output_file.txt 2>&1
% program 2>&1 | filter
Запись 2>&1 означает, что файл с дескриптором 2 (stderr) объединяется с файле имеющим дескриптор 1 (stdout). Обратите внимание на то, что эта запись должна идти после операции перенаправления в файл (первый пример), но перед операцией перенаправления в канал (второй пример).
Поток stdout является буферизуемым. Записываемые в него данные не посылаются на консоль (или на другое устройство в случае перенаправления), пока буфер не заполнится, программа не завершит работу нормальным способом или файл stdout не будет закрыт. Осуществить принудительное "выталкивание" буфера позволяет функция fflush():
fflush(stdout);
В то же время поток stderr не буферизуется. Записываемые в него данные сразу попадают на консоль.[6]
Указанная особенность потока stdout может приводить к неожиданным результатам. Например, в следующем цикле точка не выводится каждую секунду. Вместо этого все символы сначала помещаются в буфер, а затем целая их группа одновременно выводится на экран, когда буфер оказывается заполненным.
while (1) {
printf(".");
sleep(1);
}
А в этом цикле происходит то, что нам нужно:
while (1) {
fprintf(stderr, ".");
sleep(1);
}