1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 70
Текст из файла (страница 70)
По умолчаниюконсольный ввод данных буферизован (с ориентацией на строки), поэтому, прежде чемвведенный с клавиатуры символ будет послан программе, необходимо нажать клавишу<Enter>.Рассмотрим пример программы, которая считывает символ с клавиатуры с помощьюметода Read().// Считывание символа с клавиатуры.using System;class KbIn {public static void Main() {char ch;380Часть I.
Язык C#}}Console.Write("Нажмите любую клавишу, а затем -- <ENTER>: ");ch = (char) Console.Read();// Считывание// char-значения.Console.WriteLine("Вы нажали клавишу: " + ch);Вот как могут выглядеть результаты выполнения этой программы:Нажмите любую клавишу, а затем — <ENTER>: юВы нажали клавишу: юТот факт, что метод Read() буферизирует строки, является порой источникомдосадных недоразумений. При нажатии клавиши <Enter> во входной поток вводитсяпоследовательность, состоящая из символов возврата каретки и перевода строки. Болеетого, эти символы остаются во входном потоке до тех пор, пока вы их не прочитаете.
Такимобразом, в некоторых приложениях перед выполнением следующей операции ввода ихнужно удалить (просто считыванием из потока).Чтобы прочитать строку символов, используйте метод ReadLine().static string ReadLine()Метод ReadLine() считывает символы до тех пор, пока не будет нажата клавиша<Enter>, и возвращает объект типа string. При неудачном завершении метод генерируетисключение типа IOException.Рассмотрим программу, которая демонстрирует считывание строки из потокаConsole.In с помощью метода ReadLine().// Ввод данных с консоли с помощью метода ReadLine().using System;class ReadString {public static void Main() {string str;Console.WriteLine("Введите несколько символов.");}}str = Console.ReadLine();Console.WriteLine("Вы ввели: " + str);Вот такие результаты можно получить при выполнении этой программы.Введите несколько символов.Это всего лишь тест.Вы ввели: Это всего лишь тест.Несмотря на то что методы класса Console являются самым простым способомсчитывания данных из потока Console.In, можно с таким же успехом вызывать методыбазового класса TextReader.
Например, перепишем предыдущую программу сиспользованием методов, определенных в классе TextReader.// Считывание строки с клавиатуры благодаря// непосредственному использованию свойства Console.In.using System;class ReadChars2 {public static void Main() {string str;Глава 14. Использование средств ввода-вывода381}}Console.WriteLine("Введите несколько символов.");str = Console.In.ReadLine();Console.WriteLine("Вы ввели: " + str);Обратите внимание на то, что метод ReadLine() теперь напрямую вызывается дляпотока Console.In. Здесь важно то, что при необходимости доступа к методам,определенным в классе TextReader, который является базовым для объектаConsole.In, их можно вызывать так, как показано в этом примере.Запись данных в консольный выходной потокПотоки 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 {public 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);}При выполнении этой программы получим следующие результаты:Деление на нуль сгенерирует исключение.Attempted to divide by zero.Иногда неопытные программисты путаются, не зная точно, когда следуетиспользовать поток Console.Error.
Если и Console.Out, и Console.Error поумолчанию выводят данные на консоль, то почему существует два различных потока?Ответ прост: стандартные потоки можно перенаправить на другие устройства. Например,поток Console.Error можно перенаправить для записи данных в дисковый файл, а не наэкран. Следовательно, и поток ошибок можно перенаправить, например, в системныйжурнал (log file), не затрагивая при этом консольный вывод.
И наоборот, если консольныйвывод данных перенаправить в файл, а вывод ошибок — нет, то сообщения382Часть I. Язык C#об ошибках будут появляться на консольном устройстве, и их легко увидеть. Но подробнеео перенаправлении потоков мы поговорим ниже, когда рассмотрим файловый ввод-вывод.Класс FileStream и файловый ввод-вывод напобайтовой основеВ C# предусмотрены классы, которые позволяют считывать содержимое файлов изаписывать в них информацию. Конечно же, дисковые файлы — самый распространенныйтип файлов. На уровне операционной системы все файлы обрабатываются на побайтовойоснове. Нетрудно предположить, что в C# определены методы, предназначенные длясчитывания байтов из файла и записи байтов в файл.
Таким образом, файловые операциичтения и записи с использованием байтовых потоков очень востребованы. C# такжепозволяет поместить файловый поток с ориентацией на побайтовую обработку всимвольный объект. Файловые операции, ориентированные на символы, используются вслучае текстовых файлов. Символьные потоки рассматриваются ниже в этой главе. А покаизучим ввод-вывод данных на побайтовой основе.Чтобы создать байтовый поток с привязкой к файлу, используйте классFileStream. Класс FileStream — производный от Stream и потому обладаетфункциональными возможностями базового класса. Помните, что потоковые классы,включая FileStream, определены в пространстве имен System.IO. Следовательно, приих использовании в начало программы вы должны включить следующую инструкцию:using System.IO;Как открыть и закрыть файлЧтобы создать байтовый поток, связанный с файлом, создайте объект классаFileStream.
В классе FileStream определено несколько конструкторов. Чаще всего изних используется следующий:FileStream(string filename, FileMode mode)Здесь элемент filename означает имя файла, который необходимо открыть, причемоно может включать полный путь к файлу. Элемент mode означает, как именно долженбыть открыт этот файл, или режим открытия. Элемент mode может принимать одно иззначений, определенных перечислением FileMode (они описаны в табл. 14.4).
Этотконструктор открывает файл для доступа с разрешением чтения и записи.Таблица 14.4. Значения перечисления FileModeЗначениеОписаниеFileMode.AppendFileMode.CreateДобавляет выходные данные в конец файлаСоздает новый выходной файл. Существующий файл с такимже именем будет разрушенСоздает новый выходной файл. Файл с таким же именем недолжен существоватьОткрывает существующий файлОткрывает файл, если он существует, В противном случаесоздает новыйОткрывает существующий файл, но усекает его длину до нуляFileMode.CreateNewFileMode.OpenFileMode.OpenOrCreateFileMode.TruncateГлава 14.
Использование средств ввода-вывода383Если попытка открыть файл оказалось неуспешной, генерируется исключение. Еслифайл невозможно открыть по причине его отсутствия, генерируется исключение типаFileNotFoundException. Если файл невозможно открыть из-за ошибки ввода-вывода,генерируется исключение типа IOException. Возможны также исключения следующихтипов: ArgumentNullException (если имя файла представляет собой null-значение),ArgumentException (если некорректен параметр mode), SecurityException (еслипользователь не обладает правами доступа) и DirectoryNotFoundException (еслинекорректно задан каталог).В следующем фрагменте программы показан один из способов открыть файлtest.dat для ввода данных.FileStream fin;try {fin = new FileStream("test.dat", FileMode.Open);}catch(FileNotFoundException exc) {Console.WriteLine(exc.Message);return;}catch {Console.WriteLine("Не удается открыть файл.");return;}Здесь первая catch-инструкция перехватывает ошибку, связанную с отсутствиемфайла.
Вторая, предназначенная для “всеобщего перехвата”, обрабатывает другие ошибки,которые возможны при работе с файлами. Конечно, можно было бы отслеживатьвозникновение каждой ошибки в отдельности, сообщая о возникшей проблеме. Но радипростоты во всех примерах этой книги организован перехват исключений только типаFileNotFoundException или IOException, но в реальных приложениях (взависимости от обстоятельств) вам, скорее всего, придется обрабатывать другие возможныеисключения.Как уже упоминалось, приведенный выше конструктор FileStream открываетфайл с доступом для чтения и записи. Если необходимо ограничить доступ только чтениемили только записью, используйте следующий конструктор:FileStream(string filename, FileMode mode,FileAccess how)Как и прежде, элемент filename означает имя открываемого файла, a mode —способ его открытия.