Какие функции ввода вывода существуют в с
Перейти к содержимому

Какие функции ввода вывода существуют в с

  • автор:

Консольное приложение

Это руководство раскроет для вас некоторые возможности .NET и языка C#. Вы узнаете:

  • общие сведения о .NET CLI;
  • структура консольного приложения C#;
  • консольный ввод-вывод;
  • основные сведения об интерфейсах API файлового ввода-вывода в .NET;
  • основные сведениях об асинхронном программировании задач в .NET.

Вам предстоит создать приложение, которое считывает текстовый файл и выводит его содержимое в консоль. Вывод в консоль осуществляется с такой скоростью, которая позволяет читать текст вслух. Вы можете ускорить или замедлить темп, нажав клавиши «» (больше). Это приложение можно запустить в ОС Windows, Linux, macOS или в контейнере Docker.

В этом руководстве описано множество функций. Попробуем собрать их по одному.

Предварительные требования

  • Пакет SDK для .NET 6.
  • Редактор кода.

Создание приложения

Первым шагом является создание нового приложения. Откройте командную строку и создайте новый каталог для приложения. Перейдите в этот каталог. В командной строке введите команду dotnet new console . Эта команда создает начальный набор файлов для базового приложения Hello World.

Прежде чем вносить изменения, давайте запустим простое приложение Hello World. Когда вы создадите приложение, наберите в командной строке команду dotnet run . Эта команда запускает процесс восстановления пакета NuGet, создает исполняемый файл приложения и запускает этот файл.

Весь код простого приложения Hello World размещается в файле Program.cs. Откройте этот файл в любом текстовом редакторе. Замените код в Program.cs на следующий код:

namespace TeleprompterConsole; internal class Program < static void Main(string[] args) < Console.WriteLine("Hello World!"); >> 

В верхней части файла вы видите инструкцию namespace . Как и другие объектно ориентированные языки, с которыми вы могли работать ранее, C# использует пространства имен для организации типов. В нашей программе Hello World все точно так же. Как вы видите, программа находится в пространстве имен TeleprompterConsole .

Чтение и вывод файла

Первая функция, которую мы добавим, будет считывать данные из текстового файла и выводить полученный текст в консоль. Сначала нам нужно добавить текстовый файл. Скопируйте в каталог проекта файл sampleQuotes.txt из репозитория GitHub для этого примера. Он будет источником текста для вашего приложения. Чтобы скачать пример приложения для этого раздела, воспользуйтесь инструкциями в разделе Примеры и руководства.

Теперь добавьте в класс Program (он расположен сразу за методом Main ) следующий метод:

static IEnumerable ReadFrom(string file) < string? line; using (var reader = File.OpenText(file)) < while ((line = reader.ReadLine()) != null) < yield return line; >> > 

Этот метод является особым типом метода C#, который называется методом итератора. Методы итератора возвращают последовательности, которые оцениваются отложенно. Это означает, что каждый элемент в последовательности создается только в тот момент, когда к нему выполняется обращение в коде обработки последовательности. Методы итератора — это методы, содержащие один или несколько yield return операторов. Возвращаемый методом ReadFrom объект содержит код для создания каждого элемента последовательности. В нашем примере он читает следующую строку текста из исходного файла и возвращает эту строку. Каждый раз, когда вызывающий код запрашивает следующий элемент из последовательности, код считывает из файла и возвращает следующую строку текста. Когда файл закончится, последовательность сообщает, что в ней больше нет элементов.

Здесь используются два элемента синтаксиса C#, которые могут быть для вас новыми. Инструкция using в этом методе управляет освобождением ресурсов. Переменная, которая инициализируется в инструкции using (в нашем примере это reader ) должна реализовывать интерфейс IDisposable. Этот интерфейс определяет единственный метод ( Dispose ), который вызывается для освобождения ресурса. Компилятор создает такой вызов, когда выполнение кода достигает закрывающей скобки инструкции using . Созданный компилятором код гарантирует освобождение ресурса даже в том случае, если в блоке кода, определенном инструкцией using, будет создано исключение.

Переменная reader определена с ключевым словом var . Ключевое слово var определяет неявно типизированную локальную переменную. Это означает, что тип переменной определяется во время компиляции по типу объекта, присвоенного этой переменной. Здесь это возвращаемое значение метода OpenText(String), то есть объект StreamReader.

Теперь давайте создадим в методе Main код для чтения файла:

var lines = ReadFrom("sampleQuotes.txt"); foreach (var line in lines)

Запустите программу командой dotnet run и убедитесь в том, что все текстовые строки выводятся в консоль.

Добавление задержек и форматирование выходных данных

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

В этом разделе описаны два действия. Во-первых, обновите метод итератора, чтобы он возвращал не всю строку целиком, а каждое слово отдельно. Для этого внесите такие изменения. Замените инструкцию yield return line; следующим кодом:

var words = line.Split(' '); foreach (var word in words) < yield return word + " "; >yield return Environment.NewLine; 

Теперь следует изменить код обработки строк файла, добавив задержку после вывода каждого слова. Замените инструкцию Console.WriteLine(line) в методе Main на такой блок кода:

Console.Write(line); if (!string.IsNullOrWhiteSpace(line)) < var pause = Task.Delay(200); // Synchronously waiting on a task is an // anti-pattern. This will get fixed in later // steps. pause.Wait(); >

Запустите пример и проверьте выходные данные. Теперь слова появляются по одному и с задержками по 200 мс. Но пока с выводом сохраняются некоторые проблемы, поскольку в исходном текстовом файле есть несколько строк длиной более 80 символов, и они выводятся без перевода строки. Это не очень удобно читать с прокруткой. Но эту проблему легко исправить. Вам нужно лишь отслеживать длину каждой строки и создавать новую строку каждый раз, когда эта длина достигает определенного порога. После объявления words в методе ReadFrom объявите локальную переменную для хранения длины строки:

var lineLength = 0; 

Теперь добавьте следующий код после инструкции yield return word + » «; (перед закрывающей фигурной скобкой):

lineLength += word.Length + 1; if (lineLength > 70)

Запустите пример, и теперь вы сможете читать текст вслух в заданном темпе.

Асинхронные задачи

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

Добавьте следующий метод в класс Program . Этот текст основан на тексте метода Main :

private static async Task ShowTeleprompter() < var words = ReadFrom("sampleQuotes.txt"); foreach (var word in words) < Console.Write(word); if (!string.IsNullOrWhiteSpace(word)) < await Task.Delay(200); >> > 

Вы можете заметить два изменения. Во-первых, в тексте нет вызова Wait(), который в синхронном режиме ожидает завершения задачи. Вместо него в этой версии используется ключевое слово await . Чтобы это работало, в сигнатуру метода нужно добавить модификатор async . Этот метод возвращает Task . Обратите внимание, что здесь нет инструкции для возвращения объекта Task . Вместо этого объект Task создается в коде, который компилятор предоставляет в точке использования оператора await . Представьте, что метод завершает выполнение при достижении await . Он возвращает Task в знак того, что работа еще не завершена. Метод возобновит свою работу, когда завершится ожидаемая задача. Когда работа метода завершится, это будет отражено в возвращаемом объекте Task . Вызывающий код может отслеживать состояние полученного Task , чтобы определить момент завершения метода.

await Добавьте ключевое слово перед вызовом : ShowTeleprompter

await ShowTeleprompter(); 

Для этого необходимо изменить сигнатуру Main метода следующим образом:

static async Task Main(string[] args) 

Дополнительные сведения о методе async Main см. в разделе «Основы».

Затем необходимо написать второй асинхронный метод для чтения из консоли и watch для ключей «» (больше) и «X» или «x». Для выполнения этой задачи добавьте приведенный ниже метод.

private static async Task GetInput() < var delay = 200; Action work = () =>< do < var key = Console.ReadKey(true); if (key.KeyChar == '>') < delay -= 10; >else if (key.KeyChar == ' <') < delay += 10; >else if (key.KeyChar == 'X' || key.KeyChar == 'x') < break; >> while (true); >; await Task.Run(work); > 

При этом создается лямбда-выражение для представления делегата Action , который считывает ключ из консоли и изменяет локальную переменную, представляющую задержку, когда пользователь нажимает клавиши «» (больше). Метод делегата завершается, когда пользователь нажимает клавиши «X» или «x», что позволяет пользователю в любое время остановить отображение текста. Этот метод использует метод ReadKey(), чтобы блокировать выполнение и ожидать нажатия клавиши.

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

Пришло время создать класс, который может обрабатывать совместное использование данных двумя задачами. Этот класс содержит два открытых свойства: delay (задержка) и флаг Done , который означает, что файл прочитан полностью:

namespace TeleprompterConsole; internal class TelePrompterConfig < public int DelayInMilliseconds < get; private set; >= 200; public void UpdateDelay(int increment) // negative to speed up < var newDelay = Min(DelayInMilliseconds + increment, 1000); newDelay = Max(newDelay, 20); DelayInMilliseconds = newDelay; >public bool Done < get; private set; >public void SetDone() < Done = true; >> 

Поместите этот класс в отдельный новый файл и включите его в пространство имен TeleprompterConsole , как показано выше. Также следует добавить оператор using static в верхнюю часть файлами, чтобы можно было ссылаться на методы Min и Max без указания имени внешнего класса или пространства имен. Инструкция using static импортирует все методы из одного класса. Это отличается от оператора using без static , который импортирует все классы из пространства имен.

using static System.Math; 

Теперь вам нужно обновить методы ShowTeleprompter и GetInput для использования нового объекта config . И еще одна инструкция Task , которая возвращает метод async , запускающий обе задачи и завершающий работу после окончания первой задачи:

private static async Task RunTeleprompter()

Новым методом здесь является WhenAny(Task[]). Этот метод создает задачу ( Task ), которая завершается сразу, как только завершится любая из задач в списке аргументов.

Теперь вам нужно обновить методы ShowTeleprompter и GetInput , чтобы они использовали объект config для задержки:

private static async Task ShowTeleprompter(TelePrompterConfig config) < var words = ReadFrom("sampleQuotes.txt"); foreach (var word in words) < Console.Write(word); if (!string.IsNullOrWhiteSpace(word)) < await Task.Delay(config.DelayInMilliseconds); >> config.SetDone(); > private static async Task GetInput(TelePrompterConfig config) < Action work = () =>< do < var key = Console.ReadKey(true); if (key.KeyChar == '>') config.UpdateDelay(-10); else if (key.KeyChar == ' <') config.UpdateDelay(10); else if (key.KeyChar == 'X' || key.KeyChar == 'x') config.SetDone(); >while (!config.Done); >; await Task.Run(work); > 

Новая версия метода ShowTeleprompter вызывает новый метод из класса TeleprompterConfig . Сейчас нужно изменить метод Main , чтобы вместо ShowTeleprompter он вызывал RunTeleprompter :

await RunTeleprompter(); 

Заключение

В этом учебнике мы продемонстрировали вам ряд функций языка C# и библиотек .NET Core, связанных с работой в консольных приложениях. На основе полученных знаний вы сможете развивать свои представления о языке и представленных здесь классах. Вы увидели базовые примеры использования файлового и консольного ввода-вывода, асинхронного программирования на основе задач с блокировкой и без блокировки. Вы узнали о языке C# и структуре программ на C#, а также о .NET CLI.

Дополнительные сведения о файловом вводе-выводе см. в статье Файловый и потоковый ввод-вывод. Дополнительные сведения о модели асинхронного программирования, используемой в учебнике, см. в статьях Асинхронное программирование на основе задач и Асинхронное программирование.

Совместная работа с нами на GitHub

Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.

Ввод данных и обработка исключений в С++: 4-я часть гайда по языку программирования

Разбираемся, как правильно использовать ввод данных, чтобы ничего не сломалось.

Евгений Кучерявый

Евгений Кучерявый

Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.

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

Если программа работает с изначально заданными данными, то она полезна только один раз, потому что результат будет всегда один и тот же. Гораздо эффективнее она становится, когда можно использовать разные данные:

  • числа для вычисления;
  • команды пользователя;
  • изображения;
  • аудио и видео;
  • текст и так далее.

Как получить и обработать данные через консоль? Давайте пробовать.

Команды ввода и вывода в C++

В самом начале кода каждой программы мы подключаем библиотеку iostream Input/Output Stream (поток ввода/вывода). Именно в ней находится команда cout, что позволяет выводить данные на экран консоли. В ней же есть команда cin, которая, наоборот, запрашивает пользовательский ввод.

Давайте напишем простую программу, которая спрашивает имя пользователя.

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

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

Как видно на скриншоте, в первом случае всё прошло успешно, но в следующих случаях программа выдала ошибку. Давайте рассмотрим последнюю:

Разберём некоторые моменты подробнее:

  • Исключение(exception) — это что-то вроде ошибки, но не совсем. Разные части программы могут «выбрасывать» их при определённых условиях, чтобы сказать: что-то идёт не так; в данном случае — out of range (выход из диапазона). Это исключение было выброшено, потому что введённое число больше, чем может поместиться в переменную типа int.
  • Дамп памяти (memory dump) — это содержимое рабочей памяти одного процесса, ядра или всей операционной системы. Дамп можно посмотреть, чтобы понять, каким было состояние программы и почему она аварийно завершила работу.

