Большинство функций буферизованного ввода-вывода предназначено для выполнения операций чтения или записи данных в открытых потоках без их форматных преобразований. Эти функции обеспечивают обработку в потоке для отдельных символов, строк символов и структур данных произвольных абстрактных типов.
Посимвольное чтение данных из потока в наиболее универсальном варианте реализует функция fgetc. Спецификация формат ее вызова имеет вид:
int fgetc(FILE* stream);
При каждом обращении функция fgetc возвращает код очередного символа из потока ввода, который адресует аргумент stream. Когда достигнут конец потока или при обнаружении ошибки чтения, функция fgetc возвращает значение константы EOF. Следует отметить, что возврат функции fgetc не позволяет различать состояния ошибки и конца потока ввода также как и возврат других функций типа get, которые рассмотрены ниже.
Вместо функции fgetc можно использовать макрос getc, эквивалентный по типу аргумента и кода возврата. Его аргумент не должен быть выражением с побочными эффектами. Для чтения символов из потока стандартного ввода предусмотрен макрос getchar, который не имеет аргументов и эквивалентен вызову функции getc с аргументом stdin.
Если хотя бы один символ был прочитан, можно вернуть произвольное число символов обратно в поток ввода, используя функцию ungetc. Спецификация формата ее вызова имеет вид:
int ungetc(int sym, FILE* stream);
Аргумент sym обозначает код символа, который необходимо вернуть в поток ввода, специфицированный указателем stream. Возвращенные символы могут быть прочитаны последовательными операциями ввода в порядке, обратном их возврату в поток. При успешном завершении функция ungetc возвращает код символа, который был записан обратно в поток ввода. В случае ошибки возвращается значение константы EOF. Использование функции ungetc поясняет исходный код, оформленный в виде прикладной процедуры skipspaces, которая должна обеспечивать пропуск начальных пробелов потока стандартного ввода:
/* Пропуск начальных пробелов потока ввода */
 void skipspaces() {
 int c;   /* код символа потока */
/* Чтение начальных пробелов потока */
while((c = getchar()) != EOF)
  if(c != ' ')
    break;
/* Возврат в поток первого непробела */
if(c != EOF)
  ungetc(c, stdin);
} /* skipspaces */
Наиболее универсальный вариант записи отдельных символов в поток вывода предоставляет функция fputc. Спецификация формата ее вызова имеет вид:
int fputc(int sym, FILE* stream);
Каждый вызов функции fputc обеспечивает запись кода символа, который определяет аргумент sym, в поток, специфицированный аргументом stream. При успехе записи функция fputc возвращает код записанного символа. Код возврата функции fputc при ошибке записи определяет значение константы EOF.
Вместо функции fputc можно использовать макрос putc, эквивалентный по типу аргументов и кода возврата. Его аргументы не должны быть заданы с помощью выражений, которые имеют побочный эффект. Для записи символов в поток стандартного вывода предусмотрен макрос putchar. В спецификации его вызова предусмотрен только один аргумент, через который передается код записываемого символа. Обращение к макросу putchar эквивалентно вызову putc, в котором указатель потока (stream) имеет значение stdout.
Использование функций ввода-вывода символов иллюстрирует исходный код прикладной программы-фильтра, которая реализует побайтное копирование потока стандартного ввода в поток стандартного вывода, исключая коды возврата каретки '\r':
/* Фильтр возврата коретки */
#include <stdio.h>
int main() {
int c;              /* код символа потока */
while((c = getchar()) != EOF)
  if(c != '\r')
    putchar(c);
return(0);
} /* main */
Эта программа-фильтр может быть полезна для преобразования текстовых файлов в формате MS-DOS, где каждую строку принято завершать кодовой парой \r\n, в формат OS UNIX, где строку завершает только символ перевода строки с кодом '\n', а возврат каретки гарантируют установки терминала.
Чтение строк символов из файловых потоков, которые открыты для обработки текстовых файлов, реализует функция fgets. Спецификация формата ее вызова имеет вид:
char* fgets(char* string, int len, FILE* stream);
Каждое обращение к функции fgets позволяет получить из потока, который специфицирует указатель stream, не более, чем (len — 1) символов. Ввод прекращается, если прочитан символ перевода строки или достигнут конец потока. Полученные символы, включая символ перевода строки, если он был прочитан, передаются в массив, который адресует аргумент string функции fgets. Набор символов в массиве автоматически дополняет символ, который имеет нулевой код '\0'. Поэтому для корректного сохранения прочитанных данных необходимо распределить символьный массив, длиной не меньше, чем задает аргумент len, чтобы избежать ошибки нарушения границ сегментов адресного пространства пространства процесса выполнения программы.
Чтение строк символов из потока стандартного ввода обеспечивает функция gets. Спецификация формата ее вызова имеет:
char* gets(char* string);
Каждое обращение к функции gets считывает из потока стандартного ввода все символы до символа перевода строки включительно. Полученные данные сохраняет символьный массив, который адресует аргумент string. Символ перевода строки в массиве автоматически заменяет нулевой код ('\0'). Размер массива не должен быть выбран не меньше, чем длина строки потока стандартного ввода.
При успешном завершении операции чтения строки, функции fgets и gets возращают адрес символьного массива string, в котором сохранены полученные данные. Если достигнут конец потока ввода и ни один символ не был прочитан, возвращается нулевой указатель NULL, а содержимое массива полученных данных по адресу string остается неизменным.
Запись строки символов в файловый поток обеспечивает функция fputs. Спецификация формата ее вызова имеет вид:
int fputs(const char* string, FILE* stream);
Аргумент string адресует строку символов, которую необходимо записать в поток вывода, специфицированный указателем stream. Завершающий строку нулевой код '\0' не будет записан в поток.
Для записи строки в поток стандартного вывода следует применять функцию puts. Спецификация формата ее вызова имеет вид:
int puts(const char* string);
Аргумент string адресует строку символов, которую необходимо записать в поток стандартного вывода. Завершающий строку символов нулевой код '\0' при записи в поток стандартного вывода автоматически заменяется на символ перевода строки.
При успешном завершении операции записи строки символов функции fputs и puts возвращают неотрицательное целочисленное значение. В случае ошибки их код возврата равен значению константы EOF. Анализ кода возврата позволяет контролировать результат выполнения операции записи строки в поток вывода. Следует отметить, что при разработке практических приложений контроль записи редко бывает необходим, особенно, при обработке потока стандартного вывода.
Использование функций ввода-вывода потоков строк символов иллюстрирует исходный код упрощенной функциональной реализации прикладной команды fold из OS Linux, которая "изгибает" длинные текстовые строки потока ввода для вывода на стандартный экран терминала без потери информации. Ширина экрана в стандартном консольном режиме равна 80 позиций, поэтому длина строки вывода не может превышать 80 байт, а остаток более длинной входной строки должен быть перенесен в следующий ряд экранных позиций. Короткие строки должны отображаться без изменений. Указанный принцип обработки длинных строк обеспечивает последовательный вызов функций fgets и puts в сочетании с анализом длины каждой полученной строки потока ввода, как показывает исходный код прикладной процедуры fwrap, приведенный ниже.
/* Ограничитель экранного вывода длинных строк */
void fwrap(FILE* input) {
char buf[80];           /* ряд позиций строки потока ввод */
int i;                  /* индекс ряда строки ввода */
while(fgets(buf, 80, input) != NULL) {
  for(i = 0; buf[i] != '\0'; i++);
  if(buf[--i] == '\n')
    buf[i] = '\0';
  puts(row);
} /* while */
} /* fwrap */
Для чтения из потока ввода структур данных произвольных типов следует использовать функцию fread. Спецификация формата ее вызова имеет вид:
int fread(void* ptr, int size, int num, FILE* stream);
Каждое обращение к функции fread считывает из потока ввода, заданного указателем stream, не более, чем num элементов данных любого базового или абстрактного типа. Размер каждого элемента данных задает аргумент size. Для сохранения полученных данных следует предусмотреть соответствующий объем памяти, который адресует указатель ptr. Функция fread возвращает число реально прочитанных элементов данных, которое может быть меньше значения аргумента num только в случае ошибки или при достижении конца потока ввода.
Для записи в поток вывода структур данных произвольных типов следует использовать функцию fwrite. Спецификация формата ее вызова имеет вид:
int fwrite(const void* ptr, int size, int num, FILE* stream);
Каждое обращение к функции fwrite записывает в поток вывода, заданный указателем stream, не более, чем num элементов данных любого базового или абстрактного типа. Размер каждого элемента данных задает аргумент size. Указатель ptr адресует информационные структуры данных программы, которые требуется передать в поток вывода. Функция fwrite возвращает число реально записанных элементов данных, которое может быть меньше значения аргумента num только в случае ошибки.
Использование функций fread и fwrite для копирования содержимого файлов по блокам, размером BUFSIZ байтов, иллюстрирует следующий исходный код прикладной процедуры blockcopy:
/* Копирование файлов по блокам */
void blockcopy(FILE* input, FILE* output) {
unsigned char buf[BUFSIZ]; /* буфер копирования блока  */
int size;                  /* зффективный размер блока */
/* Цикл копирования */
while((size = fread((void* ) buf, BUFSIZ, 1, input) > 0)
  fwrite((const void* ) buf, size, 1, output);
} /* blockcopy */
В некоторых случаях при записи данных необходимо гарантировать передачу содержимого буфера потока в файл или на терминал до заполнения буфера. Чтобы принудительно вытолкнуть информацию из буфера потока применяется функция fflush. Спецификация формата ее вызова имеет вид:
int fflush(FILE* stream);
Обычно, единственный аргумент stream функции fflush обозначает указатель потока вывода, в который требуется передать все буферизованные, но еще незаписанные данные. Если аргумент stream специфицирует поток ввода, то вызов функции fflush приводит к уничтожению всех буферизованных, но еще непрочитанных данных. В обоих случаях, при успешном завершении функция fflush возвращает нулевой код, а в случае ошибки значение EOF. Ошибка имеет место, если поток, специфицированный указателем stream, не связан ни с каким файлом.
Использование функции fflush поясняет исходный код прикладной процедуры beep, любой вызов которой гарантирует синхронный звуковой сигнал через стандартный вывод символа с кодом 7.
/* Звуковой сигнал */
void beep() {
putchar('\007');  /* стандартный вывод звукового символа */
fflush(stdout);   /* очистка буфера стандартного вывода */
} /* beep */