Потоки 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 можно переадресовать в выходной файл на диске, а не на экран. Это,например, означает, что сообщения об ошибках могут быть направлены в файл журнала регистрации, не мешая выводу на консоль. И наоборот, если вывод на консоль переадресуется, а вывод сообщений об ошибках остается прежним, то на консоли появятсясообщения об ошибках, а не выводимые на нее данные. Мы еще вернемся к вопросупереадресации после рассмотрения файлового ввода-вывода.Класс FileStream и байтовый ввод-вывод в файл
В среде .NET Framework предусмотрены классы для организации ввода-выводав файлы. Безусловно, это в основном файлы дискового типа. На уровне операционнойсистемы файлы имеют байтовую организацию. И, как следовало ожидать, для вводаи вывода байтов в файлы имеются соответствующие методы. Поэтому ввод и выводв файлы байтовыми потоками весьма распространен. Кроме того, байтовый поток ввода или вывода в файл может быть заключен в соответствующий объект символьногопотока. Операции символьного ввода-вывода в файл находят применение при обработке текста. О символьных потоках речь пойдет далее в этой главе, а здесь рассматривается байтовый ввод-вывод.
Для создания байтового потока, привязанного к файлу, служит класс FileStream.Этот класс является производным от класса Stream и наследует всего его функции.
Напомним, что классы потоков, в том числе и FileStream, определены в пространстве имен System.IO. Поэтому в самом начале любой использующей их программыобычно вводится следующая строка кода.using System.IO;Открытие и закрытие файла
Для формирования байтового потока, привязанного к файлу, создается объекткласса FileStream. В этом классе определено несколько конструкторов. Ниже приведен едва ли не самый распространенный среди них:FileStream(string путь, FileMode режим)
где путь обозначает имя открываемого файла, включая полный путь к нему; а режим — порядок открытия файла. В последнем случае указывается одно из значений,определяемых в перечислении FileMode и приведенных в табл. 14.4. Как правило,этот конструктор открывает файл для доступа с целью чтения или записи. Исключением из этого правила служит открытие файла в режиме FileMode.Append, когдафайл становится доступным только для записи.
Таблица 14.4. Значения из перечисления FileModeЗначениеОписаниеFileMode.AppendДобавляет выводимые данные в конец файлаFileMode.CreateСоздает новый выходной файл. Существующий файл с таким же именем будет разрушенFileMode.CreateNewСоздает новый выходной файл. Файл с таким же именем не должен существоватьFileMode.OpenОткрывает существующий файлFileMode.OpenOrCreateОткрывает файл, если он существует. В противном случае создает новый файлFileMode.TruncateОткрывает существующий файл, но сокращает его длину до нуля
Если попытка открыть файл оказывается неудачной, то генерируется исключение. Если же файл нельзя открыть из-за того что он не существует, генерируетсяисключение FileNotFoundException. А если файл нельзя открыть из-за какой-нибудь ошибки ввода-вывода, то генерируется исключение IOException. К числу других исключений, которые могут быть сгенерированы при открытии файла, относятся следующие: ArgumentNullException (указано пустое имя файла),ArgumentException (указано неверное имя файла), ArgumentOutOfRangeException(указан неверный режим), SeaurityException (у пользователя нет прав доступак файлу), PathTooLongException (слишком длинное имя файла или путь к нему),NotSupportedException (в имени файла указано устройство, которое не поддерживается), а также DirectoryNotFoundException (указан неверный каталог).
Исключения PathTooLongException, DirectoryNotFoundExceptionи FileNotFoundException относятся к подклассам класса исключений IOException.Поэтому все они могут быть перехвачены, если перехватывается исключениеIOException.
Ниже в качестве примера приведен один из способов открытия файла test.datдля ввода.FileStream fin;try { fin = new FileStream("test", FileMode.Open);}catch(IOException exc) { // перехватить все исключения, связанные с вводом-выводом Console.WriteLine(exc.Message); // Обработать ошибку.}catch(Exception exc { // перехватить любое другое исключение. Console.WriteLine(exc.Message); // Обработать ошибку, если это возможно. // Еще раз сгенерировать необрабатываемые исключения.}
В первом блоке catch из данного примера обрабатываются ошибки, возникающиев том случае, если файл не найден, путь к нему слишком длинен, каталог не существует, а также другие ошибки ввода-вывода. Во втором блоке catch, который является"универсальным" для всех остальных типов исключений, обрабатываются другие вероятные ошибки (возможно, даже путем повторного генерирования исключения). Крометого, каждую ошибку можно проверять отдельно, уведомляя более подробно о нейи принимая конкретные меры по ее исправлению.
Ради простоты в примерах, представленных в этой книге, перехватывается только исключение IOException, но в реальной программе, скорее всего, потребуется перехватывать и другие вероятные исключения, связанные с вводом-выводом, в зависимости отобстоятельств. Кроме того, в обработчиках исключений, приводимых в качестве примерав этой главе, просто уведомляется об ошибке, но зачастую в них должны быть запрограммированы конкретные меры по исправлению ошибок, если это вообще возможно. Например, можно предложить пользователю еще раз ввести имя файла, если указанный ранеефайл не был найден. Возможно, также потребуется сгенерировать исключение повторно.
Как упоминалось выше, конструктор класса FileStream открывает файл, доступныйдля чтения или записи. Если же требуется ограничить доступ к файлу только для чтенияили же только для записи, то в таком случае следует использовать такой конструктор.FileStream(string путь, FileMode режим, FileAccess доступ)
Как и прежде, путь обозначает имя открываемого файла, включая и полный путьк нему, а режим — порядок открытия файла. В то же время доступ обозначает конкретный способ доступа к файлу. В последнем случае указывается одно из значений,определяемых в перечислении FileAccess и приведенных ниже.FileAccess.Read FileAccess.Write FileAccess.ReadWrite
Например, в следующем примере кода файл test.dat открывается только длячтения.FileStream fin = new FileStream("test.dat", FileMode.Open, FileAccess.Read);