Воскресенье, 28.04.2024, 13:55
Приветствую Вас Гость | RSS

Лекции

Меню сайта
Форма входа
Категории раздела
ТАУ (Теория автоматического управления) [31]
лекции по ТАУ
Экология [151]
учебник
Бухгалтерский учет и налогообложение в строительстве [56]
Дементьев А.Ю. - Практическое пособие
Психология [104]
Пип
информатика [80]
с# Карли Ватсон
современные стулья [0]
новинки
Поиск

Главная » 2010 » Февраль » 11 » Работа с файлами
00:44
Работа с файлами
Работа с файлами
В этой главе вы узнаете, каким образом можно осуществлять считывание и за-
пись текстовых файлов, что оказывается весьма существенным для многих прило-
жений .NET. Мы будем обсуждать основные классы, которые используются для
создания файлов, чтения из файлов и записи в них, а также вспомогательные
классы, которые используются для выполнения различных манипуляций над фай-
ловой системой из программ на С#. Хотя у нас отсутствует возможность подробно
описать все классы, наше рассмотрение окажется достаточно глубоким для того,
чтобы вы получили хорошее представление об основных понятиях и принципах.
Файлы могут оказаться очень удобным средством сохранения информации при
работе с несколькими вариантами приложения, они могут также использоваться
для обмена данными между приложениями. Конфигурации пользователя и прило-
жения также могут храниться в файлах, для того чтобы впоследствии извлекаться
при очередном запуске приложения. Использование файлов может быть настолько
разнообразным, насколько разными могут быть сами приложения. В .NET Framework
предусмотрено наличие инструментов, позволяющих эффективно использовать
файлы в приложениях.
В этой главе вы узнаете:
• Что такое поток, и каким образом в .NET используются классы
для работы с потоками для доступа к файлам
• Как можно использовать объект File
для выполнения манипуляций над структурой файла
• Как осуществляется запись в файл
• Как осуществляется чтение из файла
• Как осуществляется чтение из файлов
и запись в файлы отформатированных данных
• Как можно осуществлять доступ к файлам синхронно (ко всем сразу)
и асинхронно (по группам)
• Каким образом можно отслеживать изменения
в файлах и директориях
Любой ввод и вывод информации в .NET Framework включает в себя использо-
вание потоков — абстрактных представлений последовательного устройства. По-
следовательное устройство — это нечто, обеспечивающее как последовательное
Работа с файлами 557
хранение данных, так и последовательный доступ к ним — по одному байту в каж-
дый конкретный момент времени. В качестве такого устройства могут выступать
расположенный на диске файл, принтер, область памяти, а также любой другой
объект, который допускает последовательное считывание и запись информации.
Рассматривая такое устройство как абстрактное, мы можем скрыть назначение/
источник потока. Такой уровень абстракции делает возможным многократное ис-
пользование кода и позволяет создавать подпрограммы более общего назначения.
Таким образом, аналогичный код может передаваться и повторно использоваться,
когда приложение считывает данные из файлового входного потока и когда оно
считывает информацию из сетевого входного потока. Кроме того, используя поня-
тие потока, мы получаем возможность абстрагироваться от физических особенно-
стей конкретного устройства. Поэтому при считывании информации из файлового
потока нам не приходится волноваться по поводу работы головок дискового
устройства или заниматься вопросами распределения памяти.
Выходной поток используется, когда данные записываются на некое внешнее
устройство. Это может быть файл на физическом диске, сетевой сервер, принтер
или какая-либо другая программа. Понимание программирования потоков откры-
вает множество дополнительных возможностей. В этой главе мы ограничим об-
суждение рамками записи в файлы на диске.
Входной поток используется для считывания данных в память или в перемен-
ные, к которым наша программа имеет доступ. Наиболее распространенной фор-
мой входного потока, с которой нам приходилось работать, является клавиатура.
Входной поток может поступать практически от любого устройства, но мы сосре-
доточимся на чтении дисковых файлов. Понятия, применимые ддя чтения/записи
дисковых файлов, оказываются применимыми практически для всех остальных
устройств, что позволяет получить общее представление о потоках и познакомить-
ся с фундаментальным подходом, который может быть использован в самых раз-
личных ситуациях.
Классы для ввода и вывода
Пространство имен system, ю содержит в себе все классы, которые будут рас-
сматриваться в этой главе. В пространстве имен system, ю хранятся классы, пред-
назначенные для считывания данных из файлов и записи данных в файлы; для того
чтобы получить в программе доступ к этим классам, необходимо сослаться на это
пространство имен. Как можно увидеть на следующей диаграмме, в пространстве
имен содержится не так уж много классов, но мы будем рассматривать только
классы, являющиеся основными при вводе и выводе файлов:
• File — широко используемый на практике класс, который обладает
большим количеством статических методов, позволяющих переносить,
копировать и удалять файлы.
• Directory — класс, который обладает большим количеством
статических методов, позволяющих переносить, копировать и удалять
директории.
• Path — класс, позволяющий выполнять манипуляции над именами путей.
• Fileinfo — представляет физический файл, расположенный на диске,
и обладает методами, позволяющими выполнять манипуляции
над этим файлом. Для любых операций чтения/записи, выполняемых
над файлом, необходимо создавать объект stream.
558 Глава 20
Marshall By RefObject
(System)
Stream
(System.lO)
FileStream
(System.lO)
Object
(System)
Directory
(System.lO)
File
(System.lO)
Path
(System.lO)
FileSystemlnfo
(System.lO)
Component
(System)
Filelnfo
(System.lO)
Directory! nfo
(System.lO)
FileSystemWatcher
(System.lO)
TextReader
(System.lO)
A
TextWriter
(System.lO)
StreamReader
(System.lO)
StreamWriter
(System.lO)
• Directoryinf о — представляет физическую директорию, расположенную
на диске, и обладает методами, позволяющими выполнять манипуляции
над этой директорией.
• FileStream — представляет файл, который допускает либо считывание,
либо запись, либо и то, и другое одновременно. Этот файл может
считываться и записываться как асинхронно, так и синхронно.
Оба способа будут рассматриваться в настоящей главе.
• StreamReader — считывает символьную информацию из потока
И МОЖет создаваться на базе Класса FileStream.
• StreamWriter — записывает символьную информацию в поток
и может создаваться на базе класса FileStream.
• FileSystemWatcher — это один из наиболее сложных классов,
с которыми предстоит познакомиться в этой главе. Он используется
для слежения за состоянием файлов и директорий и генерирует события
в моменты, когда изменяется их местоположение, что позволяет
перехватывать их внутри приложения. Такая функциональная возможность
отсутствовала при программировании в среде Windows, но теперь,
с появлением .NET Framework, реагировать на события, происходящие
в файловой системе, становится намного проще.
Классы File и Directory
Оба класса предоставляют большое количество методов для осуществления ма-
нипуляций над самой файловой системой, а также над файлами и директориями,
которые хранятся в этой файловой системе. Эти методы являются статическими
и позволяют перемещать файлы, запрашивать и вносить изменения в атрибуты,
Работа с файлами 559
а также создавать объекты Fiiestream. Как мы
узнали в главе 8, статические методы могут
вызываться классами без создания их экземп-
ляров.
Некоторые из наиболее полезных статиче-
ских методов класса File приведены в таблице
справа.
В следующей ниже таблице представлены
некоторые полезные статические методы класса
Directory!
Метод Описание
Сору () Копирует файл в указанное место.
Create О Создает файл в соответствии
с заданным путем.
Delete () Уничтожает файл.
Open() Возвращает объект FileStream
по заданному пути.
Move () Перемещает файл в новое место.
При этом имеется возможность
задать для нового файла другое имя.
Метод Описание
CreateDirectory()
Delete()
GetDirector,ies ()
GetFilesO
Move()
Класс Fllelnfo
В отличие от класса File, класс
Fileinfo не обладает статическими
методами и может использоваться
только посредством создания экзем-
пляров объектов. Объект Fileinfo
представляет файл, расположен-
ный на диске или по некоторому
сетевому адресу. Заметьте, что этот
класс не является потоком. Для
того чтобы осуществить запись
в файл или чтение из файла, необ-
ходимо СОЗДаТЬ объект Stream.
Объект Fileinfo помогает решить
эту задачу, предоставляя несколько методов, которые возвращают созданные эк-
земпляры объектов stream. Однако прежде всего, для того чтобы создать объект
Fileinfo, необходимо указать путь к файлу или директории:
Fileinfo aFile = new Fileinfo(*C:/log.txt^O;
Многие методы, предоставляемые классом Fileinfo, аналогичны соответствую-
щим методам класса File, однако, поскольку класс File является статическим
классом, он требует задания строкового параметра с местоположением файла при
каждом вызове метода. Отсюда следует, что следующие вызовы выполняют одни
и те же действия:
Создает директорию в соответствии
с заданным путем.
Уничтожает указанную директорию
вместе со всеми находящимися в ней
файлами.
Возвращает массив объектов Directory,
в котором представлены все директории,
находящиеся в данной директории.
Возвращает массив объектов F i l e
для данной директории.
Переносит указанную директорию
на новое мевто, где этой папке может
быть присвоено новое имя.
Fileinfo aFile = new Fileinfo(Data.txt"
if (aFile.Exists)
Console.WriteLine(*File Exists");
if (File.ExistsCData.txt*))
Console.WriteLineCFile Exists") ;
/ Файл существует /
/ Файл существует /
Большинство методов объекта Fileinfo представляют в этом смысле зеркаль-
ное отражение методов объекта File. Имеет прямой смысл использовать статиче-
ский класс File, когда требуется осуществить единственный вызов метода на
объект. В этом случае вызов будет выполнен быстрее, поскольку .NET Framework
не придется проходить через процедуру создания экземпляра нового объекта с по-
следующим вызовом метода. Однако если приложение осуществляет несколько
операций над файлом, то более разумным представляется создать экземпляр объ-
екта Fileinfo и использовать его методы. Это позволит сэкономить определенное
560 Глава 20
Свойство Описание
Attributes Позволяет получать или задавать
значения атрибутов текущего файла.
CreationTime Позволяет получать дату и время
создания текущего файла.
DirectoryName Возвращает путь к директории,
в которой находится файл.
Exists
FullName
Length
Name
Определяет, существует ли данный файл.
Позволяет получать полный путь
к данному файлу.
Позволяет получать размер файла.
Возвращает только имя самого файла —
без полного пути, ведущего
к его местоположению.
время, поскольку объект будет заранее
настроен на нужный файл в файловой
системе, в то время как статическому
классу придется каждый раз осуществ-
лять его поиск заново.
Класс Fileinfo обладает перечис-
ленными в таблице слева свойствами,
относящимися к данному файлу, осуще-
ствление манипуляций над которыми
позволяет вносить изменения в файл.
Класс Directorylnfo
Класс Directorylnfo работает в точ-
ности так же, Как И КЛаСС Fileinfo. Он
требует создания экземпляра объекта,
который представляет собой одну из
директорий, имеющихся в компьютере. Как и для класса Fileinfo, многие методы
ДублируЮТСЯ В Классах Directory И Directorylnfo. Аналогичны И указания ОТНОСИ-
тельно того, когда какой из них следует использовать: при единственном вызове
следует использовать статический класс Directory. При осуществлении последова-
тельности ВЫЗОВОВ нужно ИСПОЛЬЗОВать Экземпляр объекта Directorylnfo.
Имена пути и относительные пути
При задании имени пути в программе для .NET имеется возможность использо-
вать либо абсолютное, либо относительное имя пути. Абсолютное имя пути явным
образом указывает на расположение файла или директории в каком-либо извест-
ном месте, например, на диске С:. Вот пример такого пути: C:\work\LogFiie.txt.
Обратите внимание на то, что в данном случае местоположение описывается точ-
но, без какой-либо двусмысленности.
Относительные пути указывают на местоположение относительно того места
в файловой системе, в котором выполняется приложение. При использовании от-
носительного пути не требуется указывать ни имени диска, ни какого-либо извест-
ного места; в качестве отправной точки используется текущая директория.
Например, еСЛИ Приложение ВЫПОЛНЯетСЯ В Директории C:\Development\FileDemo,
а в качестве относительного пути используется "LogFile.txt", то это будет файл
C:\Development\FileDemo\LogFile.txt. Для перехода К директории более ВЫСОКОГО
уровня используется две точки (..). Следовательно, для того же самого приложе-
ния путь -. .\Log.txt* будет указывать на файл с именем Log.txt, расположенный
В Директории C:\Development.
Наиболее коварным при использовании относительных путей в процессе разра-
ботки является именно их относительность, зависящая от того, где именно выпол-
няется данное приложение. Если разработка ведется с использованием Visual
Studio.NET, то это означает, что приложение находится на несколько уровней'
ниже создаваемой вами папки проекта: Обычно приложения располагаются в ди-
ректории ProjectName\bin\Debug.
Объект FileStream
Объект FileStream представляет поток, указывающий на какой-либо файл на
диске или на местоположение в сети. Хотя этот класс и обладает методами для
чтения из файлов и записи в файлы байтов, чаще для выполнения этих функций
Работа с файлами 561
Элементы
перечислимого
типа FileMode
вам придется Обращаться К классам StreamReader И StreamWriter. Это ПРОИСХОДИТ
потому, что класс Fiiestream оперирует байтами и массивами байтов, в то время
как классы stream оперируют символьными данными. Иметь дело с символьными
данными оказывается проще, однако нам придется столкнуться с некоторыми опе-
рациями, например, с произвольным доступом к файлам, которые могут осуществ-
ляться только посредством объекта Fiiestream, к изучению которого мы
приступим несколько позже.
Существует несколько способов создания объекта Fiiestream. Его конструктор
обладает большим количеством перегрузок/версий, однако в самом простом слу-
чае он требует передачи двух аргументов — имени файла и значения перечислимо-
го типа FileMode:
FileStream aFile = new Fi1eStream("Log.txt", FileMode.OpenOrCreate);
У перечислимого типа FileMode имеется несколько членов, которые позволяют
задавать то, каким образом файл открывается или создается. Допускается их ис-
пользование в различных сочетаниях.
Описание
Append Открывает файл (если он существует), переводит указатель файла в его конец
или создает новый файл. FileMode. Append может использоваться только
совместно с членом перечислимого типа FileAccess.Write.
Create Создает новый файл; если файл с таким именем уже существует,
то он уничтожается.
CreateNew Создает новый файл; если файл с таким именем уже существует,
то будет сгенерирована исключительная ситуация.
open Открывает существующий файл. Если указанного файла не существует,
генерируется исключительная ситуация.
OpenOrCreate Определяет, что если такой файл существует, то он должен быть открыт,
в противном случае создается новый файл. Если такой файл существует,
вся информация в нем сохраняется.
Truncate Открывает существующий файл, а вся информация, которая в нем хранилась,
уничтожается. После этого мы получаем возможность записать в файл
абсолютно новую информацию, однако при этом дата создания файла остается
прежней. Этот файл должен существовать, иначе будет сгенерирована
исключительная ситуация.
Приведенный выше пример конструктора по умолчанию открывает файл в ре-
жиме чтение/запись. Для того чтобы задать другой уровень доступа, следует ис-
пользовать дополнительный параметр — FileAccess:
FileStream aFile new FileStream("Log.txt", FileMode.OpenOrCreate,
FileAccessroWrite);
Эта строка кода откроет файл с разрешением доступа на запись. Любая попытка
прочитать содержимое этого файла приведет к возникновению исключительной
ситуации. Перечислимый тип FileAccess обладает всего тремя типами: Read, write
и Readwrite. Следовательно, есть возможность открыть файл только на чтение,
только на запись и на чтение и запись одновременно. Это свойство используется
в качестве одного из способов варьирования уровня доступа пользователя к файлу
в зависимости от уровня его прав.
562 Глава 20
Оба Класса File И Filelnfo Предоставляют методы OpenReadO И OpenWriteO,
которые облегчают создание объектов Filestream. Первый открывает метод с ре-
жимом доступа "только чтение", а второй, кроме того, разрешает осуществлять
запись. Это позволяет упростить обращение к файлу, так что вам теперь не при-
дется указывать всю информацию, как в предыдущих примерах. Например, следу-
ющая строка открывает файл Data.txt в режиме "только чтение":
FileStream aFile • File.OpenRead('Data.txt");
Обратите внимание на то, что следующий код выполняет те же самые действия:
Filelnfo aFile • new Filelnfo("Data.txt");
FileStream aFile = aFile OpenReadO;
Позиция внутри файла
Класс FileStream поддерживает внутренний указатель файла, ссылающийся на
то место в файле, в котором будет производиться очередная операция чтения или
записи. В большинстве случаев при открытии файла указатель устанавливается
в начало файла, однако такое поведение указателя может быть изменено, что по-
зволяет приложению осуществлять чтение или запись в любой точке файла. Мож-
но также организовать произвольный доступ к файлу и осуществлять поиск
конкретной позиции в файле. Такой подход позволяет сэкономить массу времени
при работе с очень большими файлами, поскольку можно постоянно отыскивать
необходимую позицию.
Метод, который реализует данную функциональную возможность, носит назва-
ние Seek():
public long Seek(long offset, SeekOrigin origin);
Первый параметр определяет, насколько далеко вперед в байтах должен быть
передвинут указатель. Второй параметр определяет, с какой точки вести отсчет.
ПеречИСЛИМЫЙ ТИП SeekOrigin СОСТОИТ ИЗ Трех значений: Begin (начало), Current
(текущая позиция) и End (конец). Следующая строка передвигает указатель файла
на восьмой байт, считая с самого первого байта данного файла:
aFile.Seek(8, SeekOrigin.Begin);
Следующая строка кода приведет к перемещению указателя файла на два байта
вперед относительно его текущей позиции. Если эта строка будет выполнена непо-
средственно сразу после предыдущей строки, то указатель будет ссылаться на де-
сятый байт файла:
aFile.Seek(2, SeekOrigin.Current);
Следует обратить внимание на то, что как при чтении из файла, так и при запи-
си в файл происходит изменение позиции указателя файла. В случае считывания,
например, десяти байтов, указатель файла будет ссылаться на байт, расположен-
ный после десятого считанного байта.
Существует также возможность указывать отрицательные значения для опреде-
ления требуемой позиции, что в сочетании с использованием значения перечисли-
мого типа SeekOrigin.End может быть использовано для поиска необходимой
позиции вблизи конца файла. Следующая строка позволит перейти к пятому с кон-
ца байту файла:
aFile.Seek(-5, SeekOrigin.End); .
Работа с файлами 563
Файлы, доступ к которым может осуществляться подобным образом, зачастую
называют файлами с произвольным доступом, поскольку у приложения имеется
возможность получить доступ к любой позиции в файле. Классы stream позволяют
осуществлять последовательный доступ к файлам, и в них не предусмотрена воз-
можность работы с указателем.
Чтение данных
Чтение данных с использованием класса Fiiestream оказывается не таким про-
стым, как С использованием классов StreamReader И StreamWriter, К рассмотрению
которых мы вернемся ниже в данной главе.' Это происходит потому, что класс
Fiiestream имеет дело исключительно с байтами, что делает класс полезным при
работе с файлами произвольного типа, а не только с текстовыми файлами. Побайт-
ное считывание данных позволяет использовать этот класс при работе с графиче-
скими и звуковыми файлами. Однако платой за подобную гибкость является
невозможность с помощью класса Fiiestream считывать данные непосредственно
в переменные типа строки. Это позволяют осуществлять классы StreamWriter
и StreamReader. Существует однако несколько конверсионных классов, которые
существенно облегчают выполнение преобразования массива байтов в символьные
массивы и обратно.
Метод F i l e S t r e a m . R e a d ( ) ЯВЛЯеТСЯ ОСНОВНЫМ Средством, ПОЗВОЛЯЮЩИМ ОСуще-
ствлять доступ к данным, содержащимся в файле, на который ссылается объект
FileStream!
public int Read(byte[] array, int offset, int count);
Первый параметр представляет собой массив байтов, передаваемый для разме-
щения в нем данных, получаемых от объекта Fiiestream. Второй параметр опреде-
ляет ту позицию в массиве байтов, начиная с которой должна осуществляться
операция записи данных. Обычно этот параметр имеет значение, равное нулю,
в результате чего данные из файла заносятся в самое начало массива. Последний
параметр определяет, какое количество байтов должно быть считано из файла.
Следующий пример демонстрирует считывание данных из файла с произволь-
ным доступом. В качестве файла, из которого будет производиться чтение, исполь-
зуется файл, специально созданный для данного примера.
Практикум: чтение данных из файла с произвольным доступом
oject Types!
uJ '
( 3 Visual C# Project
' СЗ V*sud C++ Protects
•,""! Setup &\d Deployment Projects
S) Ci3 Other Projects
_J Visual Studio Solutions
ASP.NET Web ASP .MET Web Web Control
Application Service Ufefsry
Р ЙР
Windows Empty Project
Service
• A project: fcr a e^ung 4 cvrxt&r&'ine.
С Add to Solution <• C
iro)oc< wi' Ъъ cre-jv..rd at C:\DevC^pm
Help
1. Откройте Visual Studio.NET.
2. Откройте новый проект,
выбрав File | New | Project.
Выберите пункт С# Console
Application и назовите его
ReadFile (см. рис. слева).
3. Добавьте два следующих
объявления пространств имен
в самое начало файла ciassi.es.
Пространство имен System.10
необходимо ДЛЯ класса FileStream,
а Пространство имен System.Text
потребуется для осуществления
564 Глава 20
преобразования, которое нам придется выполнять над байтами,
считанными из файла.
using System;
using System.10;
using System.Text;
4. Добавьте следующий код в метод Main():
static void Main (string [] args)
{
byte[] byData = new byte[100];
chart] charData = new Char[100];
try
{
FileStream aFile = new FileStream("../../Classl.cs",FileMode.Open);
aFile.Seek(55,SeekOrigin.Begin);
aFile.Read(byData#0,100) ;
}
catch(IOException e)
{
Console.WriteLineCAn 10 exception has been thrown' " ) ;
Console.WriteLine(e.ToString() ) ;
Console.ReadLine();
return; . • • . ' • • ; ••''•'".•'•'.
Decoder d = Encoding.UTF8.GetDecoder();
d.GetChars(byData, 0, byData.Length, charData, 0)
Console.WriteLine(charData);
Console-ReadLine();
• • • r e t u r n ; • • • - • • • • • • • • • • •• •••• • • •• • • • . • - •• . • •
5. Запустите приложение. Вы должны увидеть
приблизительно такой выходной поток,
какой показан на рисунке слева.
Имейте в виду, что выходной поток
на вашем компьютере может существенно
отличаться от изображенного на рисунке.
Нажмите клавишу Enter для закрытия
консоли.
Как это работает —
Чтение данных из файла с произвольным доступом
Данное приложение открывает свой собственный файл с расширением .cs, из
которого затем осуществляется чтение. Для этого оно осуществляет поиск по фай-
ловой структуре с'использованием двух точек в следующей строке:
FileStream aFile = new FileStream(*../../Classl.cs*, FileMode.Open);
Две строки кода, которые в действительности осуществляют поиск по файлу
и считывание из него, имеют следующий вид:
try
{
aFile.Seek(55,SeekOrigin.Begin)
aFile.Read(byData,0,100); .
Работа с файлами 565
catch(IOException e)
{
Console.WriteLine("An 10 exception has been thrown]");
Console.WriteLine(e.ToString());
Console.ReadLine();
return;
В результате выполнения первой из них указатель файла перемещается на 55-й
байт файла. Это символ 'п' из слова namespace; 54 предыдущих символа находятся
в строках с директивами using. В результате выполнения второй строки следую-
щие 100 байтов считываются в байтовый массив byData.
Обратите внимание на то, что нами используется конструкция для обработки
исключительных ситуаций try-catch, с которой мы познакомились в главе 7.
Практически любая операция, связанная с вводом/выводом, может привести
к возникновению исключительной ситуации типа iOException. Любой исполнимый
код должен включать в себя обработку исключительных ситуаций, особенно если
в нем ведется работа с файловой системой. Во всех примерах настоящей главы
будет использоваться некоторый основополагающий способ обработки ошибок.
После того как информация считана из файла в байтовый массив, возникает
необходимость преобразовать ее в символьный массив с тем, чтобы получить
возможность выводить ее на консоль. С этой целью мы воспользуемся классом
Decoder из пространства имен System.Text. Этот класс предназначен для того,
чтобы преобразовывать исходные байты в какие-либо более полезные объекты,
например, в символы:
Decoder d = Encoding.UTF8.GetDecoder();
d.GetChars(byData, 0, byData.Length, charData, 0 ) ;
В этих строках осуществляется создание объекта Decoder, который основывает-
ся на кодировке UTF8. Это схема кодирования Unicode. После этого вызывается
метод Getchars (), который принимает массив байтов и преобразовывает его
в массив символов. После завершения этой процедуры массив символов оказыва-
ется готовым к выводу на консоль.
Запись данных
Процесс записи данных в файл с произвольным доступом совершенно аналоги-
чен. Сначала необходимо создать байтовый массив; наиболее простой способ до-
стигнуть этого — создать сначала символьный массив, который мы собираемся
записывать в файл. Затем следует воспользоваться объектом Encoder, позволяю-
щим преобразовать его в массив байтов; использование этого объекта очень напо-
минает использование объекта Decoder. В завершение необходимо вызывать метод
write о , который и запишет информацию в файл.
Давайте создадим простой пример, который позволит продемонстрировать, ка-
ким образом это выполняется.
Практикум: запись данных в файл с произвольным доступом
1. Создайте новый проект. Выберите пункт меню С# Console Application
•И назовите его WriteFile.
2. Как и в предыдущем случае, вставьте в начало файла ciassi.cs
следующие два описания пространств имен:
using System;
using System.TO; : , . :
using System.Text; . ;
566 Глава 20
3. Добавьте в метод Main о следующий код:
static void Main (string [] args)
{ •
byte[] byData = new byte[100];
chart] charData = new Char[100];
try
{
FileStream aFile = new FileStream("Temp.txt",FileMode.OpenOrCreate) ,
charData = "Hello World".ToCharArray{);
Encoder e - Encoding.UTF8.GetEncoder() ;
e.GetBytes(charData,0,charBata.Length, byData,0,true);
// Перемещение указателя файла в самое качало файла
aFile.Seek(0,SeekOrigin.Begin);
aFile.Write(byData,0,byData.Length);
j .
catch(IOException ex)
{ ' • • •• / • • : • • • • • • • • • • : '
Console.WriteLine("An 10 exception has been thrown!");
Console.WriteLine (ex.ToStringO ) ;
Console.ReadLine();
return;
return;
-Notepad
Format Help

i e l i o w o r l d
4. Запустите приложение. Оно будет выполняться
некоторое время, а затем завершится.
5. Перейдите в директорию приложения — файл
будет сохранен именно в ней, поскольку мы
используем относительный путь. Файл будет
помещен В папку WriteFile\bin\Debug.
Откройте файл Temp.txt. Вы сможете
обнаружить в файле текст, приведенный
на рисунке слева.
Как это работает —
Запись данных в файл с произвольным доступом
Данное приложение открывает файл в своей собственной директории и записы-
вает в него несложную строку. Все это происходит аналогично предшествующему
примеру.
Следующая строка создает символьный массив с помощью статического метода
ToCharArray () класса string. Поскольку в С# все можно рассматривать в качестве
объекта, а текст -Hello world* на самом деле представляет собой объект string,
то эти методы могут вызываться даже для обычной строки символов:
c h a r D a t a = " H e l l o W o r l d * . T o C h a r A r r a y ( ) ;
Из следующих строк становится понятным, каким образом можно преобразо-
вать массив символов в соответствующий массив байтов, который требуется объ-
екту FileStream.
Encoder e = Encoding.UTF8.GetEncoder();
е.GetBytes(charData,0,charData.Length, byDate,0,true;
Работа с файлами 567
На этот раз мы создаем объект Encoder, который также основывается на коди-
ровке UTF8. В данном случае для выполнения перекодировки также используется
кодировка Unicode, однако на этот раз нам необходимо перекодировать символь-
ные данные в корректный байтовый формат, прежде чем у нас появится возмож-
ность записать информацию в поток. Это совершается в методе GetBytes (). В нем
происходит преобразование символьного массива в байтовый. Этому методу в ка-
честве первого параметра передается символьный массив, а в качестве второго —
индекс элемента, с которого следует начинать преобразование. В качестве третьего
параметра этому методу передается количество символов, подлежащих преобразо-
ванию; в качестве четвертого — байтовый массив, в котором должна размещаться
информация после преобразования, и в качестве пятого — индекс элемента, с ко-
торого начинается заполнение байтового массива. Последний параметр определя-
ет, должен ли объект Encoder обновлять свое состояние после окончания работы.
Это имеет отношение к тому факту, что объект Encoder сохраняет в памяти то
место, на котором была прекращена запись в байтовый массив. Это оказывается
весьма полезным при осуществлении последовательных обращений к объекту
Encoder, но оказывается совершенно бессмысленным в случае единственного об-
ращения к нему. При завершающем обращении к объекту Encoder этому парамет-
ру должно быть присвоено значение true, означающее, что память должна быть
очищена, а объект освобожден для процедуры сборки мусора.
После выполнения всех этих действий запись байтового массива в объект
Fiiestream посредством метода write о не представляет никаких трудностей:
aFile.Seek{0,SeekOrigin.Begin);
aFile.Write(byData,0,byData.Length);
Объект StreamWriter
Работа с объектом Fiiestream достаточна сложна, поэтому может возникнуть
вопрос, нет ли какого-либо более простого способа. Не стоит беспокоиться —
обычно вся работа с объектом Fiiestream оказывается скрытой от пользователя за
Счет работы С объектами StreamWriter И StreamReader И использования ИХ методов
ддя выполнения манипуляций над файлами. Если перед вами не стоит задача пере-
мещать указатель файла на произвольную позицию, то здесь эти классы позволя-
ют значительно упростить работу с файлами. Класс StreamWriter позволяет
осуществлять запись в файл символов и строк, он самостоятельно выполняет все
необходимые преобразования и производит запись в объект Fiiestream.
Существуют разнообразные СПОСОбы СОЗДаНИЯ Экземпляров Класса StreamWriter.
Если уже имеется объект Fiiestream, то можно воспользоваться им для создания
Объекта StreamWriter:
FileStream aFile = new FileStream^Log. txt" ,FileMode.CreateNew);
StreamWriter sw = new StreamWriter(aFile) ;
Объект StreamWriter может также создаваться непосредственно из файла:
StreamWriter sw = new StreamWriter("Log.txt",true);
Этот конструктор принимает в качестве параметров имя файла и логическое
значение, которое указывает на то, следует ли добавлять информацию к уже суще-
ствующему файлу или необходимо создать новый. Если это значение равно false,
то либо происходит создание нового файла, либо из существующего файла сначала
удаляется все хранящаяся в нем информация, а затем он открывается на запись.
Если же это значение равно true, то файл просто открывается, а все хранящаяся
568 Глава 20
в нем информация сохраняется. Если такого файла не существует, то создается
новый файл.
Обратите внимание на то, что здесь нет такого широкого спектра возможно-
стей, как при создании объекта FileStream. Кроме логического значения, опреде-
ляющего, следует ли добавлять информацию к уже существующему файлу или
создавать новый файл, нет никаких других опций, например, отсутствует возмож-
ность задания свойства FileMode, которое мы уже задавали при работе с классом
FileStream. ТОЧНО так Ж е отсутствует ВОЗМОЖНОСТЬ Задания СВОЙСТВа FileAccess,
поэтому вы всегда будете обладать доступом к файлу на чтение/запись. Для того
чтобы воспользоваться какой-либо из этих возможностей, следует сначала обра-
титься К КОНСТруктору класса FileStream, а Затем создать объект StreamWriter на
ОСНОВе Объекта FileStream.
Практикум: выходной поток
1. Создайте новый проект. Выберите пункт меню С# Console Application
И назовите его StreamWrite.
2. В данном случае опять придется воспользоваться пространством имен
System, ю, поэтому добавьте соответствующую строку в начало
файла Classl.cs.
using System;
using System.10;
3. Добавьте следующий код в метод Maino:
static void Main (string [] args)
try
FileStream aFile = new FileStream(''Log. txt" .FileMode.OpenOrCreate) ;
StreamWriter sw = new StreamWriter(aFile); i
// Запись данных з файл . •••'•••::/.:•.;:'v;:-:i:.--'-i: ;\.-':^:':-.--.. / sw.WriteLine( wHello World*) ; :];;'•••:;;':.-; \ :. ':••..'•••.[•',' Г
sw.Write("This is a ") ;
sw.Write("string of characters.");
sw.Close() ;•:,
catch(IOException e) ;
Console.WriteLine("An 10 exception has been thrown!");
Console.WriteLine(e.ToStringO );
Console.ReadLine();
return;
4. Постройте проект и запустите его. Если в нем не будет обнаружено
никаких ошибок, то он очень быстро выполнится и закроется.
Поскольку мы не предусмотрели вывода на консоль, то наблюдать
за работой этой программы оказывается не очень интересно.
5. Перейдите в директорию приложения и найдите там файл Log.txt.
Он ДОЛЖен располагаться В папке StreamWriter\bin\Debug,
поскольку используется относительный путь.
Работа с файлами 569
- Notepad
Edit Format Help
H e l l o World
T h i s i s a s t r i n g o f c h a r a c t e r s .
6. После того как вы откроете этот файл,
вы увидите символы, приведенные
на рисунке справа.
Как это работает —
Выходной поток
Данное простое приложение демонстрирует
работу двух наиболее важных методов класса
StreamWriter — Write () И WriteLine () . У каж-
дого из них имеется большое количество пере-
гружаемых версий, позволяющих использовать
различные сложные возможности при выводе
в файл, однако в настоящем примере мы вос-
пользовались основной возможностью вывода
строки.
Метод WriteLine () осуществляет запись переданной строки, за которой немед-
ленно следует символ новой строки. Как мы можем видеть в настоящем примере,
это приводит к тому, что следующая операция записи выполняется с новой строки.
Метод write о просто записывает передаваемую ему строку в файл, не добав-
ляя символа перехода к новой строке. Это позволяет осуществлять запись целого
предложения или абзаца с помощью нескольких обращений к методу write ().
Форматирование данных
Точно так же, как существует возможность вывода данных в отформатирован-
ном виде на консоль, так и в файл мы можем записывать отформатированные дан-
ные. ДЛЯ ЭТОГО применяются те же методы —Write () И WriteLine О, НО В ЭТОМ
случае им могут передаваться параметры, определяющие характер форматирова-
ния. Например:
sw.WriteLine("The Date is {Ob System.DateTime.Now.ToShortDateString());
sw.Write("{0}, {1}, {2}", "Kay", "Dan", "Rob");
В данном примере вы можете увидеть, каким образом осуществляется запись
файлов, разделенных запятыми. В более сложных примерах данные могут посту-
пать из объекта DataSet и других источников.
Объект StreamReader
Входные потоки используются для считывания данных, хранящихся во внешних
источниках. В большинстве случаев таким источником является дисковый файл
или сетевой адрес. Однако не следует забывать, что в качестве такого источника
может использоваться любой объект, отправляющий данные, например, сетевое
приложение, web-служба и даже консоль.
Для чтения данных мы будем использовать класс StreamReader. Так же, как
И КЛаСС StreamWriter, КЛаСС StreamReader ЯВЛЯетСЯ общим КЛаССОМ, КОТОрыЙ МО-
жет использоваться для любого потока. Мы снова будем создавать его на базе
объекта Filestream, поэтому он будет ссылаться на нужный нам файл.
Объекты StreamReader создаются способом, напоминающим способ создания
объектов StreamWriter. Наиболее распространенный способ создания таких объ-
ектов заключается в использовании заранее созданного объекта Filestream:
FileStream aFile = new FileStream("Log.txt",FileMode.Open) ;
StreamReader sr = new StreamReader (aFile);
570 Глава 20
Так же, как и класс streamwriter, streamReader может создаваться непосредст-
венно на основе строки, в которой содержится путь к конкретному файлу:
StreamReader sr = new StreamReader("Log.txt");
Практикум: входной поток
1. Создайте новый проект. Выберите пункт меню С# Console Application
И назовите его StreamRead.
2. В данном случае нам опять потребуется импортировать пространство
имен System, ю, поэтому добавьте соответствующую строку
в начало файла ciassi.cs.
using System;
using System.10;
3. Добавьте следующий код в метод Main о:
static void Main(string[] args)
string strLine;
' t r y ' • . .. • . • •
t • •• . • • • ' • • • • . •• ^ . • • • • , • • • • • : ; • . • • • •••• : ; ••. . • : . . • ., . \ .
FileStream aFile - new FiieStream("Log.txt",FileMode.Open);
StreamReader sr = new StreamReader{aFile);
strLine = sr,ReadLineO ;
//Построчное считывание данных
while(strLine != null)
Console.WriteLine(strLine);
strLine = sr.ReadLineO г
sr.Close О ;
. . \ Console.ReadLineO ;
} ' * .• -•; , : - . ; . : " .
catch(IOException e)
• •"• v • • ' • • ' • • ' . . - . • ':.- ' • - . •- , . : • ' • ' v : • • • ' ' •
Console.WriteLine(•An 10 exception has been thrown!");
Console.WriteLine(e.ToString О ) ;
Console.ReadLineO ;
• ; " r e t u r n ; ' '' • • _ . •• • . ' " • ' . ' . • . • -
• :' ' r e t u r n ; ' ' •' ' ' •• ' ' ' • • '; : - • • • • • • • • • • .: . ,. • , ; • ; : , . ' . • ; y
: , . : • • : • • ; : . • ;'. • • . : . ; -•
Создайте В Директории StreamReader\bin\Debug файл Log.txt.
Это может быть совершенно новый файл или скопированный
регистрационный файл, созданный в предыдущем примере.
Убедитесь, что файл находится в той же директории, что и приложение!
В противном случае приложение
завершится неудачей, поскольку
КОНСТруктор класса FileStream
сгенерирует исключительную ситуацию,
если ему не удастся обнаружить
требуемый файл.
5. Запустите приложение
Категория: информатика | Просмотров: 1935 | Добавил: basic | Рейтинг: 0.0/0
Всего комментариев: 0
Имя *:
Email *:
Код *:
Календарь
«  Февраль 2010  »
ПнВтСрЧтПтСбВс
1234567
891011121314
15161718192021
22232425262728
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

krutoto.ucoz.ru
Бесплатный конструктор сайтов - uCoz