Материал: 695_Poletajkin_A.N._Uchebno-metodicheskoe_posobie_Realizatsija_zhiznennogo_ch.1_

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

которой при выходе присваивается 0. Если item равно нулю, то происходит выход из бесконечного цикла, иначе осуществляется переход к пункту 1.

Процедура выхода из приложения может быть усовершенствована на случай ошибочного выбора данного варианта в меню, что не всегда может быть приемлемо, так как при этом уничтожается список, который хранится в оперативной памяти машины. С этой целью в обработчик позиции «Выход в Windows» включается например такой блок:

Console.Clear();

Console.Write("\nВы действительно хотите выйти? (y/n) "); string c = Console.ReadLine();

if ((c == "y") || (c == "н")) item = 0;

break;

и при нажатии клавиши <y/н> item будет сброшена в 0, что обеспечит выход из внешнего цикла.

Листинг 6. Структура метода Main() для организации простейшего меню

static void Main()

{

const string PressAnyKey = "\nНажмите любую клавишу...";

int item;

List list = null; do

{

int k0; do

{

Console.Clear();

Console.WriteLine("\nВыберите одно из следующих

действий:");

Console.WriteLine("\n 1. Создание нового

списка");

Console.WriteLine("\n 2. Загрузка списка из

файла");

if (list == null)

{

Console.WriteLine("\n 3. Выход в

Windows");

k0 = 3;

66

}

else

{

Console.WriteLine("\n 3. Модификация

списка...");

Console.WriteLine("\n 4. Спецоперации с

записями...");

Console.WriteLine("\n 5. Статистическая обработка"); Console.WriteLine("\n 6. Сохранение

списка");

Console.WriteLine("\n 7. Выход в

Windows");

k0 = 7;

}

Console.Write("\n Введите номер выбранного действия: ");

item = 0; try

{

item = Convert.ToInt16(Console.ReadLine());

}

catch (FormatException ex)

{

Console.WriteLine("\nНеобходимо вводить целые числа от 1 до " + Convert.ToString(k0) + PressAnyKey);

Console.ReadKey();

}

} while ((item < 1) || (item > k0)); switch (item)

{

case 1:

{

// Создание нового списка

Break;

}

case 2:

{

// Загрузка списка из файла

Break;

}

case 3:

{

if (k0 == 3)

67

 

item = 0; // Реализация выхода из

else

// приложения

// Модификация списка...

Break;

}

// … другие действия, если список существует

}

if (item == 0) break; } while (true);

}

6.5. Технология программирования задачи

Итак, манипуляции со списком осуществляются в операторе switch метода Main() пространства имен ListTest. Листинг модуля Program.cs, хранящего ListTest, приведен в конце приложения В в виде архива, который представлен в виде внедренного объекта и доступен для скачивания в электронной версии пособия. Там же приведен исполняемый файл приложения.

1. Создание нового списка

Как отмечалось выше, первые две команды меню выполняют инициализацию нового списка. Алгоритм очень прост:

1.Создание объекта при помощи оператора new (вызывается конструктор с параметром).

2.Создание через консоль статической записи PData при помощи метода

ListTest InputData().

3.Вызов метода InsertAtFront объекта list.

На выходе обработчика команды односвязный список list с одной записью, введенной с клавиатуры.

2. Загрузка списка из файла

Здесь также создается новый список, но инициализируется данными из файла. Поэтому главным образом рассмотрим работу с файлами.

Платформа .NET предоставляет такие средства для работы с файлами и каталогами. Классы, необходимые для этих целей, находятся в пространстве имен System.IO. В их число входят класс File, представляющий файл на диске, и класс Directory, который представляет каталог.

Работа с каталогами. Класс Directory содержит статические методы, позволяющие создавать, перемещать и исследовать каталоги. Поскольку все методы этого класса являются статическими, их можно вызывать, не создавая объект класса. Класс Directory info аналогичен предыдущему, но он обладает только нестатическими элементами. Этот класс является потомком класса FileSystemInfo, который имеет ряд свойств и методов, предоставляющих информацию о файле или каталоге.

Чтобы исследовать иерархическую структуру каталогов, необходимо создать объект класса Directorylnfo. Этот класс предоставляет методы,

68

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

Работа с файлами. Объект Directcrylnfo может, кроме всего прочего, возвратить коллекцию всех файлов в каждом найденном подкаталоге. Метод GetFiles() возвращает массив объектов FileInfo, каждый из которых описывает файл. Объекты FileInfo и File связаны друг с другом аналогично тому, как связаны DirectoryInfo и Directory. Как и методы класса Directory, все методы класса File — статические; подобно методам класса DirectoryInfo, все методы класса FileInfo являются нестатическими.

При создании объекта класса DirectoryInfo следует указать имя рассматриваемого каталога и путь к нему.

// Получение списка файлов на "D:\" DirectoryInfo dir = new DirectoryInfo(@"D:\"); FileInfo[] ms = dir.GetFiles("data*.txt");

Теперь массив ms содержит набор ссылок на файлы маски "data*.txt" директории "D:\", ссылка на которую dir.

Чтение и запись данных выполняются при помощи класса Stream. Платформа .NET Framework предоставляет целый ряд классов, являющихся потомками Stream, в том числе FileStrear, MemoryStream и NetworkStream.

Если доподлинно известно, что файл содержит только текст, то для чтения или записи можно применять классы StreamReader и SireamWriter соответственно. Эти классы разработаны специально для облегчения работы с текстом. Например, они обладают методами ReadLine() и WriteLine(), позволяющими читать и записывать одну строку текста. Вывод в поток SireamWriter с помощью метода WriteLine() уже рассматривался в разделе 1 при описании процедуры вывода списка в файл. Здесь рассмотрим поток

StreamReader.

Чтобы создать экземпляр класса StreamReader, необходимо сначала создать объект FileInfo и затем вызвать его метод OpenText(). Этот метод возвращает объект StreamReader для файла. Имея этот объект, можно приступать к построчному чтению файла, например, в цикле.

FileInfo f = new FileInfo(@"D:\data" + i + ".txt"); StreamReader fs = f.OpenText();

string text; do

{

text = fs.ReadLine(); } while (text != null)

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

69

1.Формируется коллекция ms файлов в данной директории.

2.Если длина коллекции ms равна 0, то выводится сообщение о том, что файлы со списком не найдены и исполнение команды завершается. В противном случае в цикле foreach печатается перечень файлов и запрашивается индекс data-файла.

3.Если ввод произошел успешно, создается объект FileInfo для данного файла. Если такой файл не существует, то выводится соответствующее сообщение и осуществляется возврат к началу цикла (то есть, к выбору файла).

4.Если файл с введенным индексом существует, создается поток для чтения текста StreamReader при помощи метода OpenText объекта-ссылки на файл.

5.Стандартным методом объекта StreamReader ReadLine() из файла считывается первая строка, хранящая имя списка, которая используется в качестве аргумента конструктору нового списка list.

6.Организуется цикл для перемещения по записям. В цикле вызывается метод ListTest CreateData(). Он просматривает строку из файла с записью данных через знак пробела (см. описание вывода в файл в разделе 1) и возвращает структуру PData, которая используется для создания записи в

списке с помощью метода InsertAtBack().

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

3. Модификация списка...

Если на этапе выбора этого действия список не был создан, то выполняется обработка команды «Выход в Windows». Если же предварительно была успешно выполнена команда 1 или 2 (см. выше), то объектная переменная list содержит данные связанного списка, и выполняется команда модификации списка.

Поскольку вариантов модификации в подменю команды 3 множество (на что указывает троеточие после названия команды), то прежде всего необходимо организовать меню второго уровня для выбора нужного варианта. Делается это аналогично реализации головного меню, для чего организуется двойной цикл с переменными item1 и k (аналогично item и k0).

3.1. добавление записи в начало списка осуществляется двумя операторами: создание статической записи при помощи метода InputData() и вызов для списка list метода InsertAtFront().

3.2. добавление записи в конец списка осуществляется аналогично, заменой вызова метода InsertAtFront() на вызов метода InsertAtBack().

Все остальные команды (3.3 – 3.6) могут выполняться только при непустом списке. Поэтому, если список пуст, то в третьей позиции оказывается команда 3.7. возврат в главное меню, что и выполняется далее.

70