Putc, putchar вывод литеры в файл


Содержание

putchar

Макрос putcnar() записывает символ, находящийся в младшем байте ch, в файл stdout. Функци­онально он эквивалентен putc(ch, sdout). Поскольку во время обращения к функции аргументы символьного типа приводятся к целому типу, можно использовать символьные переменные как аргументы putchar().

В случае успеха putcahar() возвращает записанный символ; в случае ошибки будет возвращен EOF. Если выходной поток открыт в двоичном режиме, то EOF является допустимым значением для ch. Это значит, что придется использовать ferror(), чтобы выяснить, действительно ли про­изошла ошибка.

Посимвольный ввод-вывод

Стандартные функции ввода fgetchar () или getchar () возвращают прочитанный символ из файла stdin (буфер ввода), который можно записать в переменную целого типа:

Поскольку значение признака конца файла EOF не должно совпадать с ASCII-кодами символов, его определяют равным –1, а переменную ch описывают как целую, а не символьную.

Файловые функции чтения fgetc (fp) или getc (fp) возвращают символ из открытого файла fp:

Стандартные функции вывода fputchar (ch) или putchar (ch) выводят символ на экран (stdout):

Функции записи в файл fputc (fp) или putc (fp) помещают символ в открытый файл fp:

Пример. Демонстрируются приемы открытия и закрытия файла, а также использование файлового и стандартного символьного ввода-вывода данных.

char *filename = «c:\\tc\\myfiles\\char.dat»; /* спецификация файла */

FILE *fp; /* указатель файла */

clrscr(); /* очистка экрана */

/* Открытие файла для записи: */

if (( fp = fopen(filename, «w»)) == NULL)

”Проверьте спецификацию файла.»);

puts («He забудьте про удвоение символа \\ .»);

return 0; /* аварийный выход из программы */

printf («Фaйл %s открыт для записи.\n”

”Введите несколько символов и нажмите Enter.», filename);

printf (» EOF:%d\n», EOF); /* вывод значения EOF */

while ((ch=getchar()) != ‘\n’) /* цикл ввода символов */

fputc (ch, fp); /* запись символа в файл */

freopen (filename, «r», fp); /* переоткрытие файла для чтения */

рuts («Вывод символов из файла»);

while ((ch=getc(fp)) != EOF) /* цикл чтения символов из файла */

putchar (ch); /* вывод символа на экран */

fclose(fp); /* закрытие файла */

printf(«\nНажмите лю6ую клавишу. «);

getch(); /* задержка экрана результатов */

return 1; /* нормальное окончание программы */

Файл c:\tc\myfiles\char.dat открыт для записи.

Введите несколько символов и нажмите Enter. EOF= -1

Вывод символов из файла:

Нажмите любую клавишу.

Построчный ввод-вывод

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Учись учиться, не учась! 10384 — | 7888 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

putc, putwc putc, putwc

Записывает символ в поток. Writes a character to a stream.

Синтаксис Syntax

Параметры Parameters

c c
Символ, который требуется записать. Character to be written.

вышестоящий stream
Указатель на структуру FILE. Pointer to FILE structure.

Возвращаемое значение Return Value

Возвращает записанный символ. Returns the character written. Чтобы указать ошибку или условие конца файла, putc и putchar возвращают EOF; putwc и Putwchar возвращают WEOF. To indicate an error or end-of-file condition, putc and putchar return EOF; putwc and putwchar return WEOF. Для всех четырех подпрограмм используйте ferror или feof для проверки наличия ошибки или конца файла. For all four routines, use ferror or feof to check for an error or end of file. Если для потокапередан пустой указатель, вызывается обработчик недопустимых параметров, как описано в разделе Проверка параметров. If passed a null pointer for stream, the invalid parameter handler is invoked, as described in Parameter Validation. Если выполнение может быть продолжено, эти функции возвращают EOF или WEOF и применяют значение « еинвал». If execution is allowed to continue, these functions return EOF or WEOF and set errno to EINVAL.

Дополнительные сведения об этих и других кодах ошибок см. в разделе _doserrno, errno, _sys_errlist и _sys_nerr. See _doserrno, errno, _sys_errlist, and _sys_nerr for more information on these, and other, error codes.

Примечания Remarks

Подпрограммы putc записывает один символ c в выходной поток в текущей позиции. The putc routine writes the single character c to the output stream at the current position. Любое целое число может быть передано в putc, но записываются только младшие 8 бит. Any integer can be passed to putc, but only the lower 8 bits are written. Подпрограммы putchar идентичны putc( c, stdout ) . The putchar routine is identical to putc( c, stdout ) . Если возникает ошибка чтения, то в каждой подпрограмме для каждого потока устанавливается индикатор ошибки. For each routine, if a read error occurs, the error indicator for the stream is set. putc и putchar похожи на fputc и _fputcharсоответственно, но реализуются как функции и как макросы (см. раздел Выбор между функциями и макросами). putc and putchar are similar to fputc and _fputchar, respectively, but are implemented both as functions and as macros (see Choosing Between Functions and Macros). putwc и putwchar — это версии putc и putcharдля расширенных символов соответственно. putwc and putwchar are wide-character versions of putc and putchar, respectively. поведение putwc и putc идентично, если поток открыт в режиме ANSI. putwc and putc behave identically if the stream is opened in ANSI mode. putc в настоящее время не поддерживает вывод в поток Юникода. putc doesn’t currently support output into a UNICODE stream.

Версии с суффиксом _nolock идентичны за исключением того, что они не защищены от помех со стороны других потоков. The versions with the _nolock suffix are identical except that they are not protected from interference by other threads. Дополнительные сведения см. в разделе _putc_nolock, _putwc_nolock. For more information, see _putc_nolock, _putwc_nolock.

Сопоставления подпрограмм обработки обычного текста Generic-Text Routine Mappings

Подпрограмма TCHAR.H TCHAR.H routine _UNICODE и _MBCS не определены _UNICODE & _MBCS not defined _MBCS определено _MBCS defined _UNICODE определено _UNICODE defined
_puttc _puttc putc putc putc putc putwc putwc

Требования Requirements

Подпрограмма Routine Обязательный заголовок Required header
putc putc
putwc putwc или or

Консоль не поддерживается в приложениях универсальная платформа Windows (UWP). The console is not supported in Universal Windows Platform (UWP) apps. Стандартные дескрипторы потока, связанные с консолью, stdin, stdoutи stderr, должны быть перенаправляться до того, как функции времени выполнения C смогут использовать их в приложениях UWP. The standard stream handles that are associated with the console, stdin, stdout, and stderr, must be redirected before C run-time functions can use them in UWP apps. Дополнительные сведения о совместимости см. в разделе Совместимость. For additional compatibility information, see Compatibility.

Глава 7. Ввод–вывод

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

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

Свойства библиотечных функций специфицированы в более чем дюжине головных файлов; вам уже встречались некоторые из них, в том числе , и . Мы не рассматриваем здесь всю библиотеку, так как нас больше интересует написание Си-программ, чем использование библиотечных функций. Стандартная библиотека подробно описана в приложении B.

7.1. Стандартный ввод–вывод

Как уже говорилось в гл. 1, библиотечные функции реализуют простую модель текстового ввода-вывода. Текстовый поток состоит из последовательности строк; каждая строка заканчивается литерой новая_строка. Если система в чем-то не следует принятой модели, библиотека сделает так, чтобы казалось, что эта модель удовлетворяется полностью. Например, пара литер — возврат_каретки и перевод_строки — при вводе могла бы быть преобразована в одну литеру новая_строка, а при выводе выполнялось бы обратное преобразование.

Простейший механизм ввода — это чтение одной литеры из стандартного ввела (обычно с клавиатуры) функцией getchar :

В качестве результата каждого своего вызова функция getchar возвращает следующую литеру ввода или, если обнаружен конец файла, EOF . Именованная константа EOF (аббревиатура от end of file — конец файла) определена в . Обычно значение EOF равно -1, но, чтобы не зависеть от конкретного значения этой константы, ссылаться на нее следует по имени ( EOF ).

Во многих системах клавиатуру можно заменить файлом, перенаправив ввод при помощи значка . Так, если программа prog использует getchar , то командная строка

предпишет программе prog читать литеры из infile , а не с клавиатуры. Переключение ввода делается так, что сама программа prog не замечает подмены; в частности, стринг « » не будет включен в аргументы командной строки argv . Переключение ввода будет также незаметным, если ввод исходит от другой программы и передается через «трубопроводный» механизм. В некоторых системах командная строка

приведет к тому, что две программы, otherprog и prog , соединятся напрямую, т.е. стандартный вывод otherprog станет стандартным вводом для prog .