Чтобы программа не закрывалась при выбрасывании исключений, их нужно обработать.

Обработка исключений в C++

Для этого нам пригодится конструкция try-catch.

Вот код программы, которая проверяет корректность введённых данных:

Пользовательский ввод и ветвление

С помощью пользовательского ввода можно по-настоящему раскрыть возможности условных конструкций. Например, конструкция switch позволяет создать простое консольное меню.

Конструкция if пригодится для решения квадратных уравнений:

Заключение

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

Читайте также:

  • Объектно-ориентированное программирование. Часть 1. Что такое классы и объекты
  • Как сеньор и тимлид собеседует других сеньоров
  • Как добавить в приложение поддержку нескольких языков

C#. Урок 3. Консольный ввод\вывод

Follow us on Google Plus Follow us on rss

Backend-разработка и создание утилит командной строки невозможно без консольного ввода\вывода. В рамках данной статьи будут рассмотрены методы класса Console для чтения и записи информации в консоль.

  • Консольный ввод\вывод
  • Класс Console. Вывод данных в консоль
    • Метод Write
    • Метод WriteLine
    • Метод Read
    • Метод ReadLine
    • Метод ReadKey

    Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.

    Консольный ввод\вывод

    Все приложения условно можно разделить на две группы: консольные и с графическим интерфейсом пользователя. Ко второй группе относится большая часть приложений, с которыми работают пользователи в операционной системе Windows, на смартфонах и в вебе. Под консольными будем понимать приложения, которые запускаются через терминал (командную строку), их взаимодействие с пользователем происходит в текстовом режиме. Практически все приложения, которые запускаются на стороне сервера можно отнести к этой группе. Для организации ввода\вывода информации при работе с консолью в C# используется класс Console.

    Класс Console. Вывод данных в консоль

    Класс Console содержит методы для работы с консольным вводом\выводом, управлением потоком с ошибками и окном консоли. Начнем с вывода информации в консоль, для решения этой задачи класс Console предоставляет два метода: Write и WriteLine.

    Для знакомства с методами класса Console создайте в удобном для вас каталоге проект с именем CSharpLesson3:

    > dotnet new console -o CSharpLesson3

    Метод Write

    Метод Write – направляет в стандартный выходной поток текстовое представление переданного в него объекта. Дополнительно, предоставляет возможность задать форматирование.

    Откройте файл Program.cs в созданном ранее проекте и добавьте в метод Main следующие строки, демонстрирующие работу с методом Write:

    // Примеры работы с методом Write без форматирования Console.Write("Текущая дата: "); Console.Write(DateTime.Now); Console.Write("\n"); Console.Write($"Текущая дата: \n"); // Примеры работы с методом Write с использованием форматирования Console.Write("Текущая дата: \n", DateTime.Now); Console.Write("Число: \n", 123.456789);

    Более подробно про форматирование будет написано в уроке, посвященном работе со строками.

    Сохраните документ, откройте консоль, перейдите в каталог с проектом и выполните команду:

    > dotnet run

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

    Текущая дата: 16.10.2020 11:48:28 Текущая дата: 16.10.2020 11:48:28 Текущая дата: 16.10.2020 11:48:28 Текущая дата: 1,234568E+002

    Обратите внимание на первые три вызова метода Write, если не поставить символ перевода строки, то данные будут выдаваться в одну строку друг за другом, это не всегда удобно. Если требуется, чтобы вывод каждый раз осуществлялся с новой строки, то воспользуйтесь методом WriteLine.

    Метод WriteLine

    Метод WriteLine записывает указанные данные в выходной поток и добавляет символ перевода строки. Добавьте в программу следующие строки:

    Console.WriteLine("Текущая дата: "); Console.WriteLine(DateTime.Now);

    Теперь надпись “Текущая дата:” и дата со временем будут выведены в разных строках.
    WriteLine (также как Write) может принимать не только строковые значения, но и переменные разных типов:

    DateTime nowDate = DateTime.Now; string someText = "Сегодня"; int number = 924; Console.WriteLine(nowDate); Console.WriteLine(someText); Console.WriteLine(number);

    Удобным и наглядным способом вывода значений переменных в консоль с дополнительным текстовым пояснением является использование строковой интерполяции:

    Console.WriteLine($" . Число: ");

    Запустите проект и в консоли увидите результат выполнения программы:

    Сегодня 26.08.2020 11:53:44. Число: 924

    Прием интерполяции позволяет внутри строки, используя фигурные скобки, указывать имена переменных, которые будут заменены значениями. Перед такой строкой ставится знак $.
    Методы WriteLine и Write позволяют использовать форматирование:

    Console.WriteLine(":, Число: ", nowDate, someText, number);

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

    Класс Console. Чтение данных из консоли

    В классе Console, помимо рассмотренных выше методов вывода в консоль, присутствуют методы для считывания вводимых данных из консоли.

    Метод Read

    Метод Read возвращает числовой код последнего введенного символа во входном потоке, если символ введен не был, то будет возвращен код -1. Для завершения работы метода, после ввода символа необходимо нажать клавишу “Enter”. Рассмотрим работу с методом Read на примере:

    Console.WriteLine("Нажмите любую клавишу, а затем Enter"); int key1 = Console.Read(); Console.WriteLine($"Код нажатой клавиши: "); Console.WriteLine("Символьное представление: " + Convert.ToChar(key1));

    Если после надписи “Нажмите любую клавишу, а затем Enter” будет введено более одного символа, то Read вернет только первый.

    Для представления кода в виде символа необходимо переменную key1 привести к типу Char, для этого можно воспользоваться методом Convert.ToChar().

    Метод ReadLine

    Метод ReadLine возвращает введенную строку символов до нажатия клавиши “Enter”. Добавим несколько строк в файл с исходным кодом, для демонстрации работы с методом ReadLine :

    Console.WriteLine("Введите ваше имя, а затем нажмите Enter"); string name = Console.ReadLine(); Console.WriteLine($"Привет, !");

    Если необходимо получить с консоли численные данные, то следует воспользоваться методами из класса Convert для приведения типа String к соответствующему числовому типу:

    Console.WriteLine("Сколько вам лет?"); int age = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("Ваш рост в см?"); int height = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("Привет , вам уже полных лет и ваш рост см", name, age, height);

    Метод ReadKey

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

    Console.WriteLine("Нажмите любую клавишу:"); var key2 = Console.ReadKey(); Console.WriteLine(key2.Key); Console.WriteLine(key2.KeyChar);

    Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.

    Стандартные потоки ввода/вывода. Функции putchar() и getchar()

    На этом занятии затронем тему стандартных потоков ввода/вывода. Вначале ответим на вопрос, что это за потоки и для чего они нужны?

    Я думаю, вы все прекрасно понимаете, что программы часто выводят результаты своей работы на монитор, а считывают информацию с клавиатуры. Хотя это и не обязательно так, но чаще всего мы имеем дело именно с монитором и клавиатурой. Так вот, на уровне операционной системы, как правило, имеются три стандартных потока ввода/вывода:

    • stdout – поток вывода информации (как правило, на монитор);
    • stderr – поток вывода ошибок (как правило, на монитор);
    • stdin – поток ввода информации (как правило, с клавиатуры).

    Конечно, все эти потоки можно настроить на любые другие устройства. Например, в некоторых случаях поток ошибок stderr ассоциируется с самописцем (принтером), который сразу на бумаге выдает ошибки, произошедшие в процессе работы программы. А поток вывода stdout можно связать с файлом, куда будет выводиться вся информация. Аналогично поток ввода stdin. Вместо клавиатуры можно использовать любое устройство ввода, вплоть до магнитных лент, информация с которых в битовом представлении поступает в поток ввода. То есть, монитор и клавиатура – это всего лишь частные, хотя и частые случаи. Благодаря использованию стандартных потоков ввода/вывода, программы способны универсальным образом работать с любыми устройствами, связанными с этими потоками. И это очень удобно. На программном уровне все эти потоки организованы в виде буферов приема или передачи информации. То есть, данные сначала поступают в буфер, а затем, уже либо на устройство вывода, либо в переменные программы. Это очень важный момент. Данные поступают в программу, например, с клавиатуры, не напрямую, а через буфер ввода. То же самое с выводом. Сначала данные из программы попадаю в буфер вывода и только потом, например, отображаются на мониторе или записываются в файл. Запомним этот момент. Он нам в будущем пригодится.

    Функции для работы со стандартными потоками

    • putchar() – вывод символа через поток stdout;
    • printf() – форматный вывод строки через поток stdout;
    • perror() – вывод ошибок в виде строки через поток stderr;
    • getchar() – чтение одного байта (символа) из потока stdin;
    • scanf() – форматный ввод данных из потока stdin.
    #include

    Функция getchar()

    Давайте рассмотрим эти функции и начнем с getchar(). Она имеет следующее определение: int getchar(void); Целочисленный тип int перед функцией означает, что она возвращает целое число, а void в круглых скобках говорит об отсутствии каких-либо параметров. Поэтому мы можем вызвать ее в программе следующим образом:

    #include int main(void) { int value = getchar(); return 0; }

    После запуска этой программы будет ожидаться ввод значения в поток stdin. Так как он по умолчанию ассоциирован с клавиатурой, то нам нужно набрать один символ, любой, например, буквы ‘s’ и нажать клавишу Enter. После этого программа продолжится, перейдет к следующему оператору return и завершится. Я напомню, что функция getchar() предназначена для чтения одного байта из входного потока stdin. И здесь возникает вопрос, почему эта функция возвращает целое число типа int, а не данные типа char, который и описывает один байт памяти? Дело в том, что стандартный поток ввода работает несколько более сложным образом, нежели просто выдача очередного байта из буфера. В частности, он дополнительно генерирует некоторые служебные значения. Например, значение: EOF (End of File – конец файла) которое определено как -1 в заголовочном файле stdio.h. То есть, помимо байтового диапазона [0; 255] целых чисел функция getchar() дополнительно может вернуть значения вне этого диапазона, в частности, -1. Вам может показаться странным, что мы говорим про константу EOF, когда речь идет о вводе данных с клавиатуры? Но, во-первых, стандартный поток ввода stdin вполне можно связать с файлом и тогда данные будут читаться из него, а не с клавиатуры и при достижении конца файла будет сгенерировано значение EOF. И, во-вторых, при вводе с клавиатуры мы также можем симитировать достижение конца файла путем ввода специального символа комбинацией клавиш Ctrl+Z для ОС Windows и Ctrl+D для ОС Linux. Таким образом, функции getchar() нужно возвращать целые значения, превышающий байтовый диапазон [0; 255]. Поэтому разработчик языка Си решил использовать тип int.

    Функция putchar()

    Следующая аналогичная функция – это putchar(), которая служит для вывода одного байта (символа) в выходной поток stdout и определена следующим образом: int putchar(int ch); Она также возвращает целое число типа int и в качестве аргумента принимает целое значение этого же типа int. В действительности, тип int здесь использован для сопряжения (по типам данных) с функцией getchar(). Иначе бы можно было прописать тип char, так как функция putchar() в качестве аргумента принимает код символа в диапазоне [0;255]. Любое другое значение за пределами этого диапазона просто будет приводиться к восьми битам и затем помещаться в выходной поток stdout. Возвращает эта функция код символа, переданного в выходной поток:

    #include int main(void) { int value = getchar(); int res = putchar(value); printf("\n%d\n", res); return 0; }

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

    putchar(value);

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

    Буферы приема/передачи стандартных потоков

    На протяжении всего занятия я акцентрирую ваше внимание на наличие буферов приема/передачи информации у стандартных потоков ввода/вывода. При запуске программы они пустые, в них нет никаких посторонних значений. Но, в процессе ввода или вывода информации они заполняются и это может повлиять на ход исполнения программы. Давайте я это покажу на конкретном примере. Запишем два подряд идущих вызова функции getchar() следующим образом:

    #include int main(void) { int value1 = getchar(); int value2 = getchar(); printf("%c %c\n", value1, value2); return 0; }

    И после запуска этой программы введем с клавиатуры два символа: ds. В результате, оба символа помещаются во входной буфер, первый считывается при первом вызове функции getchar(), а второй – при втором вызове функции getchar(). Поэтому программа не ждет от нас ввода какой-либо дополнительной информации, а сразу переходит к функции printf(). Соответственно в переменной value1 будет храниться код символа d, а в переменной value2 – код символа s. Затем, функция printf() выводит на экран оба прочитанных символа. Вот наглядный эффект работы входного буфера. Мало того, если бы мы ввели не два, а, скажем, три символа, то после чтения первых двух, последний так бы и остался во входном буфере до момента завершения программы. При завершении, все буферы автоматически очищаются. На этом мы завершим вводное занятие по стандартным потокам ввода/вывода, а также по функциям getchar() и putchar(). На следующем занятии продолжим эту тему и поговорим о функции printf().

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *