и не имеет кириллических символов, символов иврита, арабского языка или хины не говоря уж о такой экзотике, как корейские, японские или китайские иероглифы Гораздо более гибким является стандарт Unicode, который включает ANSI-символ как свою начальную часть, а кроме них — массу других алфавитов, включая все пе речисленные выше. Unicode имеет несколько форматов, именуемых кодировками форматом по умолчанию для С# является UTF8.
Приведенная далее демонстрационная программа FileWrite считывает строки данных с консоли и записывает их в выбранный пользователем файл
// FileWrite - запись ввода с консоли в текстовый файл using System;
using System.10; // Требуется для работы с файлами namespace FileWrite
{
- public class Program
{
public static void Main(string [] args)
//Создание объекта для имени файла — цикл while
//позволяет пользователю продолжать попытки до тех
//пор, пока файл не будет успешно открыт StreamWriter sw = null;
string sFileName = ""; while(true)
{
try
{
// |
Ввод имени |
файла для вывода (просто Enter для |
// |
завершения |
программы) |
Console.Write("Введите имя файла " |
|
|
+ "(пустое имя для завершения):"); |
sFileName = Console.ReadLine(); |
if |
(sFileName.Length == 0) |
{ |
|
|
// Имени файла нет — выходим из цикла break;
}
//Открываем файл для записи; если файл уже
//существует, генерируем исключение:
//FileMode.CreateNew - для создания файла, если
//он еще не существует и генерации исключения при
//наличии такого файла; FileMode.Append для
//создания нового файла или добавления данных к
//существующему файлу; FileMode.Create для
//создания нового файла или урезания уже
//имеющегося до нулевого размера. Возможные
//варианты FileAccess: FileAccess.Read,
//FileAccess.Write, FileAccess.ReadWrite FileStream fs = File.Open(sFileName,
FileMode.CreateNew,
FileAccess.Write);
// Генерируем файловый поток с UTF8-символами (по
436 |
Часть VII. Дополнительные главы |
//умолчанию второй параметр дает UTF8, так что он
//может быть опущен)
sw = new StreamWriter(fs, System.Text.Encoding.UTF8);
// |
Считываем по |
одной строке, выводя |
каждую из них |
// |
в FileStream |
для записи |
|
|
|
Console.WriteLine("Введите |
текст |
" + |
|
|
|
"(пустую |
строку |
для |
выхода)"); |
while(true) |
|
|
|
|
{
//Считываем очередную строку с консоли; если
//строка пуста, завершаем цикл
string slnput = Console.ReadLine(); if (slnput.Length == 0)
{
break;
}
// Записываем считанную строку в файл вывода sw.WriteLine(slnput);
}
// Закрываем созданный файл sw.Close();
sw = null; // Желательно обнуление ссылочных // переменных после использования
}
catch(IOException fe)
{
//Произошла ошибка при работе с файлом — о ней
//надо сообщить пользователю вместе с полным
//именем файла
string sDir = Directory.GetCurrentDirectory(); string s = Path.Combine(sDir, sFileName); Console.WriteLine("Ошибка с файлом {О}", s ) ;
//Теперь выводим сообщение об ошибке из
//исключения
Console.WriteLine(fe.Message);
//Ожидаем подтверждения пользователя Console.WriteLine("Нажмите <Enter> для " +
"завершения программы...");
Console.Read();
Демонстрационная программа FileWrite использует пространства имен Sys tem. 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). Creat eNew создает новый файл, но генерирует исключение IOException, если такой файл уже существует. Простой режим Create создает файл, если он отсутствует, но если он есть, то просто перезаписывает его. И наконец, Append создает файл,
438 |
Часть VII. Дополнительные главы |
Подобие StreamWriter.WriteLine() больше, чем простое совпадение.
И наконец, файл закрывается с помощью вызова
если он не существует, но если он имеется, открывает его для дописывания ин формации в конец файла.
Тип доступа: файл может быть открыт для чтения, записи или для обеих операций.
Класс FileStream имеет ряд конструкторов, у каждого из которых один или оба аргумента, отвечающие за режим открытия и тип доступа, имеют значения по умолчанию. Однако, по моему скромному мнению, вы должны указывать эти аргументы явно, поскольку это существенно повышает понятность про граммы. Поверьте, это хороший совет — значения по умолчанию могут быть удобны для программиста, но не для того, кто будет читать его код.
В следующей строке программа "оборачивает" вновь открытый файловый объект
FileStream в объект StreamWriter. Класс StreamWriter служит оберткой для объекта FileStream, которая предоставляет набор методов для работы с текстом.
Такой вид "оборачивания" одного класса вокруг другого представляет собой полез ный программный шаблон проектирования— StreamWriter "оборачивается" (содержит ссылку) вокруг другого класса FileStream и расширяет интерфейс FileStream, добавляя некоторые мелкие удобства. Методы StreamWriter вызывают методы внутреннего объекта FileStream. Это — рассматривавшееся в главе 12, "Наследование", отношение СОДЕРЖИТ.
Первый аргумент конструктора StreamWriter— объект FileStream. Второй аргу мент указывает используемую кодировку. Кодировка по умолчанию — UTF8.
Вы не должны указывать кодировку при чтении файла. Дело в том, что StreamWriter записывает тип применяемой кодировки в первых трех байтах файла. StreamReader счи тывает эти три байта при открытии файла и определяет тип используемой кодировки. Сокры тие такого рода деталей представляет собой одно из преимуществ хорошей библиотеки.
Затем программа FileWrite начинает чтение строк, вводимых с консоли. Программа завершает работу при считывании пустой строки, но до этого она собирает все считанные строки и записывает их, используя метод WriteLine () класса StreamWriter.
и Console.WriteLine()—
sw. Close ().
Обратите внимание, что программа обнуляет ссылку sw по закрытии файла. Файловый объект становится бесполезен после того, как файл закрыт. Правила хорошего тона требуют обнулять ссылки после того, как они становятся недей ствительны, так, чтобы обращений к ним больше не было (если вы попытаетесь это сделать, то будет сгенерировано исключение).
Блок catch напоминает футбольного вратаря: он стоит здесь для того, чтобы ловить все исключения, которые могут быть сгенерированы в программе. Он выводит сообще ние об ошибке, включая имя вызвавшего ее файла. Однако выводится не просто имя файла, а его полное имя, включая путь к нему. Это делается посредством класса Direc tory, который позволяет получить текущий каталог и добавить его перед введенным именем файла с использованием метода 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.txt The file already exists.
Введите имя файла (пустое имя для завершения):TestFile2.txt Введите текст (пустую строку для выхода)
Я ошибся - мне надо было ввести имя файла TestFile2.
Введите имя файла (пустое имя для завершения):
Нажмите <Enter> для завершения программы...
Все отлично работает, пока некоторый текст вводится в файл TestFilel. txt. Но при попытке открыть файл TestFilel.txt заново программа выводит сообщение The file already exists (файл уже существует). Обратите внимание на полный путь к файлу, выводимый вместе с сообщением об ошибке. Если исправить ошибку и ввести имя TestFile2 . txt, все продолжает отлично работать.
Повышение скорости чтения с использованием StreamReader
Запись файла — дело стоящее, но совершенно бесполезное, если вы не мо жете позже прочесть записанное. Приведенная далее демонстрационная про грамма считывает текстовый файл, например, созданный демонстрационной программой FileWrite или с помощью Блокнота.
440 |
Часть VII. Дополнительные главы |
Глава : |