используется для вывода: putchar(c) отправляет литеру c в стандартный вывод, под которым по умолчанию подразумевается экран. Функция putchar в качестве результата возвращает посланную литеру или, в случае ошибки, EOF . То же и в отношении вывода: при помощи записи вида >имя_файла вывод можно перенаправить в файл. Например, если prog использует для вывода функцию putchar , то

будет направлять стандартный вывод не на экран, а в outfile . А командная строка

соединит стандартный вывод программы prog со стандартным вводом программы anotherprog .

Вывод, осуществляемый функцией printf , также отправляется в стандартный выходной поток. Вызовы putchar и printf могут как угодно чередоваться, при этом вывод будет формироваться в той последовательности, в которой происходили вызовы этих функций.

Любой исходный Си-файл, использующий хотя бы одну функцию библиотеки ввода-вывода, должен содержать в себе строку

причем она должна быть расположена до первого обращения к вводу-выводу. Если имя головного файла заключено в угловые скобки и > , это значит, что поиск головного файла ведется в стандартном месте (например, в системе UNIX это обычно директория /usr/include ).

Многие программы читают только из одного входного потока и пишут только в один выходной поток. Для организации ввода-вывода таким программам вполне хватит функций getchar , putchar и printf , а для начального обучения ознакомления с этими функциями уж точно достаточно. В частности, перечисленных функций достаточно, когда требуется вывод одной программы соединить с вводом следующей. В качестве примера рассмотрим программу lower , переводящую свой ввод на нижний регистр:

Функция tolower определена в . Она переводит буквы верхнего регистра в буквы нижнего регистра, а остальные литеры возвращает без изменений. Как мы уже упоминали, «функции» типа getchar и putchar из библиотеки и функция tolower из библиотеки часто реализуют в виде макросов, чтобы исключить накладные расходы от вызова функции на каждую отдельную литеру. В разд. 8.5 мы покажем, как это делается. Независимо от того, как на той или иной машине реализованы функции библиотеки , использующие их программы могут ничего не знать о кодировке литер.

Упражнение 7.1.

Напишите программу, осуществляющую перевод ввода с верхнего регистра на нижний или с нижнего на верхний в зависимости от имени, по которому она вызывается и текст которого находится в arg[0] .

7.2. Форматный вывод ( printf )

Функция printf переводит внутренние значения в текст.

В предыдущих главах мы использовали printf неформально. Здесь мы покажем наиболее типичные случаи применения этой функции; полное ее описание дано в приложении B.

Функция printf преобразует, форматирует и печатает свои аргументы в стандартном выводе под управлением формата. Возвращает она количество напечатанных литер.

Форматный стринг содержит два вида объектов: обычные литеры, которые впрямую копируются в выходной поток, и спецификации преобразования, каждая из которых вызывает преобразование и печать очередного аргумента printf . Любая спецификация преобразования начинается знаком % и заканчивается литерой–спецификатором. Между % и литерой–спецификатором могут быть расположены (в указанном ниже порядке) следующие элементы:

  • Знак минус, предписывающий «прижать» преобразованный аргумент к левому краю поля.
  • Число, специфицирующее минимальную ширину поля. Преобразованный аргумент будет занимать поле по крайней мере указанной ширины. При необходимости лишние позиции слева (или справа при левостороннем расположении) будут заполнены пробелами.
  • Точка, отделяющая ширину поля от величины, устанавливающей точность.
  • Число (точность), специфицирующее максимальное количество печатаемых литер стринга, или количество цифр после десятичной точки — для плавающего значения, или минимальное количество цифр — для целого.
  • Буква h , если печатаемое целое должно рассматриваться как short , или l (латинская буква эль), если целое должно рассматриваться как long .

Литеры–спецификаторы перечислены в табл. 7.1. Если за % не помещена литера–спецификатор, поведение функции printf будет не определено.

Таблица 7.1. Основные преобразования printf

Литера Тип аргумента; вид печати
d, i int ; десятичное целое.
o int ; беззнаковое восьмеричное (octal) целое (без ведущего нуля).
x, X int ; беззнаковое шестнадцатиричное целое (без ведущих 0x и 0X ), для 10. 15 используются abcdef или ABCDEF .
u int ; беззнаковое десятичное целое.
c int ; одиночная литера.
s char * ; печатает литеры, расположенные до знака \0 , или в количестве, заданном точностью.
f double ; [-]m.dddddd , где количество цифр d задается точностью (по умолчанию равно 6).
e, E double ; [-]m.dddddde+-xx или [-]m.ddddddE+-xx , где количество цифр d задается точностью (по умолчанию равно 6).
g, G double ; использует %e или %E , если экспонента меньше, чем -4, или больше или равна точности; в противном случае использует %f . «Хвостовые» нули и «хвостовая» десятичная точка не печатаются.
p void * ; указатель (представление зависит от реализации).
% аргумент не преобразуется; печатается знак % .

Ширину и точность можно специфицировать при помощи * ; значение ширины (или точности) в этом случае берется из следующего аргумента (который должен быть типа int ). Например, чтобы напечатать не более max литер из стринга s . годится следующая запись:

Большая часть форматных преобразований была продемонстрирована в предыдущих главах. Исключение составляет задание точности для стрингов. Далее приводится перечень спецификаций и показывается их влияние на печать стринга «hello, world» , состоящего из 12 литер. Поле специально обрамлено двоеточиями, чтобы была видна его протяженность.

:%s:
:%10s:
:%.10s:
:%-10s:
:%.15s:
:%-15s:
:%15.10s:
:%-15.10s:

Предостережение: функция printf использует свой первый аргумент, чтобы определить, сколько еще ожидается аргументов и какого они будут типа. Вы не получите правильного результата, если аргументов будет не хватать или они будут принадлежать не тому типу. Вы должны также понимать разницу в следующих двух обращениях:

Функция sprintf выполняет те же преобразования, что и printf , но вывод запоминает в стринге

Эта функция форматирует arg1 , arg2 и т.д. в соответствии с информацией, заданной аргументом format так, как мы ранее описывали, но результат помещает не в стандартный вывод, а в string . Заметим, что string должен быть достаточно большим, чтобы в нем поместился результат.

Упражнение 7.2.

Напишите программу, которая будет печатать разумным способом любой ввод. Как минимум она должна уметь печатать неграфические литеры в восьмеричном или шестнадцатиричном виде (в форме, принятой на вашей машине), обрывая длинные текстовые строки.

7.3. Списки аргументов переменной длины

Этот раздел содержит реализацию минимальной версии printf . Приводится она для того, чтобы показать, как надо писать функции со списками аргументов переменной длины, причем такие, которые были бы переносимы. Поскольку нас главным образом интересует обработка аргументов, функцию minprintf напишем таким образом, что она в основном будет работать со стрингом, задающим формат, и аргументами; что же касается форматных преобразований, то они будут осуществляться при помощи стандартного printf .

Декларация стандартной функции printf выглядит так:

Многоточие в декларации означает, что число и типы аргументов могут изменяться. Знак многоточие может стоять только в конце списка аргументов. Наша функция minprintf декларируется как

поскольку она не будет выдавать число литер, как это делает printf .

Вся сложность в том, каким образом minprintf будет продвигаться вдоль списка аргументов, — ведь у этого списка нет даже имени. Стандартный головной файл содержит набор макроопределений, которые определяют, как шагать по списку аргументов. Наполнение этого головного файла может изменяться от машины к машине, но представленный им интерфейс везде одинаков.

Тип va_list служит для описания переменной, которая будет по очереди ссылаться на каждый из аргументов; в minprintf эта переменная имеет имя ap (от «argument pointer» — указатель на аргумент). Макрос va_start инициализирует переменную ap , чтобы она указывала на первый безымянный аргумент. К va_start нужно обратиться до первого использования ap . Среди аргументов по крайней мере один должен быть именованным; от последнего именованного аргумента этот макрос «отталкивается» при начальной установке.

Макрос va_arg на каждом своем вызове выдает очередной аргумент, а ap передвигает на следующий; по имени типа он определяет тип возвращаемого значения и размер шага для выхода на следующий аргумент. Наконец, макрос va_end делает очистку всего, что необходимо. К va_end следует обратиться перед самым выходом из функции.

Перечисленные средства образуют основу нашей упрощенной версии printf .

Упражнение 7.3.

Дополните minprintf другими возможностями printf .

7.4. Форматный ввод ( scanf )

Функция scanf обеспечивающая ввод, является аналогом printf ; она выполняет многие из упоминавшихся преобразований, но в противоположном направлении. Ее декларация имеет следующий вид:

Функция scanf читает литеры из стандартною входного потока, интерпретирует их согласно спецификациям стринга format и рассылает результаты в свои остальные аргументы. Аргумент — формат мы опишем позже; другие аргументы, каждый из которых должен быть указателем, определяют, где будут запоминаться должным образом преобразованные данные. Как и для printf , в этом разделе дается сводка наиболее полезных, но отнюдь не всех возможностей данной функции.

Функция scanf прекращает работу, когда оказывается, что исчерпался формат или вводимая величина не соответствует управляющей спецификации. В качестве результата scanf возвращает количество успешно введенных элементов данных. По исчерпании файла она выдает EOF . Существенно то, что значение EOF не равно нулю, поскольку нуль scanf выдает, когда вводимая литера не соответствует первой спецификации форматного стринга. Каждое очередное обращение к scanf продолжает ввод с литеры, следующей сразу за последней обработанной.

Существует также функция sscanf , которая читает из стринга (а не из стандартного ввода).

Функция sscanf просматривает string согласно формату format и рассылает полученные значения в arg1 , arg2 и т.д. Последние должны быть указателями.

Формат обычно содержит спецификации, которые используются для управления преобразованиями ввода. В него могут входить следующие элементы:

  • Пробелы или табуляции, которые игнорируются.
  • Обычные литеры (исключая % ), которые, как ожидается, совпадут с очередными непробельными литерами входного потока.
  • Спецификации преобразования, каждая из которых начинается со знака % и завершается литерой–спецификатором типа преобразования. В промежутке между этими двумя литерами в любой спецификации могут располагаться, причем в том порядке, как они здесь указаны: знак * (признак подавления присваивания); число, определяющее ширину поля; буква h , l или L , указывающая на размер получаемого значения.

Спецификация преобразования управляет преобразованием следующего вводимого поля. Обычно результат помещается в переменную, на которую указывает соответствующий аргумент. Однако если в спецификации преобразования присутствует * , то поле ввода пропускается и никакое присваивание не выполняется. Поле ввода определяется как стринг без пробельных литер; оно простирается до следующей пробельной литеры или же ограничено шириной поля, если она задана. Поскольку литера новая_строка относится к пробельным литерам, это значит, что scanf при чтении будет переходить с одной строки на другую. (Пробельными литерами являются литеры пробела, табуляции, новой_строки, возврата_каретки, вертикальной_табуляции и перевода_страницы.)

Литера–спецификатор указывает, каким образом следует интерпретировать очередное поле ввода. Соответствующий аргумент должен быть указателем, как того требует механизм передачи параметров по значению, принятый в Си. Литеры–спецификаторы приведены в табл. 7.2.

Таблица 7.2. Основные преобразования scanf

Литера Вводимые данные; тип аргумента
d десятичное целое; int * .
i целое; int * . Целое может быть восьмеричными (с ведущим 0) или шестнадцатиричным (с ведущими 0x или 0X ).
o восьмеричное целое (с ведущим нулем или без него); int * .
u беззнаковое десятичное целое; unsigned int * .
x шестнадцатиричное целое (с ведущими 0x или 0X или без них);
c литеры; char * . Следующие литеры ввода (по умолчанию одна) размещаются в указанном месте. Обычный пропуск пробельных литер подавляется; чтобы прочесть очередную литеру, отличную от пробельной, используйте %1s .
s стринг литер (без обрамляющих кавычек); char * , указывающий на массив литер, достаточный для стринга и завершающей литеры ‘\0’ , которая будет добавлена.
e, f, g число с плавающей точкой, возможно, со знаком; обязательно присутствие либо десятичной точки, либо экспоненциальной части, а возможно, и обеих вместе; float * .
% сам знак % , никакое присваивание не выполняется.

Перед литерами–спецификаторами d , i , o , u и x может стоять буква h , указывающая на то, что соответствующий аргумент должен иметь тип short * (а не int * ), или l (латинская эль), указывающая на тип long * . Аналогично, перед литерами–спецификаторами e , f и g может стоять l , указывающая, что тип аргумента — double * (а не float * ).

Чтобы построить первый пример, обратимся к программе калькулятора из гл. 4, в которой организуем ввод с помощью функции scanf :

Предположим, что нам нужно прочитать строки ввода, содержащие данные вида

Обращение к scanf выглядит следующим образом:

Знак & перед monthday не нужен, так как имя массива есть указатель.

В стринге формата могут присутствовать литеры, не участвующие ни в одной из спецификаций; это значит, что эти литеры должны появиться на вводе. Так, мы могли бы читать даты вида mm/dd/yy при помощи следующего обращения к scanf :

В своем формате функция scanf игнорирует пробелы и табуляции. Кроме того, при поиске следующей порции ввода она пропускает во входном потоке все пробельные литеры (пробелы, табуляции, новые_строки и т.д.). Воспринимать входной поток, не имеющий фиксированного формата, часто оказывается удобнее, если вводить всю строку целиком и для каждого отдельного случая подбирать подходящий вариант sscanf . Предположим, например, что нам нужно читать строки с датами, записанными в любой из приведенных выше форм. Тогда мы могли бы написать:

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

В завершение еще раз напомним, что аргументы scanf и sscanf должны быть указателями.

Одна из самых распространенных ошибок состоит в том, что вместо того, чтобы написать

Компилятор о подобной ошибке ничего не сообщает.

Упражнение 7.4.

Напишите свою версию scanf по аналогии с minprintf из предыдущего раздела.

Упражнение 7.5.

Перепишите основанную на постфиксной записи программу калькулятора из гл. 4 таким образом, чтобы для ввода и преобразования чисел она использовала scanf и/или sscanf .

7.5. Доступ к файлам

Во всех предыдущих примерах мы имели дело со стандартным вводом и стандартным выводом, которые для программы автоматически предопределены операционной системой конкретной машины.

Следующий шаг — научиться писать программы, которые имели бы доступ к файлам, заранее не подсоединенным к программам. Одна из программ, в которой возникает такая необходимость — это программа cat , объединяющая несколько именованных файлов и направляющая результат в стандартный вывод. Функция cat часто применяется для выдачи файлов на экран, а также как универсальный «коллектор» файловой информации тех программ, которые не имеют возможности обратиться к файлу по имени. Например, команда

направит в стандартный вывод содержимое файлов x.c и y.c (и ничего более).

Возникает вопрос: что надо сделать, чтобы именованные файлы можно было читать; иначе говоря, как связать внешние имена, придуманные пользователем, с инструкциями чтения данных?

На этот счет имеются простые правила. Для того чтобы можно было читать из файла или писать в файл, он должен быть предварительно открыт при помощи библиотечной функции fopen . Функция fopen получает внешнее имя типа x.c или y.c , после чего осуществляет некоторые организационные действия и «переговоры» с операционной системой (технические детали которых здесь не рассматриваются) и возвращает указатель, используемый в дальнейшем для доступа к файлу.

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

Из этой записи следует, что fp есть указатель на FILE , а fopen возвращает указатель на FILE . Заметим, что FILE есть имя типа, наподобие int , а не тег структуры. Оно определено при помощи typedef . (Детали того, как можно реализовать fopen в системе UNIX, приводятся в разд. 8.5.)

Обращение к fopen в программе может выглядеть следующим образом:

Первый аргумент — стринг, содержащий имя файла. Второй аргумент несет информацию о режиме. Это тоже стринг: в нем указывается, каким образом пользователь намерен использовать файл. Возможны следующие режимы: чтение ( read — «r» ), запись ( write — «w» ) и добавление ( append — «a» ), т.е. запись информации в конец уже существующего файла. В некоторых системах различаются текстовые и бинарные файлы; в случае последних в стринг режима необходимо добавить букву «b» (binary — бинарный).

Тот факт, что некий файл, которого раньше не было, открывается на запись или добавление, означает, что он создается (если такая процедура физически возможна). Открытие уже существующего файла на запись приводит к выбрасыванию его старого содержимого, в то время как при открытии файла на добавление его старое содержимое сохраняется. Попытка читать несуществующий файл является ошибкой. Могут иметь место и другие ошибки; например, ошибкой считается попытка чтения файла, который по статусу запрещено читать. При наличии любой ошибки fopen возвращает NULL . (Возможна более точная идентификация ошибки; детальная информация по этому поводу приводится в конце разд. 1 приложения B.)

Следующее, что нам необходимо знать, — это как читать из файла или писать в файл, коль скоро он открыт. Существует несколько способов сделать это, из которых самый простой состоит в том, чтобы воспользоваться функциями getc и putc . Функция getc возвращает следующую литеру из файла; ей необходимо сообщить указатель файла, чтобы она знала откуда брать литеру.

Функция getc возвращает следующую литеру из потока, на который ссылаются при помощи *fp ; в случае исчерпания файла или ошибки она возвращает EOF .

Функция putc пишет литеру c в файл fp и возвращает записанную литеру или EOF , в случае ошибки. Аналогично getchar и putchar , программы getc и putc могут быть реализованы в виде макросов, а не функций.

При запуске Си-программы операционная система всегда открывает три файла и обеспечивает три файловые ссылки на них. Этими файлами являются: стандартный ввод, стандартный вывод и стандартный файл ошибок; соответствующие им указатели называются stdin , stdout и stderr ; они описаны в . Обычно stdin соотнесен с клавиатурой, а stdout и stderr — с экраном. Однако stdin и stdout можно связать с файлами или, используя механизм «трубопровода», соединить напрямую с другими программами, как это описывалось в разд. 7.1.

При помощи getc , putc , stdin и stdout функции getchar и putchar теперь можно определить следующим образом:

Форматный ввод-вывод файлов можно построить на функциях fscanf и fprintf . Они идентичны scanf и printf с той лишь разницей, что первым их аргументом является указатель, ссылающийся на файл, для которого осуществляется ввод-вывод, формат же указывается вторым аргументом.

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

Файловые указатели stdin и stdout представляют собой объекты типа FILE * . Это константы, а не переменные, следовательно, им нельзя ничего присваивать.

— обратная по отношению к fopen ; она разрывает связь между файловым указателем и внешним именем (которая раньше была установлена при помощи fopen ), освобождая тем самым этот указатель для других файлов. Так как в большинстве операционных систем количество одновременно открытых одной программой файлов ограничено, то файловые указатели, если они больше не нужны, лучше освобождать, как это и делается в программе cat . Есть еще одна причина применить fclose к файлу вывода, — это необходимость «опорожнить» буфер, в котором putc накопила, предназначенные для вывода данные. При нормальном завершении работы программы для каждого открытого файла fclose вызывается автоматически. (Вы можете закрыть stdin и stdout , если они вам не нужны. Воспользовавшись библиотечной функцией freopen их можно создать заново.)

7.6. Управление ошибками ( stderr и exit )

Обработка ошибок в cat нельзя признать идеальной. Беда в том, что если файл по какой-либо причине недоступен, сообщение об этом мы получим по окончании конкатенируемого вывода. Это нас устроило бы, если бы вывод отправлялся только на экран, а не в файл или другой программе, напрямую по «трубопроводу».

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

Перепишем cat так, чтобы сообщения об ошибках отправлялись в stderr .

Программа сигнализирует об ошибках двумя способами. Первый — сообщение об ошибке при помощи fprintf посылается в stderr с тем, чтобы оно попало на экран, а не оказалось в «трубопроводе» или в другом файле вывода. Имя программы, хранящееся в argv[0] , мы включили в сообщение, чтобы в случаях, когда данная программа работает совместно с другими, был ясен источник ошибки.

Второй способ указать на ошибку — обратиться к библиотечной функции exit , завершающей работу программы. Аргумент функции exit доступен некоторому процессу, вызвавшему данный процесс. А следовательно, успешное или ошибочное завершение программы можно проконтролировать с помощью некоей программы, которая рассматривает эту программу в качестве подчиненного процесса. По общей договоренности возврат нуля сигнализирует о том, что работа прошла нормально, в то время как ненулевые значения обычно говорят об ошибках. Чтобы опустошить буфера, накопившие информацию для всех открытых файлов вывода, функция exit вызывает fclose .

Инструкция главной программы return выр эквивалентна обращению к функции exit(выр) . Последняя запись (при помощи exit ) имеет то преимущество, что она пригодна для выхода и из других функций, и, кроме того, ее легко обнаружить при помощи программы контекстного поиска, похожей на ту, которую мы рассматривали в гл. 5.

Функция ferror выдает ненулевое значение, если в файле fp была обнаружена ошибка.

Хотя при выводе редко возникают ошибки, все же они встречаются (например, оказался переполненным диск); поэтому в программах широкого пользования они должны тщательно контролироваться.

Функция feof(FILE *) аналогична функции ferror ; она возвращает ненулевое значение, если встретился конец указанного в аргументе файла.

В наших небольших иллюстративных программах мы не заботились о выдаче статуса выхода, т.е. выдаче некоторого числа, характеризующего состояние программы в момент завершения: работа закончилась нормально или прервана из-за ошибки? Если работа прервана в результате ошибки, то какой? Любая серьезная программа должна выдавать статус выхода.

7.7. Ввод–вывод строк

В стандартной библиотеке имеется программа ввода fgets , аналогичная программе getline , которой мы пользовались в предыдущих главах.

Функция fgets читает следующую строку ввода (включая и литеру новая_строка) из файла fp в массив литер line , причем она может прочитать не более maxline-1 литер. Переписанная строка дополняется литерой ‘\0’ . Обычно fgets возвращает line , а по исчерпании файла или в случае ошибки — NULL . (Наша getline возвращала длину строки, которой мы потом пользовались, и нуль по концу файла.)

Функция вывода fputs пишет стринг (который может и не заканчиваться литерой новая_строка) в файл.

Эта функция возвращает EOF , если возникла ошибка, и нуль в противном случае.

Библиотечные функции gets и puts подобны функциям fgets и fputs . Отличаются они тем, что оперируют только стандартными файлами stdin и stdout , и, кроме того, gets выбрасывает последнюю литеру ‘\n’ , а puts ее добавляет.

Чтобы показать, что ничего особенного в функциях типа fgets и fputs нет, мы приводим их здесь в том виде, в каком они существуют в стандартной библиотеке на нашей системе.

Упражнение 7.6.

Напишите программу, сравнивающую два файла и печатающую первую строку, в которой они различаются.

Упражнение 7.7.

Модифицируйте программу поиска по образцу из гл. 5 таким образом, чтобы она брала текст из множества именованных файлов, а если имен файлов в аргументах нет, то — из стандартного ввода. Следует ли печатать имя файла, в котором найдена подходящая строка?

Упражнение 7.8.

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

7.8. Другие библиотечные функции

В стандартной библиотеке представлен широкий спектр различных функций. Настоящий раздел содержит краткий обзор наиболее полезных из них. Более подробно эти и другие функции описаны в приложении B.

7.8.1. Операции со стрингами

Мы уже упоминали функции strlen , strcpy , strcat и strcmp , описание которых даны в . Далее, до конца подраздела, предполагается, что s и t имеют тип char * , c и n — тип int .

strcat(s, t) конкатенирует t в конец s
strncat(s, t, n) конкатенирует n литер t в конец s
strcmp(s, t) возвращает отрицательное число, нуль или положительное число для s , s == t или s > t соответственно
strncmp(s, t, n) делает то же, что и strcmp , но количество сравниваемых литер не может превышать n
strcpy(s, t) копирует t в s
strncpy(s, t, n) копирует не более n литер t в s
strlen(s) возвращает длину s
strchr(s, c) возвращает указатель на первое появление литеры c в s или, если c нет в s , NULL
strrchr(s, c) возвращает указатель на последнее появление литеры c в s или, если c нет в s , NULL

7.8.2. Анализ класса литер и преобразование литер

Несколько функций из библиотеки выполняют проверки и преобразование литер. Далее, до конца подраздела, переменная c — это переменная типа int , которая может быть представлена значением unsigned char или EOF . Функции возвращают значения типа int .

isalpha(c) не нуль, если c — буква; 0 в противном случае
isupper(c) не нуль, если c — буква верхнего регистра; 0 в противном случае
islower(c) не нуль, если c — буква нижнего регистра; 0 в противном случае
isdigit(c) не нуль, если c — цифра; 0 в противном случае
isalnum(c) не нуль, если или isalpha(c) , или isdigit(c) истинны; 0 в противном случае
isspace(c) не нуль, если c — литера пробела, табуляции, новой_строки, возврата_каретки, перевода_страницы, вертикальной_табуляции
toupper(c) возвращает c , приведенную к верхнему регистру
tolower(c) возвращает c , приведенную к нижнему регистру

7.8.3. Функция ungetc

В стандартной библиотеке содержится более ограниченная версия функции ugetch по сравнению с той, которую мы написали в гл. 4. Называется она ungetc . Эта функция, имеющая прототип

отправляет литеру c назад в файл fp и возвращает c или EOF , в случае ошибки. Для каждого файла гарантирован возврат не более одной литеры. Функцию ungetc можно использовать совместно с любой из функций ввода типа scanf , getc , getchar и т.д.

7.8.4. Исполнение команд операционной системы

Функция system(char *s) выполняет команду системы, содержащуюся в стринге s , и затем возвращается к выполнению текущей программы. Содержимое s , строго говоря, зависит от конкретной операционной системы. Рассмотрим простой пример: в системе UNIX инструкция

вызовет программу date , которая направит дату и время в стандартный вывод. Функция возвращает зависящий от системы статус выполненной команды. В системе UNIX возвращаемый статус — это значение, переданное функцией exit .

7.8.5. Управление памятью

Функции malloc и calloc получают динамически запрашиваемые ими области памяти. Функция malloc с прототипом

возвращает указатель на n байт неинициализированной памяти или NULL , если запрос удовлетворить нельзя. Функция calloc с прототипом

