basic-c/sections/03-io.tex

111 lines
13 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\section{Базовый ввод-вывод}
\subsection{Форматированный вывод}
Общение с пользователем на чистом С происходит через консоль. Для того, чтобы выводить какую-либо информацию для чтения пользователем - используется функция \code{printf();} предназначенная для форматированного вывода некоторого текста в консоль. Функция описана в заголовке \code{stdio.h}, поэтому мы и включили данный заголовок в нашу программу. Какого рода форматирование применяется при выводе строк в консоль? Существуют два основных инструмента придания выводу необходимого вида: экранированные последовательности (escape sequences) и заполнители (placeholders).
\paragraph{Экранированная последовательность} это буква или символ, написанные после знака обратного слэша (\code{\char`\\}), которые при выполнении программы будут на что-то заменены. Самые часто используемые это:
\begin{itemize}
\item \code{\char`\\'} - одинарная кавычка;
\item \code{\char`\\"} - двойная кавычка;
\item \code{\char`\\?} - вопросительный знак;
\item \code{\char`\\\char`\\} - обратный слэш;
\item \code{\char`\\0} - нулевой символ;
\item \code{\char`\\b} - забой (backspace);
\item \code{\char`\\f} - перевод страницы - новая страница;
\item \code{\char`\\n} - перевод строки - новая строка;
\item \code{\char`\\r} - возврат каретки;
\item \code{\char`\\t} - горизонтальная табуляция;
\item \code{\char`\\v} - вертикальная табуляция;
\item \code{\char`\\nnn} - произвольное восьмеричное значение;
\item \code{\char`\\xnn} - произвольное шестнадцатеричное значение;
\item \code{\char`\\unnnn} - произвольное Юникод-значение.
\end{itemize}
Чтобы убедится что это правильно работает выведем еще одну строку (в дополнение к коду со страницы \hyperref[code:firstprogram]{\pageref{code:firstprogram}}) с надписью <<Это новая строка>> на следующую строку нашей консоли. Также добавим к ещё одной строке символ табуляции чтобы увидеть как он работает. И сразу рассмотрим экранированную последовательность \code{\char`\\\char`\\} она делает ни что иное как добавляет символ обратного слэша в наш текст. Аналогичным образом работают и другие символьные экранирования. Это нужно, чтобы компилятор мог отличить, например, символ двойных кавычек в строке, написанной программистом, от символа двойных кавычек, завершающего строку в коде программы. Обратите внимание что не поставив в конец строки последовательность \code{\char`\\n} мы заставим компилятор постоянно писать текст на одной строке, не переходя на новую. И наконец \code{\char`\\0} сообщает компилятору что строка закончилась. Даже если у нас есть еще какие-то символы до закрывающих кавычек компилятор их просто проигнорирует. Добавим в код программы ещё пару строк, таким образом тело функции \code{main} должно принять следующий вид:
\begin{figure}[h!]
\begin{lstlisting}[language=C,style=CCodeStyle]
printf("Hello, world!\n");
printf("This is a new row");
printf("This is \"\t\"tab\n");
printf("This is \\ symbol\n");
printf("This is a terminant \0 it ends a string");
\end{lstlisting}
\end{figure}
Запустив программу мы можем убедиться, что всё работает так, как мы описали в тексте: сначала идёт приветствие миру, на новой строке сообщение о том, что это новая строка, далее на той же строке (ведь мы не переходили на новую) демонстрация пробела и символа табуляции. На последних двух строках происходит демонстрация обратного слэша и терминанта, видно, что остаток строки не был выведен на экран.
\begin{figure}[h!]
\begin{verbatim}
$ ./program
Hello, World!
This is a new rowThis is " "tab
This is \ symbol
This is terminant
$
\end{verbatim}
\end{figure}
\paragraph{Заполнитель} это также специальная последовательность, но она говорит компилятору, что на место этой последовательности необходимо вставить некий аргумент, который будет передан после строки, через запятую, при вызове данной функции \code{printf();}. Заполнитель начинается со знака процента и обозначает тип вставляемой переменной.
\begin{itemize}
\item \code{\%\%} - символ процента;
\item \code{\%i (\%d)} - целые числа (integer, decimal);
\item \code{\%ld (\%li)} целые числа (long int);
\item \code{\%s} - для вывода строк;
\item \code{\%c} для вывода символов;
\item \code{\%p} - для вывода указателей;
\item \code{\%f} для вывода чисел с плавающей точкой;
\item \code{\%lf} для вывода чисел с плавающей точкой удвоенной точности;
\item \code{\%x (\%X)} - беззнаковое целое в шестнадцатеричном виде.
\end{itemize}
Как видно, существуют заполнители для всех основных типов данных и для экранирования самого символа начала заполнителя. Заполнители можно и нужно комбинировать в строках как между собой, так и с экранированными последовательностями. Умение работать с заполнителями пригодится не только в консоли, но и при формировании локализованных сообщений в более сложных приложениях, в том числе на других языках.
\begin{figure}[h!]
\begin{lstlisting}[language=C,style=CCodeStyle]
int a = 50;
printf("%d\n", a);
printf("%5d\n", a);
printf("%05d\n", a);
printf("%.2f\n", 5.12345);
printf("Placeholders are \"%5d%%\" of formatting\n", a);
\end{lstlisting}
\end{figure}
Так первый оператор выведет просто число. Второй это же число, но оставив для его отображения пять пробельных символов (два окажутся заняты разрядами числа 50, и ещё три останутся свободными, слева от числа). Третий оператор форматированного вывода впишет число в пять отображаемых символов, но заполнит пустоту нулями (запись с лидирующими нулями, leading zeroes). Четвёртый осуществит вывод числа с плавающей точкой, ограничив дробную часть двумя отображаемыми символами, при этом важно, что не произойдёт математического округления, символы просто не отобразятся, такое отображение часто используется для демонстрации денежных значений в долларах и центах, рублях и копейках, и пр. Последний же оператор выведет на экран надпись, информирующую о том, что заполнители - это 50\% форматирования:
\begin{figure}[h!]
\begin{verbatim}
$ ./program
50
50
00050
5.12
Placeholders are " 50%" of formatting
$
\end{verbatim}
\end{figure}
Для заполнителей \code{\%d}, \code{\%i}, \code{\%f} часто используются дополнительные параметры, такие как количество знаков после запятой, например, \code{\%.2f} или минимальное количество знаков для отображения целого числа \code{\%5d}. Также в пользу оператора форматированного вывода говорит тот факт, что, например, в С++ стандартный вывод в консоль осуществляется с помощью команды \code{std::cout}, которая не поддерживала форматирование строк вплоть до принятия стандарта С++20.
\subsection{Форматированный ввод}
Поговорив о выводе в консоль нельзя не сказать о пользовательском вводе данных. Один из способов пользовательского ввода данных в программу - это использование функции \code{scanf();}. Предложим пользователю ввести некоторое число:
\begin{figure}[h!]
\begin{lstlisting}[language=C,style=CCodeStyle]
int input;
printf("Please, enter a number: ");
scanf("%d", &input);
\end{lstlisting}
\end{figure}
Функция \code{scanf();} это функция форматированного ввода. Принцип её работы очень похож на принцип работы функции \code{printf();} В двойных кавычках мы указываем в виде заполнителя тип переменной, которую ожидаем от пользователя, а в качестве дополнительного аргумента указываем адрес той переменной, в которую хотим записать введённые пользователем данные. Получается процесс прямо противоположный выводу. В этой функции можно использовать все те же заполнители, что и при выводе, поэтому пользователь может ввести как целые числа, так и символы, строки и числа с плавающей точкой.
\begin{figure}[h!]
\begin{lstlisting}[language=C,style=CCodeStyle]
printf("You entered %d, let's double it: %d\n", input, input * 2);
\end{lstlisting}
\end{figure}
Выведем в консоль изменённое число, введённое пользователем, чтобы удостовериться, что всё работает. В результате запуска программы, консоль застынет в ожидании пользовательского ввода. Пользователь сообщает консоли (терминалу операционной системы) об окончании ввода нажатием клавиши \code{Enter}:
\begin{figure}[h!]
\begin{verbatim}
$ ./program
Please, enter a number: 50
You entered 50, let's double it: 100
$
\end{verbatim}
\end{figure}
Функция форматированного ввода позволяет не только приводить пользовательский ввод к нужному типу данных, но и считывать разные сложные пользовательские вводы из множества переменных, разделённых символами.