1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 86
Текст из файла (страница 86)
10 и System. Пространство имен System. 10 содержит классы, предназначенныедня работы с файлами.Обратитесь к разделу справочной системы, посвященному пространству имен Sys tern.Text. Одним из его более полезных классов является StringBuilder, который предоставляет эффективный подход для работы со сложными строками, состоящими из нескольких частей. Это гораздо более эффективный способ работы,чем использование оператора + для конкатенации большого количества строк.Глава 19. Работа с файлами и библиотеками437Программа начинает работу с функции Main ( ) , которая включает цикл while содержащий try-блок. В этом нет ничего необычного для программ, работающих с файлами. (В следующем разделе, где описывается работа с классом StreamReader,используется немного другой подход, дающий те же результаты.)Все функции ввода-вывода вставлены в try-блок с перехватом, в котором генерируется соответствующее сообщение об ошибке. В этом надо быть очень аккуратным, так как файловый ввод-вывод является источником множества ошибоктаких как отсутствующие файлы и каталоги, неверные пути и тому подобное.Цикл while служит двум следующим целям.Он позволяет программе вернуться и повторить попытку в случае, если произошлаошибка ввода-вывода.
Например, если демонстрационная программа не можетнайти файл, который планирует читать пользователь, она может запросить у негоимя файла еще раз, а не просто оставить его с сообщением об ошибке.Команда break в программе переносит вас за try-блок, тем самым предоставляетудобный механизм для выхода из функции или программы. Не забывайте о том;что break работает только в пределах цикла, в котором вызвана эта команда,Демонстрационная программа FileWrite считывает имя создаваемого файла с комсоли. Программа прекращает работу путем выхода из цикла с помощью команды break,если пользователь вводит пустое имя файла.
Ключ к программе заключается в следующих строках:FileStream fs = File.Open(sFileName, FileMode.CreateNew,FileAccess.Write);/ / ...sw = new StreamWriter(fs, System.Text.Encoding.UTF8);В первой строке программа создает объект FileStream, который представляетфайл, записываемый на диск. Конструктор FileStream использует следующие три аргумента.Имя файла: это просто имя файла, который следует открыть.
Простое имя файланаподобие filename. txt предполагает, что файл находится в текущем каталоге(для демонстрационной программы FileWrite это подкаталог \bin\Debugnкаталоге проекта; словом, это каталог, в котором находится сам . ЕХЕ-файл). Имяфайла,начинающеесясобратнойкосойчертынаподобие\directory\filename.txt, рассматривается как полный путь на локальной машине.
Имяфайла,начинающееся с двух обратных косых черт (например,\\machine\directory\filename.txt), указывает файл, расположенный на другой машине в вашей сети. Кодировка файла — существенно более сложный вопрос, выходящий за рамки данной книги.Режим работы с файлом: этот аргумент определяет, что вы намерены делатьс файлом. Основными режимами работы с файлом для записи являются создание(CreateNew), добавление к файлу (Append) и перезапись (Create). CreateNew создает новый файл, но генерирует исключение IOException, если такойфайл уже существует. Простой режим Create создает файл, если он отсутствует,но если он есть, то просто перезаписывает его. И наконец, Append создает файл,438Часть VII. Дополнительные главыесли он не существует, но если он имеется, открывает его для дописывания информации в конец файла.Тип доступа: файл может быть открыт для чтения, записи или для обеих операций.Класс FileStream имеет ряд конструкторов, у каждого из которых один илиоба аргумента, отвечающие за режим открытия и тип доступа, имеют значенияпо умолчанию.
Однако, по моему скромному мнению, вы должны указыватьэти аргументы явно, поскольку это существенно повышает понятность программы. Поверьте, это хороший с о в е т — значения по умолчанию могут бытьудобны для программиста, но не для того, кто будет читать его код.В следующей строке программа "оборачивает" вновь открытый файловый объектFileStream в объект StreamWriter. Класс StreamWriter служит оберткой дляобъекта FileStream, которая предоставляет набор методов для работы с текстом.Такой вид "оборачивания" одного класса вокруг другого представляет собой полезный программный шаблон проектирования— StreamWriter "оборачивается"(содержит ссылку) вокруг другого класса FileStream и расширяет интерфейсFileStream, добавляя некоторые мелкие удобства. Методы StreamWriterвызывают методы внутреннего объекта FileStream.
Это — рассматривавшеесяв главе 12, "Наследование", отношение СОДЕРЖИТ.Первый аргумент конструктора StreamWriter— объект FileStream. Второй аргумент указывает используемую кодировку. Кодировка по умолчанию — UTF8.Вы не должны указывать кодировку при чтении файла. Дело в том, что StreamWriterзаписывает тип применяемой кодировки в первых трех байтах файла.
StreamReader считывает эти три байта при открытии файла и определяет тип используемой кодировки. Сокрытие такого рода деталей представляет собой одно из преимуществ хорошей библиотеки.Затем программа FileWrite начинает чтение строк, вводимых с консоли. Программазавершает работу при считывании пустой строки, но до этого она собирает все считанныестроки и записывает их, используя метод WriteLine () класса StreamWriter.ПодобиеStreamWriter.WriteLine()иConsole.WriteLine()—больше, чем простое совпадение.И наконец, файл закрывается с помощью вызова sw. Close ().Обратите внимание, что программа обнуляет ссылку sw по закрытии файла.Файловый объект становится бесполезен после того, как файл закрыт. Правилахорошего тона требуют обнулять ссылки после того, как они становятся недействительны, так, чтобы обращений к ним больше не было (если вы попытаетесьэто сделать, то будет сгенерировано исключение).Блок catch напоминает футбольного вратаря: он стоит здесь для того, чтобы ловитьвсе исключения, которые могут быть сгенерированы в программе.
Он выводит сообщение об ошибке, включая имя вызвавшего ее файла. Однако выводится не просто имяфайла, а его полное имя, включая путь к нему. Это делается посредством класса Directory, который позволяет получить текущий каталог и добавить его перед введеннымименем файла с использованием метода Path. Combine () (Path — класс, разработан-Глава 19. Работа с файлами и библиотеками439ный для работы с информацией о путях, a Directory предоставляет свойства и методадля работы с каталогами).Путь — это полное имя каталога. Например, если имя файла — с: \user\directory\!text. txt, то его путь — с : \user\directory.Метод Combine () достаточно интеллектуален, чтобы разобраться, что дляфайла наподобие c:\test.txt Path О не является текущим каталогомPath.
Combine () представляет также наиболее безопасный путь, гарантирующий корректное объединение двух частей пути, включая символ-разделитель (\)между ними. (В Windows символ-разделитель п у т и — \. Вы можете получитькорректный разделитель для операционной системы, под управлением которойзапущена программа, с помощью Path.DirectorySeparatorChar.
Библиотека .NET Framework изобилует такого рода возможностями, существенно облегчая программистам на С# написание программ, которые должны работать подуправлением нескольких операционных систем.)Достигнув конца цикла w h i l e — либо после выполнения try-блока, либо послеблока catch, — программа возвращается к началу цикла и позволяет пользователю записать другой файл.Вот как выглядит пример выполнения демонстрационной программы (пользовательскийввод выделен полужирным шрифтом).Введите имя файла (пустое имя для завершения):TestFilel.txtВведите текст (пустую,строку для выхода)Это какой-то текстИ ещеИ еще р а з . . .Введите имя файла (пустое имя для завершения):TestFilel.txtОшибка с файлом С:\C#Programs\FileWrite\bin\Debug\TestFilel.txtThe file already exists.Введите имя файла (пустое имя для завершения):TestFile2.txtВведите текст (пустую строку для выхода)Я ошибся - мне надо было ввестиимя файла TestFile2.Введите имя файла (пустое имя для з а в е р ш е н и я ) :Нажмите <Enter> для завершения программы...Все отлично работает, пока некоторый текст вводится в файл TestFilel.
txt. Нопри попытке открыть файл TestFilel.txt заново программа выводит сообщениеThefilealready exists (файл уже существует). Обратите внимание на полныйпуть к файлу, выводимый вместе с сообщением об ошибке. Если исправить ошибкуи ввести имя TestFile2 . txt, все продолжает отлично работать.Повышение скорости чтения с использованием StreamReaderЗапись файла — дело стоящее, но совершенно бесполезное, если вы не можете позже прочесть записанное.
Приведенная далее демонстрационная программа считывает текстовый файл, например, созданный демонстрационнойпрограммой FileWrite или с помощью Блокнота.440Часть VII. Дополнительные главыГлава :// FileRead - читает текстовый файл и выводит на консоль его// содержимоеusing System;using System. 10;namespace FileRead{public class Programpublic static void Main(string [] args){// Нам нужен объект для чтения файлаStreamReader sr;string sFileName = " ";// Пытаемся получить корректное имя файла до тех пор,// пока наконец его не получим (для выхода из// программы надо использовать комбинацию клавиш// <Ctrl+C>)while(true){try{// Ввод имени файлаConsole.Write("Введите имя текстового ф а й л а : " ) ;sFileName = Console.ReadLine();// Если пользователь ничего не ввел, генерируем// исключение для указания, что такой ввод// неприемлемif (sFileName.Length == 0){throw newIOException("Введено пустое имя ф а й л а " ) ;}// Открываем файловый поток для чтения; если файл// не существует - не создаем егоFileStream fs = File.Open(sFileName,F i1eMode.Open,FileAccess.Read);// Преобразуем в StreamReader - этот класс// использует первые три байта файла для// определения использованной кодировки (но не для// языка)sr = new StreamReader(fs, t r u e ) ;break;}// Сообщение об ошибке с указанием имени файлаcatch(IOException fe){Console.WriteLine("{0}\n\n", fe.Message);}}// Чтение содержимого файлаConsole.WriteLine("\пСодержимое файла:") ;tryГлава 19.