возвращает указатель на область, достаточную для хранения массива из n объектов указанного размера ( size ), или NULL , если запрос не удается удовлетворить. Выделенная память обнуляется.

Указатель, возвращаемый функциями malloc и calloc , будет выдан с учетом выравнивания, выполненного согласно указанному типу объекта. Тем не менее к нему должна быть применена операция приведения к соответствующему типу, как это сделано в следующем фрагменте программы:

Функция free(p) освобождает область памяти, на которую указывает p , — указатель, первоначально полученный с помощью malloc или calloc . Никаких ограничений на порядок, в котором будет освобождаться память, нет, но ужасной ошибкой считается освобождение тех областей, которые не были получены при помощи calloc или malloc .

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

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

В разд. 8.7 мы рассмотрим реализацию программы управления памятью типа malloc , позволяющую освобождать выделенные блоки памяти в любой последовательности.

7.8.6. Математические функции

В описано более двадцати математических функций. Здесь же приведены наиболее употребительные. Каждая из них имеет один или два аргумента типа double и возвращает результат также типа double .

sin(x) синус x , x в радианах
cos(x) косинус x , x в радианах
atan2(y, x) арктангенс y/x , y/x в радианах
exp(x) экспоненциальная функция e x
log(x) натуральный (по основанию e) логарифм x ( x > 0 )
log10(x) обычный (по основанию 10) логарифм x ( x > 0 )
pow(x, y) x y
sqrt(x) корень квадратный x (x >= 0)
fabs(x) абсолютное значение x

7.8.7. Генератор случайных чисел

Функция rand() вычисляет последовательность псевдослучайных целых в диапазоне от нуля до значения, заданного именованной константой RAMD_MAX , которая определена в . Привести случайные числа к значениям с плавающей точкой, большим или равным 0 и меньшим 1 , можно по формуле

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

Функция srand(unsigned) осуществляет «начальную затравку» для rand . Реализации rand и srand , предлагаемые стандартом и, следовательно, переносимые на различные машины, рассмотрены в разд. 2.7.

Упражнение 7.9.

Реализуя функции типа isupper , можно либо экономить память, либо время. Напишите оба варианта функции.

Стандартный ввод и вывод: функции getchar и putchar

ALTE DOCUMENTE

Стандартный ввод и вывод: функции getchar и putchar

getchar . Функция getchar при каждом к ней обращении возвращает следующий вводимый символ. В большинстве сред, которые поддерживают язык «С», терминал может быть заменен некоторым файлом с помощью обозначения (символа «меньше»): если некоторая программа prog использует функцию getchar то командная строка:

приведет к тому, что prog будет читать из файла infile , а не с терминала. Переключение ввода делается таким образом, что сама программа prog не замечает изменения. В частности строка » не включается в командную строку аргументов в argv .

pipe -механизма * . В этом случае командная строка:

прогоняет две программы, otherprog и prog , и организует так, что стандартным вводом для prog служит стандартный вывод otherprog .

Функция getchar возвращает значение EOF , когда она попадает на конец файла, какой бы ввод она при этом не считывала. Стандартная библиотека полагает символическую константу EOF равной — 1 (посредством #define в файле stdio.h ), но проверки следует писать в терминах EOF , а не — 1, чтобы избежать зависимости от конкретного значения.

putchar , помещающей символ ‘с’ в «стандартный ввод», который по умолчанию является терминалом. Вывод можно направить в некоторый файл с помощью обозначения > (символа «больше») : если PROG использует putchar , то командная строка:

приведет к записи стандартного вывода в файл outfile , а не на терминал. Можно также использовать поточный механизм. Строка:

помещает стандартный вывод prog в стандартный ввод anotherprog . И опять prog не будет осведомлена об изменении направления.

printf , также поступает в стандартный вывод, и обращения к putchar и printf могут перемежаться.

getchar , putchar и printf может оказаться вполне адекватным и для начала определенно достаточным. Это особенно справедливо тогда, когда имеется возможность указания файлов для ввода и вывода и поточный механизм для связи вывода одной программы с вводом другой. Рассмотрим, например, программу lower , которая преобразует прописные буквы из своего ввода в строчные:

// Преобразование прописных букв в строчные

# include stdio . h >

isupper и tolower на самом деле являются макросами, определенными в stdio.h . Макрос isupper проверяет, является ли его аргумент буквой из верхнего регистра, и возвращает ненулевое значение, если это так, и нуль в противном случае. Макрос tolower преобразует букву из верхнего регистра в ту же букву нижнего регистра. Независимо от того, как эти функции реализованы на конкретной машине, их внешнее поведение совершенно одинаково, так что использующие их программы избавлены от знания символьного набора.

cat системы Unix ,

cat file 1 file 2 . | lower>output

и избежать тем самым вопроса о том, как обратиться к этим файлам из программы (программа cat приводится позже в этой главе).

getchar и putchar на самом деле могут быть макросами. Это позволяет избежать накладных расходов на обращение к функции для обработки каждого символа.

Вывод putchar у меня корректен?

Есть учебный маленький образец вывода посимвольного. Программа:

проблема, что она выводит вот такое:

так и должно быть?

1 ответ 1

Попробуйте while ((c = getchar()) != EOF)

Всё ещё ищете ответ? Посмотрите другие вопросы с метками c или задайте свой вопрос.

Похожие

Подписаться на ленту

Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.

дизайн сайта / логотип © 2020 Stack Exchange Inc; пользовательское содержимое попадает под действие лицензии cc by-sa 4.0 с указанием ссылки на источник. rev 2020.11.11.35402

Функции getchar и putchar

Прототип: int getchar( ); — чтение одного символа с клавиатуры.

Файл прототипа: stdio.h.

Возвращаемое значение: символ (при успешном вводе)или EOF (ошибка ввода или конец файла). Признак конца файла при вводе: символы Ctrl+z.

Прототип: int putchar(int); — вывод одного символа на экран.

Файл прототипа: stdio.h.

Возвращаемое значение: символ (успех) | EOF (ошибка).

// Чтение строки с терминала.

// Возвращает указатель на строку и ее длину

int getline(int lim, // Максимальная длина вводимой строки

char c; // Вводимый символ

int i;

for(i=0; i

return i;

Функции gets и puts

Прототип: char* gets(char*); — чтение строки символов.

Файл прототипа: stdio.h.

Возвращаемое значение: указатель на введенную строку (успех) | NULL— «пустой» указатель.

Ограничитель ввода: символ перевод строки, который после ввода заменяется на конечный нулевой символ.

char string[20];

Замечание. Можно включать в строку пробелы в отличие от спецификации s функции scanf.

Прототип: int puts(char*); — вывод на экран строки символов.

Файл прототипа: stdio.h.

Возвращаемое значение: последний выведенный символ (успех) | EOF (конец файла).

Замечание. Конечный 0 не выводится. В конец строки добавляется символ перевод строки.

Рекомендация. Этим функциям следует предпочтение перед применением спецификации sфункций scanfи printf по причинам:

— позволяют выполнять ввод-вывод строк, содержащих пробелы;

getsудаляет за собой ограничитель ввода из потока.

Внутренняя передача данных (функции sscanf и sprintf)

Эти функции являются функциями преобразования данных, а не ввода-вывода, однако правила преобразования те же, что и для соответствующих функций ввода-вывода.

Преобразование из символьной строки во внутреннее представление.

Прототип: int sscanf(char *buf, char *control, аргумент[, аргумент]. );

Здесь buf – исходная строка, control – управляющая строка.

Возвращаемое значение – см.функцию scanf.

Преобразование из внутреннего представления в символьную строку.

Прототип: int sprintf(char *buf, char *control, аргумент[, аргумент]. );

Файл прототипов обеих функций: stdio.h.

Возвращаемое значение: длина сформированной строки — число байтов без конечного 0, который добавляется или EOF (при ошибке).

Результат будет иметь вид: -37.45x**4

Вопросы для самопроверки и контроля

Вопросы для самопроверки

1. Какие символы являются ограничителями ввода для спецификации s функции scanf?

2. Удаляются ли символы-ограничители из входного потока функцией gets?

3. Какой символ помещается в конец выводимой строки функцией puts?

4. Можно ли ввести строку, содержащую пробелы, функцией scanf?

Контрольные вопросы

1. Какой символ ограничивает ввод функции gets?

2. Что возвращает функция getcharпри ошибке ввода?

3. Для чего можно использовать функции внутренней передачи данных?

4. Какие стандартные потоки определены в языке C?

ФАЙЛЫ

Файл – это набор данных ограниченного объема, хранящийся на некотором носителе информации. Файлом можно назвать книгу, музыку, записанную на компакт-диске, картину на холсте и многое другое. Однако исторически сложилось, что файлом называют наборы данных, которые можно обрабатывать средствами вычислительной техники. Основными носителями информации для них в настоящее время являются: «жесткий» диск, компакт-диск, флеш-память, микросхемы.

Основные понятия

По типу организации файлы делят на 2 основных вида: последовательные и файлы с прямым (произвольным) доступом. Последовательными называют файлы, в которых для доступа к нужному фрагменту информации необходимо просмотреть всю предыдущую информацию. Примером таких файлов является информация, записанная на магнитной ленте. Этот носитель может хранить только последовательные файлы. Однако последовательная организация применяется и на носителях информации, где возможен произвольный доступ. Файлы с произвольным (random) доступом позволяют обратиться к нужным данным, не просматривая предыдущую информацию. Очевидно, что для нахождения нужного фрагмента эти данные должны иметь некий, отличающий их от других, признак (ключ). В качестве ключа могут использоваться: поле файла базы данных или их комбинация, закладка, гиперссылка.

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

В данном разделе будут рассмотрены средства работы с последовательными текстовыми файлами, как с наиболее распространенными и простыми. Такие файлы могут быть подготовлены обычными редакторами текстов, например: NotePad (Блокнот), WordPad. Для цели обмена информацией между приложением и внешней средой используется набор встроенных функций.

Описание файла

Правила описания файлов зависят от операционной системы (ОС), однако традиционно это описание имеет вид:

В ОС Windows длина имени файла меньше или равна 255 символам и русские символы допустимы. Расширение часто называют типом файла, поскольку многие программы, их создающие, имеют расширения по умолчанию.

К ним относятся:

doc – текстовый процессор MS Word (с версии 2007 и выше – docx);

xls – табличный процессор MS Excel(с версии 2007 и выше – xlsx);

c – исходный модуль языка C;

cpp – исходный модуль языка C++;

vb – общий модуль языка Basic;

frm – файл экранной формы программы;

obj | o – объектный файл;

exe – выполняемый файл;

txt – текстовый файл, подготовленный редактором NotePadили другим.

Заметим, что пользователь имеет возможность дать этим типам другое расширение. Трансляторы с языков Cи C++сохраняют предыдущую версию исходного текста программы в файле с расширением bak.

Putc, putchar вывод литеры в файл

Добавим к системной функции write еще и read. Тогда мы получим возможность вводить данные в работающую программу. Эти данные могут поступать как с клавиатуры, так и из файлов на диске, а также по конвейеру из других программ. Добавление read делается с головокружительной простотой:

Лст. 1-1. Копирование файла в новый и правка.

Рис. 1-1. Функция read.

Меняется только write на read и номер системной функции 0. Как получить объектный модуль, вы уже видели. Сделайте это сами, а потом мы займемся очень полезным мероприятием: избавимся от бесконечного набора командных строк.

Здесь я предлагаю воспользоваться вот такой полезной вещью:

Лст. 1-2. Очень простой командный файл для компиляции.

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

$1 – это первый параметр, передаваемый в скрипт из командной строки. В нашем случае, ожидается имя главного файла программы на языке C. То есть фактически $1 представляет hello. Командный процессор просто производит подстановку. Остальные объектные модули должны быть уже подготовлены. Компилятор обрабатывает исходный файл на языке C, но перед записью в файл hello.s очищает его от макросов, при помощи потокового редактора sed. Вывод cc подается по конвейеру через фильтр. Программа для sed состоит из набора шаблонов для удаления строк, где они встречаются, поэтому очень важны малозаметные символы апострофов ‘.

Оставшиеся командные строки вам уже знакомы. Всюду, вместо $1, скрипт подставит hello, или другое имя с вашей программой. Только не используйте расширение файла .c – этот скрипт не настолько умен, чтобы его игнорировать.

Немного о ключах в командных строках. cc: Как вы уже знаете, -S это указание компилятору вывести результат в файл с ассемблерным кодом. -fno-builtin – это указание игнорировать имена встроенных функций. Это мы проясним потом, хотя пока ни одной такой функции не используется.

as: -o (output) Это ключ для имени выходного файла, по умолчанию мы получим файл a.out

ld: -o Как и выше, а -s (strip) удаление отладочной информации из исполняемого файла. И в самом деле, нам она только лишняя обуза. Итак, делаем скрипт исполняемым:

Лст. 1-3. Изменение прав доступа к файлу.

и пользуемся им на здоровье.

Чтобы испытать новую функцию read, напишем новую версию hello. Теперь программа должна спросить у пользователя имя и поприветствовать его. После того, как программа заработает, мы разберем ее достаточно подробно. Код программы я показываю пока в скринах редактора, чтобы подчеркнуть синтаксические элементы подсветкой. Позже они пойдут в обычных листингах.

Рис. 1-2. Новая версия hello.

Компилируем программу и запускаем:

Рис. 1-3. Новая версия. (Еще и работает!)

Вот так просто. Вместо длинных командных строк. По сути дела, мы выполнили операции 1, 2 и 5 из предыдущей главы (см. Рис. 0-8). По аналогии с производством можно сказать так: с помощью скрипта/команды m мы из мастера, который бегает между работниками, превратились в начальника цеха, который не занимается мелочами.

Теперь пришло время обратить внимание на сам код в программе C. Сначала на то, что в языке используется понятие типа данных. Есть несколько элементарных типов, мы не будем нудно перечислять, а затронем только используемые.

char – это числовой тип для представления букв, цифр и знков препинания. Занимает размер в 1 байт. Может представлять беззнаковые числа в диапазоне 0т 0 до 255.

int – это числовой тип для представления целых чисел. Любой элемент данных такого типа занимает 4 байта. Представляет числа со знаком в диапазоне от -2147483647 до 2147483647.
Тип данных записывается перед объявлением имени переменной. Так как char – это всего один байт, то для выделения памяти для строки используют массивы. Квадратные скобки – оператор индекса в массивах. Если в квадратных скобках ничего не указано, то компилятор может узнать о размере массива из его инициализирующей части. Например, ask имеет размер 19 байт, так как такова длина строки, записанная после оператора присваивания – знака равенства.

Кстати, строки в языке C всегда записываются в двойных кавычках, а одиночные символы – в одиночных кавычках.

Переменная-массив answer содержит заготовку ответа пользователю и достаточный запас для остатка – имени пользователя. На этот раз мы не просто инициализировали строку, но еще и предусмотрели 256 байтов.

В квадратных скобках указывается размер массива. Массив name резервирует память в размере 32 байта, но ничем не инициализирован.

Кроме того, в программе есть две переменные целого типа i и j, для перебора символов в строке. Как можно заметить, язык допускает перечисление нескольких переменных после объявления типа.

И еще одна очень важная вещь: каждая инструкция в языке C обязательно заканчивается точкой с запятой. Объявление переменной – тоже инструкция.

В этой книжке мы часто будем просматривать код ассемблера, сгенерированный для наших программ или отдельных функций. Это поможет лучше понять язык C, мы увидим его подобно врачу, который смотрит рентгеновский снимок. Сейчас давайте посмотрим на распределение памяти, которое создал компилятор для переменных.

Лст. 1-4. Память для переменных в программе hello v2.

Данные делятся на инициализированные и нет. В первом случае, они “зашиваются” в код программы. Во втором, – для данных выделяется память необходимого размера, но это происходит в процессе загрузки. Инициализированные данные влияют на размер скомпилированной программы, а неинициализированные нет. Неиспользованная память в сегменте данных заполняется нулями. Как мы можем видеть, это делает компилятор, заполняющий остаток строки нулями. Компилятор, как нетрудно догадаться, сам принимал решение, в каких сегментах размещать объявленные в программе переменные. Что касается выравнивания (alignment), то это способ расположить данные в памяти так, чтобы их выборка производилась как можно быстрее. Выравнивание приводит к небольшой потере памяти: для ask мы использовали всего 20 байт памяти, но из-за выравнивания с границей 16 было израсходовано 16+4 и еще 12 байтов образовали дыру, не занятую ничем. Такова оптимизация по умолчанию, которую предлагает компилятор, – по скорости выполнения.

Теперь к коду самой программы. Начнем с пояснения вызова функции. Первой в программе C всегда вызывается функция main (не считая стартового кода). Все остальное, внутри фигурных скобок, делает пользователь, то есть, тот, кто писал программу. Сначала мы вызываем функцию write, которая должна вывести вопрос на экран. Эта функция принимает три аргумента: дескриптор файла, адрес данных, которые надо вывести, и длинну (размер) данных.

Размер данных в нашем случае выполняет оператор sizeof. Он похож на функцию, но называется оператором. Формально это и есть функция, но sizeof выполняется во время компиляции, а не при работе программы. (Математики обычно называют операторами плохо ведущие себя функции.) Здесь sizeof избавляет нас от подсчета букв в строках.

1 – это дескриптор файла stdout, а 0 – stdin (есть еще 2 – stderr). Для каждой запущенной программы автоматически открываются эти три файловых потока. Правда, они имеют указанные символические имена (STanDard OUTtput, standard input, standard error), но у нас пока все так просто, что мы можем позволить себе роскошь пользоваться совсем небольшими числами.

Второй аргумент функции write – адрес строки для вывода. Как давно известно, любое имя всегда относится к определенному адресу, будь то имя переменной или имя функции. Их для того и придумали, чтобы ассемблер (или компилятор более высокого уровня) избавил человека от ужасного геморроя по ручному распределению адресов.

И наконец, третий аргумент – это размер данных. Чтобы знать, когда остановиться. Функция read имеет совершенно аналогичный набор аргументов. Список аргументов этих функций совершенно логичен: куда/откуда, что, и в каком количестве писать или читать.

После того, как функция read запишет в переменную name ввод пользователя, программа должна составить строку из приветствия, имени пользователя, и восклицательного знака. Для этого мы можем просто дописать в строку answer имя пользователя, или то, что он ввел, используя зарезеревированное место. Но что значит “дописать”? Мы должны объяснить это буквально: копируем символы из строки name в строку answer начиная с места после пробела. Копирование должно вовремя остановиться. Для этого мы должны вычислить длину введенного имени. (Можно было бы узнать эту длину другим способом, если вы наблюдательны и можете мыслить по аналогии.)

Цикл for (это тоже оператор, оператор цикла) занимается подсчетом длины. Вообще-то, он может заниматься чем угодно, но в нашем случае он подсчитывает байты. Этот пример цикла не использует инструкции, которые выполняются при его проходе. Бывают и такие циклы. В скобках этого оператора задается начальное значение переменной цикла, i, затем проверяется условие: не равен ли очередной байт нулю?, и затем, переменная цикла увеличивается на единицу. Если условие нарушено – цикл прекращается. Как результат, мы в этом случае используем саму переменную i.
Оператор != – имеет смысл “не равно”. Нулевой байт, как и каждый предстваитель типа char, закавычен апострофами. Причем, это не символ нуля, это именно нулевой байт, поэтому он записывается как \0, а не 0. Дело в том, что ‘0’ – это код символа нуль, он равен 0x30 в шестнадцатеричной системе счисления, а ‘\0’ – это 0x00 (числа записаны по правилам языка C в системе с основанием 16. Десятичный эквивалент 0x30 3*16 = 48).

Следующий цикл for использует переменную j для обращения к name по индексу элемента массива. Здесь проверяется аналогичное условие равенства байта нулю. Таким образом, мы достигаем конца строки name. В языке C признаком конца строки считается нулевой байт, называемый также нуль-терминатором или завершающим нулем, это синонимы. Конструкция answer[i++] означает, что сначала выбирается i-й байт массива answer, а затем i увеличивается на единицу. i++ это пример постинкремента, а у нас есть еще и другой пример, когда переменная сначала инкрементируется, а потом используется. Но об этом чуть ниже.

Последний цикл for имеет инструкцию, именно поэтому в нем нет точки с запятой после закрывающей скобки при for. Инструкция перенесена в следующую строку. Собственно, инструкция как раз и состоит из оператора for и последующего присваивания-копирования.

Странным может показаться последующее, где мы дописываем строку ответа. Когда второй цикл выполняется последний раз, то i увеличивается на единицу, так же, как и во всех предыдущих случаях. Тогда зачем мы обращается к предыдущему элементу answer[i-1]? Все дело в том, что когда данные в файловый поток поступают с клавиатуры, туда же идет и клавиша Enter. И лишь после этого строка завершается нулем. Все логично, вы же ввели строку в файл? Вот строку и получайте. Она интерпретируется как перевод строки, и восклицательный знак переедет на новую строку. Но такая строка, разорванная на две части, нас не устраивает. Поэтому мы подавляем ненужный символ, просто записывая на его место восклицательный знак. А затем, вместо последнего символа записываем перевод строки, символ ‘\n’ или байт 0x0A, что то же самое. И после этого записываем завершающий ноль. Чтобы не затереть \n, мы сначала увеличиваем i на единицу, а затем используем. Это пример преинкремента (предварительного увеличения) ++i. И наконец, записываем на экран строку, которая получилась в результате.

Конечно, в таком сыром виде язык никто не использует. Возможность писать функции предоставляет неисчерпаемые возможности по упорядочиванию и организации кода. И естественно, начинать нужно с самого начала, с работы над самыми простыми элементами текста, отдельными символами.

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

Лст. 1-5. Код функции getc.

Функция получает в качестве аргумента дескриптор файла и возвращает считанный символ. Если происходит ошибка при операции чтения, то read вернет число -1 в переменную e и установит код (причину) ошибки. Мы эту ситуацию пока не обрабатываем (не пользуемся переменной errno, да и бог с ней). Для нас важно, что если read возвращает не -1 и не 0, а что-то другое, то значит, все в порядке. А именно, read возвращает число реально прочитанных байтов. Если read прочитает символ конца файла, EOF, он же число -1, то она, (функция read), вернет ноль.

Как уже нам известно, read передает считанную из файла информацию через указатель в своем втором аргументе. Сначала мы получаем адрес той переменной, куда надо записать результат, а затем преобразуем его в указатель на целое и (третий аргумент), читаем один элемент данных. Так что, мы можем прочитать из файлового потока и -1 в том числе. Но обратите внимание, что на этот раз -1 попадает не в e, а в r, а это другое дело. В e в этом случае окажется 1.

Следующая инструкция проверяет, чем является e. Если это -1, то мы выходим из функции со значением -1. Если это 0, то мы тоже выходим из функции с тем же значением -1. Таким образом, ситуация ошибки также обрабатывается как конец файла, что в целом логично – файл-то, с точки зрения функции, все равно, накрылся медным тазом, до конца он прочитан, или нет. Если же все прошло нормально, то getc вернет считанный символ как целое число. В том числе, и символ UNICODE, который, в подавляющем числе случаев, занимает не более 2 байтов. Если read вернет 0, то из файла читайть больше нечего, а это равносильно концу файла и getc снова должна вернуть EOF, то есть, -1.

Функция putc действует обратным образом и записывает в поток символ, как целое число. Она использует системную функцию write.

Лст. 1-6. Код функции putc.

Эта функция получает два аргумента: символ для записи c, и идентификатор файла f. В случае успеха, то есть, если write вернет число, отличное от нуля, функция putc возвращает записанный символ, иначе -1.

Поскольку очень часто UNIX программы используются в конвейерах или просто терминале, то файловые потоки известны заранее. Это stdin и stdout (числа 0 и 1, в нашем нестрогом изложении). Поэтому были придуманы еще две функции getchar и putchar:

Лст. 1-7. Код функций getchar и putchar.

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

Имена функций getchar – взять (прочитать, извлечь) символ и putchar – положить (вывести, напечатать) символ просты и понятны всем англоязычным, а теперь и русским, пользователям языка C, С-программистам. К сожалению, этого не скажешь про множество других функций.

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

Была такая машинка шифровальная, широко известная по грешным делам Кригсмарине времен WW2 (Функшлюссель Ц). Вояки выкупили коммерческую машинку, усилили ее добавочными дисками и снабдили ею свой флот, бывший, в основном, подводным. Предполагалось, что расшифровка займет три месяца, а за это время соответствующая информация устареет. Сама суть шифрования заключалась в том, что каждая буква в тексте преобразовывалась в другую по заданному ключу. И если частотный анализ позволял легко восстановить исходную букву для одной операции, то для двух сделать это было уже труднее, трех, четырех – очень сложно и так далее.

Дело в том, что в каждом языке частота букв в словах своя, и зная статистику по буквам в тексте, можно восстановить исходный текст. Чем чаще мы применяем замену букв, тем “ровнее” делается распределение по частоте букв в тексте. Тем большая выборка, время и объем вычислений понадобятся для расшифровки. Знание метода шифрования тут ничем не поможет. Только угаданный ключ. Технически все эти дела могут быть обставлены очень тонко и хитро, но мы упростим задачу, тем более, что я и сам не сильно большой специалист в тайнописи. (Дж. И. Литтлвуд приводит такую шутку: “предположим для простоты”, что означает простоту самого рассуждающего.)

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

Лст. 1-8. Библиотечка для теста и шифровальная машина.

Работает все это достаточно просто. Программа работает в бесконечном цикле. В этом нет ничего страшного, в таком же цикле работает и любая операционная система. Всегда наступит некое событие, которое это цикл прервет (хотя бы Страшный Суд). В нашем случае цикл прервется в конце файла, поступающего на вход машины.

Внутри цикла читается очередной символ в переменную c. Сразу же проверяем: не пора ли закругляться? Если нет, запускаем машину, то есть, входим в функцию crypt. Функция получает символ и шифрует его. Зашифрованный символ помещается в ту же переменную, откуда был взят. Затем остается только направить его в выходной поток. Вот и вся работа на этом уровне.

Внутри crypt содержится код, на основе статической переменной i, симулирующий работу колеса настоящей машины. Чтобы «колесо» действительно вращалось, мы проверям значение i, и в случае, если оно больше 3, присваиваем ему значение 0. Таким образом, мы перебираем значение ключа key по кругу, что и означает работу колеса.

Сама же функция шифрования – это просто логическая операция XOR, предложенная еще американским инженером из AT&T Джилбертом Вернамом для шифрования телеграфных кодов. Она обратима, как мы позже увидим.

Посмотрим, как работает машинка:

Рис. 1-4. Работа шифровальной машины и тест функций.

Мы создали кодированный файл wetterbericht на диске. При попытке напечатать его мы получили абракадабру, содержащую непечатаемые символы. Для бумажной машинки это недопустимо, но для компьютерного файла, — почему бы и нет? В крайнем случае, мы всегда можем напечатать шестнадцатеричный дамп этого файла.

Мы также просто можем расшифровать зашифрованный файл:

Рис. 1-5. Повторное шифрование шифра.

«Весит» наша машинка всего 904 байта.

Если взять не ключ длиной 4 байта, а случайную последовательность символов очень большой длины, например, читаемую из отдельного файла, то можно обеспечить абсолютно стойкое шифрование. Такой прием использовали дипломаты и шпионы в докомпьютерную эру. Случайный файл назывался шифроблокнотом. Если оба корреспондента имеют такой большой файл, размером в несколько гигабайт, то можно шифровать не меньшее количество текста, при условии, что каждый кусок «ключа» используется однократно. Сгенерировать такой файл можно, имея генератор белого шума (например, на основе диода, включенного определенным образом) и АЦП на 8 бит, если направить поток образующихся данных в файл. Это будет истинно случайная последовательность, а не псевдослучайная, которая обычно применяется в компьютерах.

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

Что касается нашей машинки, это всего лишь игрушка, не так ли? Читателю предлагается как-нибудь на досуге выяснить, что означает:

Лст. 1-9. Послание потомкам.

Метод кодирования и длина ключа прежние. Надо найти ключ и закодированный текст.

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

А пока займитесь переносом кода в отдельные объектные модули.

Вопрос по c, putchar, getch, ncurses &#8211 getch и putchar не работают без возврата

Я пытался получить getch работать в другой программе без успеха. Таким образом, я сделал самую основную программу, которую я могу использовать getch так, как я хочу, чтобы он работал в основной программе.

Я исследовал необходимость noecho , cbreak , initscr а также nodelay Я также пытался использовать newscr() но безуспешно.

Проблема, с которой я сталкиваюсь, заключается в том, что символы не выводятся на экран до тех пор, пока я не нажму «ввод», когда они должны быть put на экран каждый цикл. Почему это происходит? Также курсор не возвращается в левую часть экрана на новой строке. например.

Я искал ответ, но снова в тупике .

И да, вы должны вызвать & quot; initscr () & quot; и & quot; cbreak () & quot ;.

1) Сравните ваш код с этим ncurses & quot; привет миром & quot ;:

2) Посмотрите, что произойдет, если вы сделаете & quot; refresh () & quot; и / или удалить «noecho ()».

3) В этом руководстве много полезной информации, которая также может помочь:

