Programming Taskbook

English

E-mail:

Пароль:

Регистрация пользователя   Восстановление пароля

Электронный задачник по программированию

©  М. Э. Абрамян, 1998–2011

 

Решения | C# и VB.NET | Обработка файлов

PrevNext


Выполнение заданий на обработку файлов

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

Двоичные файлы с числовой информацией: File48

Особенности выполнения заданий на обработку файлов рассмотрим на примере задания File48.

File48°. Даны три файла целых чисел одинакового размера с именами SA, SB, SC и строка SD. Создать новый файл с именем SD, в котором чередовались бы элементы исходных файлов с одним и тем же номером: A1B1C1, A2B2C2, ... .

Создание программы-заготовки и знакомство с заданием

Напомним, что проект-заготовку для решения задания можно создать с помощью модуля PT4Load. В созданный проект будет входить файл с именем File48; его расширение зависит от выбранного языка: .cs для C# и .vb для VB.NET. Приведем текст процедуры Solve из данного файла (именно в эту процедуру требуется ввести решение задания):

[C#]

public static void Solve()
{
    Task("File48");
}

[VB.NET]

Sub Solve()
    Task("File48")
End Sub

После запуска программы на экране появится окно задачника. На рисунке приводится вид этого окна в случае использования языка C#.

В первой строке раздела исходных данных указаны имена трех исходных файлов (SA, SB и SC) и одного результирующего (SD). В трех последних строках раздела исходных данных показано содержимое исходных файлов. Для отображения содержимого каждого файла отводится по одной строке. Элементы файлов отображаются бирюзовым цветом, чтобы подчеркнуть их отличие от обычных исходных данных (желтого цвета) и комментариев (светло-серого цвета).

Поскольку размер файлов, как правило, превышает количество элементов, которое может уместиться на одной экранной строке, предусмотрена возможность прокрутки (листания) элементов файла с помощью мыши или клавиатуры.

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

Вернемся к нашей программе, только что запущенной на выполнение. Так как в ней не указаны операторы ввода-вывода, запуск программы считается ознакомительным, проверка решения не производится, а в разделе результатов отображается вкладка «Пример верного решения» с контрольными данными (в нашем случае это числа, которые должны содержаться в результирующем файле при правильном решении задания).

Ввод исходных данных

Все классы библиотеки .NET Framework, обеспечивающие работу с файлами, определены в пространстве имен System.IO. Для того чтобы при обращении к именам этих классов не требовалось дополнительно указывать имя пространства имен, в начале программы надо поместить следующий оператор:

[C#]

using System.IO;

[VB.NET]

Imports System.IO

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

[C#]

public static void Solve()
{
    Task("File48");
    FileStream[] f = new FileStream[4];
    for (int i = 0; i < 3; i++)
        f[i] = new FileStream(GetString(), FileMode.Open);
}

[VB.NET]

Sub Solve()
    Task("File48")
    Dim f(3) As FileStream
    For i As Integer = 0 To 2
        f(i) = New FileStream(GetString(), FileMode.Open)
    Next
End Sub

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

Запуск нового варианта программы уже не будет считаться ознакомительным, поскольку в программе выполняется ввод исходных данных. Так как имя результирующего файла осталось непрочитанным, этот вариант решения будет признан неверным и приведет к сообщению «Введены не все требуемые исходные данные. Количество прочитанных данных: 3 (из 4)».

Пример программы, приводящей к ошибке времени выполнения

Изменим программу так, чтобы в цикле выполнялось не 3, а 4 итерации:

[C#]

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

[VB.NET]

    For i = 0 To 3

Теперь запуск программы приведет к следующему сообщению об ошибке: «Error System.IO.FileNotFoundException: Could not find file "C:\PT4Work\d8kf.tst"» (имя файла, естественно, будет другим). Сообщение, начинающееся со слова «Error», означает, что при работе программы произошла ошибка времени выполнения (runtime error). В программах, разработанных для платформы .NET, любая ошибка времени выполнения приводит к возбуждению исключения (exception). В тексте сообщения вначале указывается тип возникшего исключения (в данном случае FileNotFoundException), а затем — его краткое описание на английском языке.

Создание пустого результирующего файла

Чтобы избежать ошибки времени выполнения, отсутствующий файл результатов следует открыть не в режиме FileMode.Open, а в режиме FileMode.Create, что обеспечит создание данного файла. Для этого восстановим прежнее количество итераций цикла, равное 3, а после цикла добавим оператор, открывающий последний, четвертый файловый поток в режиме создания:

[C#]

    f[3] = new FileStream(GetString(), FileMode.Create);

[VB.NET]

    f(3) = New FileStream(GetString(), FileMode.Create)

Запуск этого варианта программы не приведет к ошибке времени выполнения, однако будет выведено сообщение «Результирующий файл не закрыт». Для закрытия файловых потоков предусмотрен специальный метод Close, который следует вызывать сразу после того, как работа с данным потоком закончена. Заметим, что если при выполнении заданий с использованием задачника Programming Taskbook результирующие файловые потоки не закрыты методом Close, то решение будет считаться ошибочным (с указанной выше диагностикой).

В нашем случае вызов метода Close удобно организовать в цикле. Внеся это добавление, мы получим следующий вариант процедуры Solve:

[C#]

public static void Solve()
{
    Task("File48");
    FileStream[] f = new FileStream[4];
    for (int i = 0; i < 3; i++)
        f[i] = new FileStream(GetString(), FileMode.Open);
    f[3] = new FileStream(GetString(), FileMode.Create);
    //
    for (int i = 0; i < 4; i++)
        f[i].Close();
}

[VB.NET]

Sub Solve()
    Task("File48")
    Dim f(3) As FileStream
    For i As Integer = 0 To 2
        f(i) = New FileStream(GetString(), FileMode.Open)
    Next
    f(3) = New FileStream(GetString(), FileMode.Create)
    REM
    For i As Integer = 0 To 3
        f(i).Close()
    Next
End Sub

Комментарий (// для C#, REM для VB.NET) расположен в том месте программы, в котором можно выполнять операции ввода-вывода для всех четырех файлов: они уже открыты в режиме Open или Create и еще не закрыты методом Close.

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

EOF:

Особое значение EOF (End Of File — «конец файла») для указателя текущей файловой позиции в окне задачника означает, что данный файл существует, но не содержит ни одного элемента. Таким образом, нам осталось реализовать фрагмент алгоритма, обеспечивающий ввод и вывод файловых данных.

Использование неправильных типов для файловых элементов

Во всех ранее рассмотренных вариантах программы мы не использовали операции ввода-вывода для файлов. Поэтому тип файловых элементов нас не интересовал.

Однако при чтении данных из файла (и при их записи в файл) крайне важно правильно указывать тип файловых элементов. Чтобы продемонстрировать это на примере нашей программы, попытаемся прочесть из исходных файлов по одному элементу вещественного типа и запишем прочитанные элементы в файл результатов. Для этого заменим комментарий (// или REM) на следующий фрагмент:

[C#]

    BinaryReader[] r = new BinaryReader[3];
    for (int i = 0; i < 3; i++)
        r[i] = new BinaryReader(f[i]);
    BinaryWriter w = new BinaryWriter(f[3]);
    for (int i = 0; i < 3; i++)
        w.Write(r[i].ReadDouble());

[VB.NET]

    Dim r(2) As BinaryReader
    For i As Integer = 0 To 2
        r(i) = New BinaryReader(f(i))
    Next
    Dim w As New BinaryWriter(f(3))
    For i As Integer = 0 To 2
        w.Write(r(i).ReadDouble())
    Next

Данный фрагмент создает вспомогательные потоки-оболочки типа BinaryReader и BinaryWriter, обеспечивающие считывание из файла и запись в файл элементов различных типов, после чего производит считывание одного вещественного элемента для каждого из трех исходных файлов и запись этих элементов в результирующий файл (в требуемом порядке). Подчеркнем, что мы неправильно выбрали метод чтения элементов (ReadDouble); тем не менее, компиляция программы пройдет успешно, а после ее запуска не произойдет ошибок времени выполнения.

Результат работы программы будет неожиданным: судя по экранной строке с содержимым результирующего файла, в него будут записаны не три, а шесть элементов, по два начальных элемента из каждого исходного файла. Объясняется это тем, что считывание из файла и последующая запись в файл одного «вещественного элемента» фактически приводит к считыванию и записи блока данных размером 8 байтов, содержащего два последовательных целочисленных элемента исходного файла.

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

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

Правильное решение, его тестирование и просмотр результатов

Приведем, наконец, верное решение задания File48:

[C#]

public static void Solve()
{
    Task("File48");
    FileStream[] f = new FileStream[4];
    for (int i = 0; i < 3; i++)
        f[i] = new FileStream(GetString(), FileMode.Open);
    f[3] = new FileStream(GetString(), FileMode.Create);
    BinaryReader[] r = new BinaryReader[3];
    for (int i = 0; i < 3; i++)
        r[i] = new BinaryReader(f[i]);
    BinaryWriter w = new BinaryWriter(f[3]);
    while (f[0].Position < f[0].Length)
        for (int i = 0; i < 3; i++)
            w.Write(r[i].ReadInt32());
    for (int i = 0; i < 3; i++)
        r[i].Close();
    w.Close();
}

[VB.NET]

Sub Solve()
    Task("File48")
    Dim f(3) As FileStream
    For i As Integer = 0 To 2
        f(i) = New FileStream(GetString(), FileMode.Open)
    Next
    f(3) = New FileStream(GetString(), FileMode.Create)
    Dim r(2) As BinaryReader
    For i As Integer = 0 To 2
        r(i) = New BinaryReader(f(i))
    Next
    Dim w As New BinaryWriter(f(3))
    Do While f(0).Position < f(0).Length
        For i As Integer = 0 To 2
            w.Write(r(i).ReadInt32())
        Next
    Loop
    For i As Integer = 0 To 2
        r(i).Close()
    Next
    w.Close()
End Sub

От предыдущего варианта данное решение отличается добавлением цикла while (Do While для языка VB.NET), условие которого будет истинным, пока в первом файле будут оставаться непрочитанные элементы. Таким образом, цикл while обеспечивает считывание всех элементов из исходных файлов (напомним, что по условию задания все исходные файлы имеют одинаковый размер) и запись их в результирующий файл в нужном порядке. В программе, кроме того, изменен фрагмент, связанный с закрытием файловых потоков: вместо вызова метода Close объектов FileStream выполняется вызов одноименных методов объектов BinaryReader и BinaryWriter. Это обеспечивает закрытие всех потоков, так как при вызове метода Close для потока-оболочки BinaryReader или BinaryWriter происходит автоматический вызов метода Close для связанного с ним базового файлового потока FileStream.

После запуска этого варианта мы получим сообщение «Верное решение. Тест номер 1 (из 5)», а после пяти подобных запусков — сообщение «Задание выполнено!». С помощью команды меню Visual Studio «Tools | PT4 Results» мы можем вывести на экран окно результатов, в котором будут перечислены все наши попытки решения задания. Приведем содержимое окна результатов в случае решения задания на языке C#:

File48     S24/03 12:15 Ознакомительный запуск.
File48     S24/03 12:16 Введены не все требуемые исходные данные.
File48     S24/03 12:17 Результирующий файл не найден.
File48     S24/03 12:18 Error FileNotFoundException.
File48     S24/03 12:19 Ошибочное решение.--3
File48     S24/03 12:21 Задание выполнено!

Примечание. Программу, решающую данное задание, можно сделать более краткой, если не использовать массив объектов FileStream, а создавать эти объекты «на лету», сразу передавая их в конструкторы объектов BinaryReader и BinaryWriter. Кроме того, для создания файловых потоков вместо конструкторов класса FileStream можно использовать методы класса File. Приведем соответствующий вариант решения.

[C#]

public static void Solve()
{
    Task("File48");
    BinaryReader[] r = new BinaryReader[3];
    for (int i = 0; i < r.Length; i++)
        r[i] = new BinaryReader(File.OpenRead(GetString()));
    BinaryWriter
        w = new BinaryWriter(File.Create(GetString()));
    while (r[0].BaseStream.Position < r[0].BaseStream.Length)
        for (int i = 0; i < r.Length; i++)
            w.Write(r[i].ReadInt32());
    foreach (BinaryReader a in r)
        a.Close();
    w.Close();
}

[VB.NET]

Sub Solve()
    Task("File48")
    Dim r(2) As BinaryReader
    For i As Integer = 0 To r.Length - 1
        r(i) = New BinaryReader(File.OpenRead(GetString()))
    Next
    Dim w As New BinaryWriter(File.Create(GetString()))
    Do While r(0).BaseStream.Position < r(0).BaseStream.Length
        For i As Integer = 0 To r.Length - 1
            w.Write(r(i).ReadInt32())
        Next
    Loop
    For Each a As BinaryReader In r
        a.Close()
    Next
    w.Close()
End Sub

Обратите внимание на использование в циклах for свойства Length массива r вместо константы 3, что делает программу более наглядной и гарантирует, что выхода за границы массива не произойдет. В заголовке цикла while было использовано свойство BaseStream потока-оболочки BinaryReader; это свойство обеспечивает доступ к свойствам Position и Length файлового потока, связанного с данным потоком-оболочкой.

Обратите также внимание на применение специального цикла foreach (For Each для языка VB.NET) при вызове метода Close для каждого элемента массива r. Данный цикл обеспечивает большую наглядность по сравнению с циклом for. Однако в цикле foreach элементы массива доступны только для чтения, поэтому цикл foreach нельзя использовать для инициализации элементов массива. Заметим, что цикл foreach наиболее эффективен для обработки динамических структур данных (в частности, динамических массивов ArrayList), в то время как для обычных массивов и строк быстрее выполняется «обычный» цикл for.


Строковые и текстовые файлы: File67, Text21

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

Двоичные строковые файлы

В качестве примера задания на строковые файлы рассмотрим задание File67.

File67°. Дан строковый файл, содержащий даты в формате «день/месяц/год», причем под день и месяц отводится по две позиции, а под год — четыре (например, «16/04/2001»). Создать два файла целых чисел, первый из которых содержит значения дней, а второй — значения месяцев для дат из исходного строкового файла (в том же порядке).

При выполнении заданий на языках C# и VB.NET предполагается, что элементы в двоичном строковом файле всегда имеют одинаковую длину, равную 80 символам (строки меньшей длины дополняются справа пробелами). Это обеспечивает возможность прямого доступа к файловой строке по ее номеру, однако требует удаления завершающих пробелов после считывания строки из файла (для удаления завершающих пробелов можно использовать метод TrimEnd() класса String). Соответственно, перед записью строки в двоичный файл необходимо дополнить ее справа пробелами до 80 символов (для этого достаточно использовать метод PadRight(80) класса String). Помимо символов в строковом файле также хранится информация о длине каждой строки. В случае строки длины 80 эта информация кодируется 1 байтом, поэтому размер каждого элемента в строковом файле в данном случае будет равен 81 байту. Таким образом, если при выполнении задания требуется определить количество элементов в строковом файле, то достаточно разделить размер файла (в байтах) на 81.

Следует подчеркнуть, что условие на длину элементов строковых файлов накладывается задачником Programming Taskbook; в обычных программах, не связанных с выполнением учебных заданий, в строковые файлы можно записывать строки любой длины.

При выполнении заданий на обработку строковых файлов, содержащих русские буквы, необходимо устанавливать для них формат кодирования System.Text.Encoding.Default (данный формат соответствует кодировке ANSI, используемой системой Windows по умолчанию). Формат кодирования указывается в качестве последнего параметра в конструкторах потоков BinaryReader и BinaryWriter. Если этого не сделать, то строки с русскими буквами будут неверно считываться из файлов (и записываться в них).

В задании File67 строки, содержащиеся в исходном строковом файле, включают только цифры и символы «/», поэтому устанавливать формат кодирования System.Text.Encoding.Default для данного файла не требуется. Кроме того, при обработке этих строк нет необходимости удалять из них завершающие пробелы. Сразу приведем правильное решение задания File67, учитывающее отмеченные выше особенности строковых файлов (предполагается, что в программе, решающей задание, с помощью директивы uses для C# или Imports для VB.NET указано пространство имен System.IO, как и при решении задания File48):

[C#]

public static void Solve()
{
    Task("File67");
    BinaryReader
        f = new BinaryReader(File.OpenRead(GetString()));
    BinaryWriter
        f1 = new BinaryWriter(File.Create(GetString())),
        f2 = new BinaryWriter(File.Create(GetString()));
    for (long i = 1; i <= f.BaseStream.Length / 81; i++)
    {
        string s = f.ReadString();
        f1.Write(int.Parse(s.Substring(0, 2)));
        f2.Write(int.Parse(s.Substring(3, 2)));
    }
    f.Close();
    f1.Close();
    f2.Close();
}

[VB.NET]

Sub Solve()
    Task("File67")
    Dim f As New BinaryReader(File.OpenRead(GetString())), _
        f1 As New BinaryWriter(File.Create(GetString())), _
        f2 As New BinaryWriter(File.Create(GetString()))
    For i As Long = 1 To f.BaseStream.Length \ 81
        Dim s As String = f.ReadString()
        f1.Write(Integer.Parse(s.Substring(0, 2)))
        f2.Write(Integer.Parse(s.Substring(3, 2)))
    Next
    f.Close()
    f1.Close()
    f2.Close()
End Sub

В данной программе для перебора всех строк, содержащихся в исходном файле, использован не цикл while (как в примере решения задания File48), а цикл for, число итераций которого равно количеству строк, содержащихся в файле. Для нахождения этого количества свойство f.BaseStream.Length, равное размеру исходного файлового потока в байтах, делится на 81, т. е. на размер элемента строкового файла (80 символов плюс байт с информацией о длине строки). Поскольку свойство Length имеет тип «длинное целое» (long в C#, Long в VB.NET), именно такой тип указан для параметра цикла i.

Текстовые файлы

В качестве примера задания на текстовые файлы рассмотрим задание Text21.

Text21°. Дан текстовый файл, содержащий более трех строк. Удалить из него последние три строки.

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

Далее, поскольку строки, содержащиеся в текстовом файле, могут иметь различную длину, для определения числа строк необходимо последовательно считать из файла все его строки. Заметим, что для чтения данных из текстовых файлов следует использовать не класс BinaryReader (как для двоичных строковых файлов), а класс StreamReader. Аналогично, для записи данных в текстовый файл надо использовать класс StreamWriter. При создании этих текстовых потоков не требуется указывать базовый файловый поток типа FileStream; достаточно указать имя файла и (для StreamWriter) дополнительный параметр логического типа, определяющий режим открытия файла: значение false означает режим перезаписи (прежнее содержимое файла пропадает), а значение true — режим дополнения (прежнее содержимое файла дополняется новыми данными). Если файл, указанный в конструкторе потока StreamWriter, не существует, то он автоматически создается, и в этой ситуации режим его открытия может быть любым.

При выполнении заданий на текстовые файлы необходимо указывать формат кодирования System.Text.Encoding.Default, поскольку в большинстве заданий исходные текстовые файлы содержат текст на русском языке (данный формат указывается в качестве последнего параметра в конструкторах классов StreamReader и StreamWriter). Для имени формата кодирования можно использовать краткую форму: Encoding.Default, однако в этом случае необходимо в начале программы с помощью директивы uses (для C#) или Imports (для VB.NET) указать пространство имен System.Text.

Приведем решение задания Text21, учитывающее отмеченные выше особенности текстовых файлов (предполагается, что в программе, решающей задание, указаны пространства имен System.IO и System.Text):

[C#]

public static void Solve()
{
    Task("Text21");
    string s1 = GetString(), s2 = "$T21$.tmp";
    int n = 0;
    StreamReader f1 = new StreamReader(s1, Encoding.Default);
    StreamWriter f2 = new StreamWriter(s2, false,
        Encoding.Default);
    while (f1.Peek() != -1)
    {
        f1.ReadLine();
        n++;
    }
    f1.BaseStream.Position = 0;
    for (int i=0; i < n - 3; i++)
        f2.WriteLine(f1.ReadLine());
    f1.Close();
    f2.Close();
    File.Delete(s1);
    File.Move(s2, s1);
}

[VB.NET]

Sub Solve()
    Task("Text21")
    Dim s1 As String = GetString(), _
        s2 As String = "$T21$.tmp", n As Integer = 0, _
        f1 As New StreamReader(s1, Encoding.Default), _
        f2 As New StreamWriter(s2, False, Encoding.Default)
    Do While f1.Peek() <> -1
        f1.ReadLine()
        n += 1
    Loop
    f1.BaseStream.Position = 0
    For i As Integer = 1 To n - 3
        f2.WriteLine(f1.ReadLine())
    Next
    f1.Close()
    f2.Close()
    File.Delete(s1)
    File.Move(s2, s1)
End Sub

Для определения того, достигнут ли конец текстового файла, обычно используется метод Peek, возвращающий код текущего файлового символа, но не перемещающий файловый указатель на следующий символ; при достижении конца файла данный метод возвращает –1. Следует также обратить внимание на оператор f1.BaseStream.Position = 0, который обеспечивает переход на начало текстового потока f1 перед повторным считыванием его данных.

Приведенный выше вариант решения является неэффективным, поскольку требует двух просмотров исходного файла f1: первый — для определения его размера, который записывается в переменную n, второй — для создания вспомогательного файла f2, содержащего все строки исходного файла, кроме трех последних.

Задание Text21 можно выполнить и за один просмотр исходного файла, если воспользоваться следующим наблюдением: строка должна быть записана во вспо- могательный файл, если после нее в исходном файле находятся по крайней мере три строки. Таким образом, записывать очередную строку во вспомогательный файл следует только после считывания из исходного файла трех следующих за ней строк. Благодаря такому упреждающему считыванию необходимость в предварительном определении размера исходного файла отпадает. Для хранения строк, которые уже считаны из исходного файла, но еще не записаны во вспомогательный файл, удобно использовать массив из трех элементов строкового типа.

Приведем программу, реализующую описанный выше эффективный однопроходный алгоритм решения задания:

[C#]

public static void Solve()
{
    Task("Text21");
    string[] s = new String[3];
    string s1 = GetString(), s2 = "$T21$.tmp";
    int n = 0;
    StreamReader f1 = new StreamReader(s1, Encoding.Default);
    StreamWriter f2 = new StreamWriter(s2, false,
        Encoding.Default);
    for (int i= 0; i < 3; i++)
        s[i] = f1.ReadLine();
    while (f1.Peek() != -1)
    {
        f2.WriteLine(s[n]);
        s[n] = f1.ReadLine();
        n = (n + 1) % 3;
    }
    f1.Close();
    f2.Close();
    File.Delete(s1);
    File.Move(s2, s1);
}

[VB.NET]

Sub Solve()
    Task("Text21")
    Dim s(2) As String, s1 As String = GetString(), _
        s2 As String = "$T21$.tmp", n As Integer = 0, _
        f1 As New StreamReader(s1, Encoding.Default), _
        f2 As New StreamWriter(s2, False, Encoding.Default)
    For i As Integer = 0 To 2
        s(i) = f1.ReadLine()
    Next
    Do While f1.Peek() <> -1
        f2.WriteLine(s(n))
        s(n) = f1.ReadLine()
        n = (n + 1) Mod 3
    Loop
    f1.Close()
    f2.Close()
    File.Delete(s1)
    File.Move(s2, s1)
End Sub

PrevNext

 

Рейтинг@Mail.ru

Разработка сайта:
М. Э. Абрамян, В. Н. Брагилевский

Последнее обновление:
11.10.2011