В классе File St ream определены два метода для чтения байтов из файла: ReadByte()
и Read()
. Так, для чтения одного байта из файла используется метод ReadByte()
, общая форма которого приведена ниже.
int ReadByte()
Всякий раз, когда этот метод вызывается, из файла считывается один байт, который затем возвращается в виде целого значения. К числу вероятных исключений, которые генерируются при этом, относятся NotSupportedException
(поток не открыт для ввода) и ObjectDisposedException
(поток закрыт).
Для чтения блока байтов из файла служит метод Read()
, общая форма которого выглядит так.
int Read(byte[ ] array, int offset, int count)
В методе Read()
предпринимается попытка считать количество count
байтов в массив array
, начиная с элемента array[offset]
. Он возвращает количество байтов, успешно считанных из файла. Если же возникает ошибка ввода-вывода, то генерируется исключение IOException
. К числу других вероятных исключений, которые генерируются при этом, относится NotSupportedException
. Это исключение генерируется в том случае, если чтение из файла не поддерживается в потоке.
В приведенном ниже примере программы метод ReadByte()
используется для ввода и отображения содержимого текстового файла, имя которого указывается в качестве аргумента командной строки. Обратите внимание на то, что в этой программе проверяется, указано ли имя файла, прежде чем пытаться открыть его.
/* Отобразить содержимое текстового файла.
Чтобы воспользоваться этой программой, укажите имя того файла,
содержимое которого требуется отобразить. Например, для просмотра
содержимого файла TEST.CS введите в командной строке следующее:
ShowFile TEST.CS
*/
using System;
using System.IO;
class ShowFile {
static void Main(string[] args) {
int i;
FileStream fin;
if (args.Length != 1) {
Console.WriteLine("Применение: ShowFile Файл");
return;
}
try {
fin = new FileStream(args[0], FileMode.Open);
} catch (IOException exc) {
Console.WriteLine("He удается открыть файл");
Console.WriteLine(exc.Message);
return; // Файл не открывается, завершить программу
}
// Читать байты до конца файла,
try {
do {
i = fin.ReadByte();
if (i != -1)
Console.Write((char)i);
} while (i != -1);
} catch (IOException exc) {
Console.WriteLine("Ошибка чтения файла");
Console.WriteLine(exc.Message);
} finally {
fin.Close();
}
}
}
Обратите внимание на то, что в приведенной выше программе применяются два блока try
. В первом из них перехватываются исключения, возникающие при вводе-выводе и способные воспрепятствовать открытию файла. Если произойдет ошибка ввода-вывода, выполнение программы завершится. В противном случае во втором блоке try
будет продолжен контроль исключений, возникающих в операциях ввода- вывода. Следовательно, второй блок try
выполняется только в том случае, если в переменной fin
содержится ссылка на открытый файл. Обратите также внимание на то, что файл закрывается в блоке finally
, связанном со вторым блоком try
. Это означает, что независимо от того, как завершится цикл do-while
(нормально или аварийно из-за ошибки), файл все равно будет закрыт. И хотя в данном конкретном примере это и так важно, поскольку программа все равно завершится в данной точке, преимущество такого подхода, вообще говоря, заключается в том, что файл закрывается в завершающем блоке finally
в любом случае — даже если выполнение кода доступа к этому файлу завершается преждевременно из-за какого-нибудь исключения.
В некоторых случаях оказывается проще заключить те части программы, где осуществляется открытие и доступ к файлу, внутрь блока try
, вместо того чтобы разделять обе эти операции. В качестве примера ниже приведен другой, более краткий вариант написания представленной выше программы ShowFile
.
// Отобразить содержимое текстового файла.
using System;
using System.IO;
class ShowFile {
static void Main(string[] args) {
int i;
FileStream fin = null;
if (args.Length != 1) {
Console.WriteLine("Применение: ShowFile File");
return;
}
// Использовать один блок try для открытия файла и чтения из него
try {
fin = new FileStream(args[0], FileMode.Open);
// Читать байты до конца файла,
do {
i = fin.ReadByte();
if (i != -1)
Console.Write((char)i);
} while (i != -1);
} catch (IOException exc) {
Console.WriteLine("Ошибка ввода-вывода:\n" + exc.Message);
} finally {
if (fin != null)
fin.Close();
}
}
}
Обратите внимание на то, что в данном варианте программы переменная fin
ссылки на объект класса FileStream
инициализируется пустым значением. Если файл удастся открыть в конструкторе класса FileStream
, то значение переменной fin окажется непустым, а иначе — оно так и останется пустым. Это очень важно, поскольку метод Close()
вызывается внутри блока finally
только в том случае, если значение переменной fin
оказывается непустым. Подобный механизм препятствует любой попытке вызвать метод Close()
для переменной fin
, когда она не ссылается на открытый файл. Благодаря своей компактности такой подход часто применяется во многих примерах организации ввода-вывода, приведенных далее.
Следует, однако, иметь в виду, что он не пригоден в тех случаях, когда ситуацию, возникающую в связи с невозможностью открыть файл, нужно обрабатывать отдельно. Так, если пользователь неправильно введет имя файла, то на экран, возможно, придется вывести приглашение правильно ввести имя файла, прежде чем входить в блок try
, где осуществляется проверка правильности доступа к файлу.
В целом, порядок открытия, доступа и закрытия файла зависит от конкретного приложения. То, что хорошо в одном случае, может оказаться неприемлемым в другом. Поэтому данный процесс приходится приспосабливать к конкретным потребностям разрабатываемой программы.
Открытие и закрытие файла | Запись в файл |