Обработка текстовых файлов
Методические указания
к лабораторной работе № 6 по дисциплине
«Программирование на языках высокого уровня»
для студентов специальности 220100 (ВМКСС)
Тверь, 2009
Цель работы
Освоить работу с текстовыми файлами. Создавать и использовать собственные функции.
Содержание отчета
Отчет по работе должен содержать:
1. Цель работы
2. Вариант задания на выполнение работы
3. Алгоритм программы на псевдокоде (для каждой функции отдельно).
4. Исходный текст программы.
5. Результаты выполнения программы, включающие для каждого варианта исходных данных копию экрана и распечатки входного файла (файлов) и выходного файла.
Файлы и потоки
Большинство программ, так или иначе, работают с каталогами и файлами. Средства для работы с файлами предусмотрены практически в любом языке программирования. Эти средства могут быть встроены непосредственно в язык, а также входить в стандартные библиотеки функций и классов, поставляющихся вместе с компилятором.
Программы, составленные на языке С#, работают с каталогами и файлами при помощи специально предназначенных для этого классов, входящих в состав библиотеки классов Microsoft.NET Framework.
Все операции с файлами выполняются в программах С# с помощью так называемых потоков данных (data stream). Программы могут выполнять над потоками данных 3 операции:
§ запись данных в поток,
§ чтение данных из потока,
§ позиционирование.
Для потоков, связанных с файлами, определено также такое понятие, как текущая позиция внутри файла. Перед началом операций ввода-вывода программа должна открыть поток. При этом текущая позиция устанавливается на начало файла. При чтении файла или записи в файл блоков данных текущая позиция сдвигается к концу файла на количество байтов, равное размеру прочитанного или записанного блока данных. При помощи средств позиционирования программа может установить текущую позицию в произвольное место файла. Когда работа с файлом закончена, программа обязательно должна закрыть соответствующий поток явным образом.
Программа С# может работать с потоками нескольких типов:
§ стандартными потоками ввода и вывода,
§ потоками, связанными с локальными файлами,
§ потоками, связанными с файлами в оперативной памяти.
Стандартные потоки и классы
Стандартные потоки
Стандартные потоки обычно связаны с консолью и клавиатурой, но могут быть перенаправлены в файлы средствами операционной системы. По своему назначению эти потоки больше всего напоминают стандартные потоки ввода и вывода, а также стандартный поток вывода сообщений об ошибках.
Практически все программы, примеры которых приводились в данном курсе, работают со стандартными потоками ввода и вывода. Для вывода данных в стандартный поток вывода применялись методы System. Console. Write () и System. Console. WriteLine (). Ввод данных из стандартного потока выполнялся с помощью метода System. Console. ReadLine ().
Основные классы ввода и вывода
Классы, реализующие операции с файлами, достаточно многочисленны. Тем не менее, можно выделить основные группы действий, которые необходимы при работе с файлами, и классы, методы которых выполняют эти действия.
Класс BinaryReader предназначен для чтения блоков данных из потоков ввода на уровне отдельных байтов. Обычно для чтения объектов из файлов, таких, как строки и числа, программисты используют другие, более мощные классы.
Класс BinaryWriter служит в качестве низкоуровневого средства записи данных в потоки вывода.
Класс File предназначен для работы с оглавлениями каталогов. С помощью статических методов этого класса можно получить список файлов и каталогов, расположенных в заданном каталоге, создать или удалить каталог, переименовать файл или каталог, а также выполнить некоторые другие операции.
С помощью статических методов класса Directory программа может получать списки каталогов и подкаталогов, создавать и удалять каталоги, а также выполнять над каталогами другие подобные операции.
Класс Path обеспечивает методы и свойства, с помощью которых программы могут работать с именами и полными путями каталогов.
Классы для работы с потоками
На базе класса System. IO. Stream создано несколько производных классов, специально предназначенных для работы с потоками ввода и вывода.
Класс FileStream, как это видно из его названия, предназначен для работы с файлами через потоки ввода и вывода. Он обеспечивает позиционирование внутри потоков ввода и вывода методом Seek. Это позволяет выполнять прямой доступ к файлам.
С помощью класса CryptoStream можно организовать ввод и вывод данных в зашифрованном виде.
Класс MemoryStream позволяет создавать файлы в оперативной памяти. Доступ к таким файлам осуществляется очень быстро, так как не требуется работать с медленными дисковыми устройствами. Обычно файлы в оперативной памяти создаются для временного хранения данных, потому что после выключения компьютера содержимое этих файлов исчезнет.
С помощью класса NetworkStream можно осуществлять удаленный ввод и вывод данных через сетевое соединение.
Класс BufferedStream обеспечивает буферизацию при работе с потоками ввода и вывода. Буферизация операций ввода и вывода в большинстве случаев значительно ускоряет работу приложений, так как при ее использовании сокращается количество обращений к системе для обмена данными с внешними устройствами.
Классы для работы с потоками текстовых символов
Специально для работы с потоками текстовых символов в кодировке UNICODE в библиотеке классов Microsoft. NET Framework предусмотрены классы System. IO. TextReader и System. IO. TextWriter. Первый из этих классов предназначен для потоков ввода, а второй — для потоков вывода.
С помощью класса StreamReader, созданного на основе класса System. IO. TextReader, программа может читать отдельные байты входного потока символов. Аналогично класс StreamWriter, созданный на основе класса System. IO. TextWriter, позволяет писать байты символов в выходной поток.
Классы StringReader и StringWriter, созданные на базе классов System.IO.TextReader и System.IO.TextWriter, соответственно предназначены для чтения и записи текстовых строк, т.е. данных типа string.
Перечисления
При работе с потоками, файлами и каталогами используются перечисления FileAccess, FileMode, FileShare и SeekOrigin. Они содержат определения констант, нужных для определения режимов создания и открытия файлов, режимов совместного доступа к файлу, а также позиции при произвольном доступе к файлам.
Все эти константы будут рассматриваться далее по мере изложения материала этого раздела.
Создание потоков, связанных с файлами
Если вам нужно создать входной или выходной поток, связанный с локальным файлом, содержащим двоичные данные, следует воспользоваться классами BinaryWriter и BinaryReader из библиотеки Microsoft. NET Framework. Что же касается чтения из файла или записи в файл текстовых данных, то для решения этой задачи обычно используются классы StreamReader и StreamWriter.
В любом случае, перед тем как читать данные из файла или записывать их в файл, необходимо выполнить операцию открытия потока, связанного с файлом. Когда работа с файлом окончена, соответствующий поток необходимо закрыть явным образом.
Обращаем ваше внимание на то, что система сборки мусора, встроенная в среду исполнения Microsoft. NET Framework, следит за использованием только объектов, находящихся в оперативной памяти, таких, как переменные, массивы, экземпляры объектов, созданных на базе классов и т. п. Что же касается потоков, связанных с файлами, то программист сам обязан следить за их открытием и закрытием.
Работа с текстовыми файлами
Текстовые файлы содержат символьную информацию, но, как и двоичные, тоже состоят из байтов. Поэтому потоки FileStream, BinaryWriter и BinaryReader можно использовать для записи в файлы и чтения из файлов текстовых строк, но лучше
Для работы с файлами применят специально предназначенные для этого потоки классов StreamWriter и StreamReader. Эти потоки чрезвычайно просты в использовании и удобны для работы с текстовыми файлами.
Основные приемы использования потоков StreamWriter и StreamReader демонстрируются в программе, исходный текст которой приведен ниже:
Namespace пространство имён
using System;
using System.IO;
namespace TextFile
{
class Program
{
static string testFile = "mydata.txt";
static void Main(string[] args)
{
if (File.Exists(testFile))
{
Console.WriteLine("Файл {0} уже существует", testFile);
Console.ReadLine();
return;
}
StreamWriter sw = File.CreateText(testFile);
sw.WriteLine("Каждый охотник желает знать, где сидит фазан!");
sw.WriteLine("Число \"Пи\" равно примерно {0}.", 3.1415926);
sw.Close();
StreamReader sr = File.OpenText(testFile);
while (true)
{
string str = sr.ReadLine();
if (str == null)
break;
Console.WriteLine(str);
}
sr.Close();
Console.WriteLine("Файл успешно создан");
Console.ReadLine();
}
}
}
Данная программа сразу после запуска проверяет существование рабочего файла в текущем каталоге:
if (File.Exists(testFile))
{
Console.WriteLine("Файл {0} уже существует", testFile);
Console.ReadLine();
return;
}
Если файл с именем mydata.txt существует в текущем каталоге, программа завершает свою работу с сообщением об ошибке. В противном случае программа создает текстовый файл и открывает поток для работы с ним класса StreamWriter:
StreamWriter sw = File.CreateText(testFile);
Как видите, эта операция выполняется методом CreateText, определенным в классе File. Аналогичного эффекта можно было бы достичь и с помощью следующего конструктора класса StreamWriter:
StreamWriter sw = new StreamWriter(testFile, false);
Первый параметр этого конструктора определяет полный путь к открываемому файлу. Если значение второго параметра равно true, новые данные будут добавлены к файлу, а если false — содержимое файла будет перезаписано.
Для открытия текстового файла на запись вы можете использовать любой из этих конструкторов. Заметим, что в классе StreamWriter имеются и другие конструкторы, позволяющие, например, задать кодировку текстовых символов, записываемых в файл.
После того как поток StreamWriter открыт, программа может записывать в него текстовые строки, пользуясь хорошо известными вам методами Write и WriteLine:
sw.WriteLine("Каждый охотник желаетзнать, где сидит фазан!");
sw.WriteLine ("Число \"Пи\" равно примерно {0}.", 3.1415926);
Когда запись новых данных в поток завершена, необходимо закрыть поток методом Close:
sw.Close();
Теперь о чтении данных из текстового файла. Прежде всего, программа должна открыть поток класса StreamReader, привязав его к файлу. Это можно сделать методом File.OpenText:
StreamReader sr = File.OpenText(testFile);
Вы можете также открыть поток и привязать его к файлу с помощью конструктора класса StreamReader:
StreamReader sr = new StreamReader(testFile);
Далее наша программа считывает текстовые строки из файла, вызывая в цикле метод ReadLine:
while(true)
{
string str = sr.ReadLine();
if(str == null)
break;
Console.WriteLine(str);
}
Этот метод возвращает прочитанную строку или значение null при достижении конца файла. После завершения работы с потоком StreamReader его следует закрыть методом Close:
sr.Close();
Таким образом, приведенная выше программа записывает две строки в файл, а затем читает их оттуда и отображает на консоли:
При повторном запуске, однако, результат будет совершенно другой:
Это совершенно нормально, т.к. программа включает проверку существования файла и в случае его обнаружения завершает работу.
Работа с двоичными файлами
Открытие потока FileStream
Для работы с двоичными файлами программа должна вначале создать поток класса FileStream, воспользовавшись соответствующим конструктором, например:
FileStream fs = new FileStream("myfile.dat", FileMode.CreateNew);
В качестве первого параметра конструктору необходимо передать полный путь к файлу или имя файла, а в качестве второго — режим открытия потока: