Работа с текстовыми файлами

Работа с текстовыми файлами

Программа и данные

Каждая программа обрабатывает данные. Программа без данных просто не существует. Данные программы можно  разделить на три группы: входные, выходные и промежуточные. Входные данные – это те данные, которые известны и задаются для того, чтобы можн было решить поставленную задачу. Выходные данные – это результаты решения задачи, данные которые создаются в процессе решения задачи. Обычно большую часть данных представляют промежуточные данные, создаваемые самой програмой в процессе решения, и не являющиеся результатами решения задачи.

Откуда берутся входные данные? Они могут задаваться пользователем в ходе решения задачи. Можно считать, что в этом случчае организуется диалог пользователя и программы. В консольных проектах инциатором диалога является программа. В Windows – проектах инициатором является пользователь.

Входные данные в ряде случаев задаются в самой программе при начале ее работы. Программист отвечает за создание таких данных.

В больших программных проектах входные данные поступают из внешних источников. Природа этих источников может быть разной. Это может быть данные, хранящиеся во внешней памяти компьютера, данные поступающие из корпоративной сети или сети Internet, это могут быть данные, поступающие от различных приборов, связанных с компьютером.

Простейшим видом данных, хранящихся во внешней памяти, являются файлы. Более сложным источником данных являются базы данных, представляющих сложно организованную систему файлов с программной системой, управляющей доступом к хранимым данным.

Что такое файл?

В программировании файлом называют множество элементов, сохраняемых во внешней памяти. Элементы файла называюся записями.

В более общем контексте файлом называют собранную, сброшюрованную коллекцию бумаг, документов. В английском языке одно из значений слова «file» — это иголочка, на которую накалывают чеки, в русском – файлом называют папочку для хранения бумаг.

Одним из наиболее распространенных видов файлов в програмировании являются текстовые файлы, в которых каждая запись рассматривается как строка текста. Данные, читаемые с консоли, или выводимые на консоль, могут рассматриваться как текстовые файлы, над которыми определены операции ReadLine и WriteLine. Первая из этих операций читает очередную запись файла – строку текста. Вторая – записывает в файл очередную запись.

Создание текстового файла

Создать текстовый файл можно программно или в любом текстовом редакторе, например в Блокноте. При работе в среде Visual Sudio можно создать текстовый файл, не выходя из среды. Для этого достаточно в меню выбрать Файл/Создать/Файл.. В результате откроется текстовый редактор – аналог Блокнота, в котором можно создавать записи.

Нужно помнить одну важную особенность работы с текстовыми файлами при программировании на C#. Текстовые файлы должны сохраняться в формате UTF-8 (Unicode Text Format).  Поэтому при сохранении файла нужно установить корректную кодировку. При работе в Блокноте нужно выбрать соответствущую кодировку на вкладке «Кодировка». При работе в среде Visual Sudio прежде чем сохранять файл из меню нужно выбрать Файл/Дополнительные параметры кодировки… В открывшемся окне установить требуемую кодировку.

Программное создание текстового файла рассмотрим чуть ниже.

Открытие текстового файла для чтения

Предположим, что текстовый файл уже создан. Как можно прочесть записи этого файла в нашей программе на C#?

Как обычно, вначале нужно объявить объект соответствующего типа, создать его. После этого для этого объекта нужно выполнить операцию открытия файла. Если операция открытия файла прошла успешно, то это означает что программный объект связан с физическим файлом на диске. В этом случае можно последовательно читать запись за записью открытого файла.  Прочитанную запись, представленную в программе переменной типа string, можно соответствующим образом обработать.

После завершения чтения файла его следует закрыть и освободить соответствующий ресурс.

Давайте подробнее рассмотрим этот процесс работы с текстовым файлом в консольном проекте. Первым делом создайте консольный проект, например, с именем ConsoleTextFile. В построеном по умолчанию классе Program в разделе using добавьте ссылку на пространство имен

System.IO;

Пространство имен IO (Input / Output) содержит множество полезных классов для работы с файлами.

В поле класса Program объявите объект input, представляющий текстовый файл для чтения:

/// <summary>

/// Входной файл для чтения

/// </summary>

static StreamReader input;

Чтобы не перегружать процедру Main, выполняя непосредственно в ней все работы, связанные с файлом, разобъем нашу задачу на подзадачи. Для каждой задачи зададим свой метод, а в процедуре Main будем вызывать эти методы.

Первым делом добавим в Main пару строчек:

bool found;

//Открыть файл для чтения

found = OpenReadFile();

Булевская переменная found будет принимать значение true, если файл удалось открыть, и false – в случае неудачи. Метод OpenReadFile обеспечивает открытие файла. Вот как он выглядит:

/// <summary>

/// Открытие файла.

/// Путь к файлу input задан явно

/// </summary>

/// <returns>true, если файл успешно открыт</returns>

static bool OpenReadFile()

{

//Задать путь к директории, хранящей проект и файл для чтения

path = @»E:\_bil\C#Licey_Projects\ConsoleTextFile\input.txt»;

//Проверка существования файла в указанной директории

//Открытие текстового файла для чтения

if (File.Exists(path))

{

input = new StreamReader(path);

Console.WriteLine(«Файл открыт для чтения»);

return true;

}

else

{

Console.WriteLine(«Ошибка в задании пути файла!»);

return false;

}

}

Конструктор класса StreamReader создает программный объект input и связывает его с физическим файлом. Эта операция успешна, если путь к текстовому файлу указан правильно. Поскольку ошибки при выполнении этой операции могут возникать достаточно часто (файл удален, перемещен или путь к нему указан неверно), то предусмотрена проверка существования файла. При проверке используется статический метод Exist класса File, возвращающий true, если файл существует для указанного пути, false – если файла нет.

Корректное программирование требует, чтобы открытие файла выполнялось в охраняемом блоке в сопровождении catch-блока, обрабатывающего исключительную ситуацию, которая может возникать при неудаче операции открытия файла.

Обработка записей текстового файла

При обработке записей файла предполагается, что файл успешно открыт. Будем также предполагать, что при обработке последовательно читаются и обрабатываются все записи файла. Первый вопрос, который возникает, как узнать сколько записей хранится в файле. Одно из часто используемых решений, свойственное например олимпиадным задачам по информатике или задачам ЕГЭ, состоит в том, что первая запись файла содержит целое число, указывающее количество далее следующих записей. В этом случае, прочитав первую запись, становится ясно, сколько строк нужно прочитать для обработки всего файла.

Другой способ основан на использовании свойства EndOfStream, которым обладает наш объект input класса StreamReader. Это булевское свойство возвращает значение true при достижении конца файла, так что несложно организовать цикл:

while(!input. EndOfStream)

{

<прочесть запись>

<обработать запись>

}

Добавим в Main еще пару строк текста:

if (found)

{

//чтение и обработка файла

ReadingFile();

//закрытие файла

input.Close();

}

Если файл успешно открыт, то его можно прочесть и обработать, а завершив обработу, файл следует закрыть. Метод ReadingFile выполняет чтение и обработку файла. В данном примере метод устроен  просто. Предполагается, что файл в первой строке содержит число реальных записей. Поэтому вначале читается первая строка, после чего становится известным значение n – число реальных записей. Далее в цикле по n читаются строки файла. Обработка строки в данном случае свдися к выводу ее на консоль. Вот текст этого метода:

/// <summary>

/// Чтение и обработка файла

/// </summary>

static void ReadingFile()

{

// первая строка файла содержит число далее идущих строк

n = int.Parse(input.ReadLine());

for (int i = 0; i < n; i++)

{

string current = input.ReadLine();

//обработка записи — вывод ее на консоль.

Console.WriteLine(current);

}

}

Давайте добавим в Main вызов еще одного метода NextReading,  в котором файл повторно открывается и обрабатывается:

//Повторное открытие и обработка файла

NextReading();

В этом методе при чтении файла будем использовать цикл while. Вот как выглядит код этого метода:

/// <summary>

/// Чтение и обработка файла

/// </summary>

static void NextReading()

{

Console.WriteLine(«Повторное открытие и чтение файла»);

OpenReadFile();

while (!input.EndOfStream)

{

string current = input.ReadLine();

//обработка записи — вывод ее на консоль.

Console.WriteLine(current);

}

input.Close();

}

Программное создание текстового файла

Также как и при работе с файлом для чтения, при работе с текстовым файлом для записи необходимо объявть соответствующий программный объект (логический файл), связать его с физическим файлом, открыть для записи и записать в файл нужное количество записей.

Добавим в поля класса Program объект output, задающий файл с выходными данными, и другие объекты, используемые при работе с файлами:

/// <summary>

/// Выходной файл для записи

/// </summary>

static StreamWriter output;

/// <summary>

/// путь к файлу

/// </summary>

static string path;

/// <summary>

/// число записей в файле -

/// </summary>

static int n;

В процедуру Main добавим вызов метода открывающего файл для записи:

//Открыть файл для записи

OpenWriteFile();

Следует понимать, что при записив файл ситуация отличается от ситуации чтения файла. Предполагается, что физический файл для чтения должен существовать в момент открытия, иначе возникнет ошибка. При открытии файла на запись файл может не существовать. В этом случае физический файл (пока пустой) следует создать, связать его с логическим файлом и открыть для записи. Если же файл существует, и мы хотим писать в него записи, то появляются две возможности – стереть старые записи и начать повторную запись файла или новые записи добавлять в конец существующего файла. Вот как выглядит метод OpenWriteFile, учитывающий эти возможности:

/// <summary>

/// Открытие файла на запись.

/// Если файл не существует, он будет создан

/// Если файл существует он будет переписан

/// Добавление записей в конец файла закомментировано

/// </summary>

static void OpenWriteFile()

{

//Получить путь к директории, хранящей проект и файл для записи

path = @»E:\_bil\C#Licey_Projects\ConsoleTextFile\output.txt»;

if (File.Exists(path))

{

//Открытие для добавления записей в конец файла

// output = new StreamWriter(path, true);

//Открытие текстового файла, старые записи удаляются

output = new StreamWriter(path);

}

else

{

//Создание текстового файла для записи в файл

output = new StreamWriter(path, true);

}

Console.WriteLine(«Файл открыт для записи»);

}

Покажем теперь на простом примере, как добавляются записи в файл. Вставим еще несколько строчек в процедуру Main:

//Запись файла

WritingFile();

//закрытие файла

output.Close();

Процедура WritingFile осуществляет запись в файл:

/// <summary>

/// Запись в файл

/// </summary>

static void WritingFile()

{

// первая строка файла содержит число далее идущих строк

n = 10;

output.WriteLine(n.ToString());

for (int i = 0; i < n; i++)

{

string current = (10 * i).ToString();

output.WriteLine(current);

}

Console.WriteLine(«Файл output успешно записан»);

}

Файлы как ресурсы ОС. Освобождение ресурсов

Понятно, что один и тот же файл может использоваться в разных программах. Вполне вероятна ситуация, когда нескольким одновременно работающим на компьютере программам потребуется один и тот же файл. Операционная система (ОС) компьютера регулирует доступ разных программ к файлу, позволяя избежать конфликтов при записи и чтении. Когда ОС предоставляет файл одной программе, то доступ к этому файлу блокируется дя всех остальных программ. Со своей стороны программа, закончив работу с файлом, должна освободить ресурс, уведомив ОС, что файл свободен и может быть предоставлен другой программе.  Добавьте в конец процедуры Main две строчки, осовобождающие ресурсы:

//Освобождение ресурсов

input.Dispose();

output.Dispose();

Результаты работы проекта

В заключение приведу результаты рабоы построенного нами проекта. Входной файл input был построен руками в редакторе Visual Studio. Выходной файл output создается программно. На следующем рисунке показаны результаты работы:

Рис. 1 Результаты работа проекта ConsoleTextFile

Скачать лекцию можно по следующей ссылке:

Работа с текстовыми файлами