stdout Поток буферизован.

Ваша программа получает индивидуальные символы в порядке; но выходной поток их буферизует.

Пытаться fflush(stdout); или переключение stdout в небуферизованный режим с setbuf(stdout, NULL); .

Проблема с отключением буферизации состоит в том, что она неэффективна для массовой обработки данных, когда вывод не является терминалом.

Вы можете сделать это условным для стандартного вывода tty:

Чтобы вернуть курсор в начало строки, нужно вывести возврат каретки \r , Это потому, что проклятия & apos; cbreak режим отключил ONLCR Режим tty (на выходе, при отправке NL добавить CR).

Если вы безоговорочно добавляете \r , то он появится в файлах, когда ваш вывод будет перенаправлен. Итак, снова вам нужно немного isatty хак.

Намного лучше было бы узнать, как использовать tcgetattr а также tcsetattr функции для точного управления определенными параметрами tty, если все, что вам нужно, — это вводить символы за раз без эха, а не разрабатывать интерактивную программу на основе проклятий

Вы действительно хотите вводить символ за разом или просто набирать эхо? Эхо легко отключить. Вызов tcgetattr заполнить struct termios с текущими настройками дескриптора файла 0 (если это tty). Переверните несколько флагов, чтобы отключить эхо, затем позвоните tcsetattr установить обновленную структуру. Когда ваша программа выйдет, будьте добры и верните оригинал. Готово.

Putc, putchar вывод литеры в файл

В IAR имеется довольно удобная возможность для отладки — вывод в окно терминала текстовых сообщений.

[Вывод отладочных сообщений в окно терминала]

Для вывода отладочных сообщений используется оператор printf. По умолчанию все отладочные сообщения выводятся во встроенное в IAR окно терминала (Terminal I/O). Чтобы задействовать эту возможность, нужно выполнить следующие простые шаги:

1. Добавьте в код заголовочный файл stdio.h.

2. Укажите в нужном месте кода, где должен быть отладочный вывод, оператор printf с нужным текстовым сообщением:

3. Запустите отладку () и откройте окно терминала (View -> Terminal I/O). После выполнения оператора printf в окне терминала Вы увидите свое сообщение.

Обработка форматного вывода (%) printf может занимать много места в памяти программ. Поэтому В IAR есть выбор опций вывода printf, что иногда полезно для экономии памяти и ресурсов микроконтроллера. Выбор типа форматера printf (как и scanf) задается через настройку опций библиотеки проекта, Project -> Options. -> General Options -> закладка Library options (Full, Large, Small, Tiny). На возможности вывода также влияет выбор на закладке Library Configuration (None, Normal, Full, Custom). Подробнее по возможностям вариантов настройки форматтера смотрите Руководство разработчика IAR (IAR C/C++ Development Guide), раздел «Choosing formatters for printf and scanf».

[Перенаправление вывода printf IAR 4]

Вывод printf можно перенаправить в любое устройство ввода/вывода, например в DBGU или на экран LCD. Встроенная в IAR библиотека содержит готовый модуль, который для этого Вам нужно заменить на свою собственную реализацию, причем нет необходимости перекомпилировать всю библиотеку. Библиотечные файлы исходного кода, которые пользователь может перезадать на свои, для IAR 4.0 находятся в папке C:\Program Files\IAR Systems\Embedded Workbench 4.0 Evaluation\ARM\src\lib\. Здесь приведена пошаговая инструкция для изменения низкоуровневого вывода printf, для чего нужно определить свою собственную версию модуля write.c (нужно будет исправить код функции __write), и добавить реализацию функции MyLowLevelPutchar. В оригинальной версии модуля write.c функционал перенаправления вывода закомментирован, и в нем даны инструкции, как этот функционал включить.

1. Скопируйте файл write.c в папку своего проекта IAR, где находятся все модули исходного кода программы.
2. Добавьте скопированный модуль write.c в Ваш проект (Project -> Add Files. ), и откройте его в текстовом редакторе. Посмотрите реализацию функции __write: в ней блоком #if 0 / #else удалена реализация низкоуровневого вывода printf. Функция __write предназначена для вывода size символов из буфера buffer, причем она может делать этот вывод специальным, определяемым Вами способом (благодаря вызову MyLowLevelPutchar). Удалите директивы #if 0 / #else / #endif и последний ненужный оператор return _LLIO_ERROR; (он находится между #else и #endif).
3. Определите в проекте (в любом модуле, или прямо в модуле write.c) код функции MyLowLevelPutchar. Эта функция предназначена для низкоуровневого вывода одного символа на любое устройство вывода. Это может быть порт DBGU, файл, экран LCD или любой выбранный Вами интерфейс, поэтому в этой функции Вы сами должны определить код для вывода символа. Функция MyLowLevelPutchar должна возвратить тот же символ, который был у неё во входном параметре, либо -1 в случае ошибки (_LLIO_ERROR).

Внимание: после вышеуказанных шагов вывод printf во встроенное окно терминала перестанет работать (теперь printf будет выводить символы в указанное Вами устройство вывода).

Ниже приведен код модуля write.c, где перенаправление вывода раскомментировано, и дан пример реализации функции MyLowLevelPutchar, которая осуществляет отображение информации на пользовательском устройстве вывода.

Пример пользовательской реализации функции MyLowLevelPutchar. Функция пишет графику символов в видеопамять, массив video_memory, и обрабатывает символы CR, LF и TAB. Код скроллинга экрана ScrollUp не приведен (для упрощения).

[Перенаправление вывода printf IAR 5]

Илон Маск рекомендует:  Что такое код ociwritelobtofile
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL