Книга: C# 4.0: полное руководство

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

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

Консольный ввод-вывод осуществляется с помощью стандартных потоков, представленных свойствами Console.In, Console.Out и Console.Error. Примеры консольного ввода-вывода были представлены еще в главе 2, поэтому он должен быть вам уже знаком. Как будет показано ниже, он обладает и рядом других дополнительных возможностей.

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

Чтение данных из потока ввода с консоли

Поток Console.In является экземпляром объекта класса TextReader, и поэтому для доступа к нему могут быть использованы методы и свойства, определенные в классе TextReader. Но для этой цели чаще все же используются методы, предоставляемые классом Console, в котором автоматически организуется чтение данных из потока Console.In. В классе Console определены три метода ввода. Два первых метода, Read() и ReadLine(), были доступны еще в версии .NET Framework 1.0. А третий метод, ReadKey(), был добавлен в версию 2.0 этой среды.

Для чтения одного символа служит приведенный ниже метод Read().

static int Read()

Метод Read() возвращает очередной символ, считанный с консоли. Он ожидает до тех пор, пока пользователь не нажмет клавишу, а затем возвращает результат. Возвращаемый символ относится к типу int и поэтому должен быть приведен к типу char. Если при вводе возникает ошибка, то метод Read() возвращает значение -1. Этот метод сгенерирует исключение IOException при неудачном исходе операции ввода. Ввод с консоли с помощью метода Read() буферизуется построчно, поэтому пользователь должен нажать клавишу <Enter>, прежде чем программа получит любой символ, введенный с консоли.

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

// Считать символ, введенный с клавиатуры.
using System;
class KbIn {
  static void Main() {
    char ch;
    Console.Write("Нажмите клавишу, а затем — <ENTER>: ");
    ch = (char) Console.Read(); // получить значение типа char
    Console.WriteLine("Вы нажали клавишу: " + ch) ;
  }
}

Вот, например, к какому результату может привести выполнение этой программы.

Нажмите клавишу, а затем — <ENTER>: t
Вы нажали клавишу: t

Необходимость буферизировать построчно ввод, осуществляемый с консоли посредством метода Read(), иногда может быть досадным препятствием. Ведь при нажатии клавиши <Enter> в поток ввода передается последовательность символов перевода каретки и перевода строки. Более того, эти символы остаются во входном буфере до тех пор, пока они не будут считаны. Следовательно, в некоторых приложениях приходится удалять эти символы (путем их считывания), прежде чем приступать к следующей операции ввода. Впрочем, для чтения введенных с клавиатуры символов без построчной буферизации, можно воспользоваться рассматриваемым далее методом ReadKey(). Для считывания строки символов служит приведенный ниже метод ReadLine().

static string ReadLine()

Символы считываются методом ReadLine() до тех пор, пока пользователь не нажмет клавишу <Enter>, а затем этот метод возвращает введенные символы в виде объекта типа string. Кроме того, он сгенерирует исключение IOException при неудачном исходе операции ввода.

Ниже приведен пример программы, в которой демонстрируется чтение строки из потока Console.In с помощью метода ReadLine().

// Ввод с консоли с помощью метода ReadLine().
using System;
class ReadString {
  static void Main() {
    string str;
    Console.WriteLine("Введите несколько символов.");
    str = Console.ReadLine();
    Console.WriteLine("Вы ввели: " + str);
  }
}

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

Введите несколько символов.
Это просто тест.
Вы ввели: Это просто тест.

Итак, для чтения данных из потока Console.In проще всего воспользоваться методами класса Console. Но для этой цели можно обратиться и к методам базового класса TextReader. В качестве примера ниже приведен переделанный вариант предыдущего примера программы, в котором используется метод ReadLine(), определенный в классе TextReader.

// Прочитать введенную с клавиатуры строку
// непосредственно из потока Console.In.
using System;
class ReadChars2 {
  static void Main() {
    string str;
    Console.WriteLine("Введите несколько символов.");
    str = Console.In.ReadLine(); // вызвать метод ReadLine()
          // класса TextReader
    Console.WriteLine("Вы ввели: " + str);
  }
}

Обратите внимание на то, что метод ReadLine() теперь вызывается непосредственно для потока Console.In. Поэтому если требуется доступ к методам, определенным в классе TextReader, который является базовым для потока Console.In, то подобные методы вызываются так, как было показано в приведенном выше примере.

Применение метода ReadKey()

В состав среды .NET Framework включен метод, определяемый в классе Console и позволяющий непосредственно считывать отдельно введенные с клавиатуры символы без построчной буферизации. Этот метод называется ReadKey(). При нажатии клавиши метод ReadKey() немедленно возвращает введенный с клавиатуры символ. И в этом случае пользователю уже не нужно нажимать дополнительно клавишу <Enter>. Таким образом, метод ReadKey() позволяет считывать и обрабатывать ввод с клавиатуры в реальном масштабе времени.

Ниже приведены две формы объявления метода ReadKey().

static ConsoleKeylnfo ReadKey()
static ConsoleKeylnfo ReadKey(bool intercept)

В первой форме данного метода ожидается нажатие клавиши. Когда оно происходит, метод возвращает введенный с клавиатуры символ и выводит его на экран. Во второй форме также ожидается нажатие клавиши, и затем возвращается введенный с клавиатуры символ. Но если значение параметра intercept равно true, то введенный символ не отображается. А если значение параметра intercept равно false, то введенный символ отображается.

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

char KeyChar
ConsoleKey Key
ConsoleModifiers Modifiers

Свойство KeyChar содержит эквивалент char введенного с клавиатуры символа, свойство Key — значение из перечисления ConsoleKey всех клавиш на клавиатуре, а свойство Modifiers — описание одной из модифицирующих клавиш (<Alt>, <Ctrl> или <Shift>), которые были нажаты, если это действительно имело место, при формировании ввода с клавиатуры. Эти модифицирующие клавиши представлены в перечислении ConsoleModifiers следующими значениями: Control, Shift и Alt. В свойстве Modifiers может присутствовать несколько значений нажатых модифицирующих клавиш.

Главное преимущество метода ReadKey() заключается в том, что он предоставляет средства для организации ввода с клавиатуры в диалоговом режиме, поскольку этот ввод не буферизуется построчно. Для того чтобы продемонстрировать данный метод в действии, ниже приведен соответствующий пример программы.

// Считать символы, введенные с консоли, используя метод ReadKey().
using System;
class ReadKeys {
  static void Main() {
    ConsoleKeyInfo keypress;
    Console.WriteLine("Введите несколько символов, " +
          "а по окончании - <Q>.");
    do {
      keypress = Console.ReadKey(); // считать данные о нажатых клавишах
      Console.WriteLine(" Вы нажали клавишу: " + keypress.KeyChar);
      // Проверить нажатие модифицирующих клавиш.
      if((ConsoleModifiers.Alt & keypress.Modifiers) != 0)
        Console.WriteLine("Нажата клавиша <Alt>.");
      if((ConsoleModifiers.Control & keypress.Modifiers) != 0)
        Console.WriteLine("Нажата клавиша <Control>.");
      if((ConsoleModifiers.Shift & keypress.Modifiers) != 0)
        Console.WriteLine("Нажата клавиша <Shift>.");
    } while(keypress.KeyChar != 'Q');
  }
}

Вот, например, к какому результату может привести выполнение этой программы.

Введите несколько символов, а по окончании - <Q>.
а Вы нажали клавишу: а
b Вы нажали клавишу: b
d Вы нажали клавишу: d
А Вы нажали клавишу: А
Нажата клавиша <Shift>.
В Вы нажали клавишу: В
Нажата клавиша <Shift>.
С Вы нажали клавишу: С
Нажата клавиша <Shift>.
• Вы нажали клавишу: •
Нажата клавиша <Control>.
Q Вы нажали клавишу: Q
Нажата клавиша <Shift>.

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

Запись данных в поток вывода на консоль

Потоки Console.Out и Console.Error являются объектами типа TextWriter. Вывод на консоль проще всего осуществить с помощью методов Write() и WriteLine(), с которыми вы уже знакомы. Существуют варианты этих методов для вывода данных каждого из встроенных типов. В классе Console определяются его собственные варианты метода Write() и WriteLine(), и поэтому они могут вызываться непосредственно для класса Console, как это было уже не раз показано на страницах данной книги. Но при желании эти и другие методы могут быть вызваны и для класса TextWriter, который является базовым для потоков Console.Out и Console.Error.

Ниже приведен пример программы, в котором демонстрируется вывод в потоки Console.Out и Console.Error. По умолчанию данные в обоих случаях выводятся на консоль.

// Организовать вывод в потоки Console.Out и Console.Error.
using System;
class ErrOut {
  static void Main() {
    int a=10, b=0;
    int result;
    Console.Out.WriteLine("Деление на нуль приведет " +
                 "к исключительной ситуации.");
    try {
      result = a / b; // сгенерировать исключение при попытке деления на нуль
    } catch(DivideByZeroException exc) {
      Console.Error.WriteLine(exc.Message);
    }
  }
}

При выполнении этой программы получается следующий результат.

Деление на нуль приведет к исключительной ситуации.
Попытка деления на нуль.

Начинающие программисты порой испытывают затруднения при использовании потока Console.Error. Перед ними невольно встает вопрос: если оба потока, Console.Out и Console.Error, по умолчанию выводят результат на консоль, то зачем нужны два разных потока вывода? Ответ на этот вопрос заключается в том, что стандартные потоки могут быть переадресованы на другие устройства. Так, поток Console.Error можно переадресовать в выходной файл на диске, а не на экран. Это, например, означает, что сорбщения об ошибках могут быть направлены в файл журнала регистрации, не мешая выводу на консоль. И наоборот, если вывод на консоль переадресуется, а вывод сообщений об ошибках остается прежним, то на консоли появятся сообщения об ошибках, а не выводимые на нее данные. Мы еще вернемся к вопросу переадресации после рассмотрения файлового ввода-вывода.

Оглавление книги


Генерация: 1.264. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз