pascalabcnet/PABCNetHelp/ForEducation/CheckedTasks/ctfile.html

344 lines
21 KiB
HTML
Raw Normal View History

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title></title>
<link rel="StyleSheet" href="../../default.css">
</head>
<body>
<H1>Задание на обработку файлов</H1>
<hr>
<h3>Создание программы-заготовки и знакомство с заданием</h3>
<p>В качестве примера задания на обработку файлов рассмотрим задание File48.</p>
<p>Напомним, что программу-заготовку для решения этого задания можно создать с помощью
команды
меню &laquo;Модули | Создать шаблон программы&raquo;, кнопки
<img border="0" src="gif/Load.gif" width="16" height="17"> или клавиатурной
комбинации <b>Shift+Ctrl+L</b>. Эта заготовка будет иметь следующий вид:
</p>
<blockquote>
<code><pre>
<b>uses</b> PT4;
<b>begin</b>
Task('File48');
<b>end</b>.</pre></code>
</blockquote>
<p>После запуска данной программы на экране появится <a href="../PT4/window.html">окно задачника</a>:</p>
<blockquote>
<img border="0" src="gif/file48-1.png">
</blockquote>
<p>В первой строке раздела исходных данных указаны имена трех исходных файлов (S<sub>A</sub>, S<sub>B</sub>
и S<sub>C</sub>) и одного результирующего (S<sub>D</sub>). В последующих строках раздела исходных данных
показано содержимое исходных файлов.
Элементы файлов отображаются бирюзовым цветом, чтобы подчеркнуть их отличие от обычных исходных данных (желтого цвета)
и комментариев (светло-серого цвета).</p>
<p>Поскольку размер файлов, как правило, превышает количество элементов,
которое может уместиться на одной экранной строке,
для отображения содержимого файла может отводиться
более одной экранной строки. Слева от каждой строки с содержимым файла указывается порядковый номер
файлового элемента, значение которого указано первым в этой строке (элементы нумеруются от 1).</p>
<p>Запуск нашей программы признан <i>ознакомительным</i> (и поэтому правильность решения не анализировалась),
так как в ходе ее выполнения не было введено ни одного элемента исходных данных.
При ознакомительном запуске раздел результатов не отображается, однако приводится
<i>пример верного решения</i>, т.&nbsp;е. те числа,
которые должны содержаться в результирующем файле при правильной обработке исходных файлов.</p>
</p>
<hr>
<h3>Ввод части исходных данных</h3>
<p>Добавим в программу фрагмент, позволяющий ввести имена исходных файлов и связать с этими файлами соответствующие
файловые переменные. Поскольку мы собираемся работать с четырьмя файлами одного типа, удобно предусмотреть <i>массив</i>
для хранения всех файловых переменных:</p>
<blockquote>
<code><pre>
<b>uses</b> PT4;
<b>var</b>
i: integer;
s: string;
f: <b>array </b>[1..4] <b>of</b> <b>file</b> <b>of</b> integer;
<b>begin</b>
Task('File48');
<b>for</b> i := 1 <b>to</b> 3 <b>do</b>
<b>begin</b>
read(s);
Assign(f[i], s);
<b>end</b>;
<b>end</b>. </pre></code>
</blockquote>
<p>Мы намеренно ограничились <i>тремя</i> итерациями цикла, оставив непрочитанным имя результирующего файла.
Считывание имен файлов производится в одну и ту же переменную <code>s</code>, поскольку после связывания файла,
имеющего имя <code>s</code>,
с соответствующей файловой переменной (процедурой <code>Assign</code>) все остальные действия
с данным файлом в нашей программе
будут осуществляться с использованием файловой переменной, без обращения к имени файла.</p>
<p>Запуск нового варианта программы уже не будет считаться ознакомительным, поскольку в программе
выполняется ввод исходных данных. Так как имя результирующего файла осталось непрочитанным, этот вариант
решения будет признан неверным и приведет к сообщению &laquo;<i>Введены не все требуемые исходные данные</i>&raquo;:</p>
<blockquote>
<img border="0" src="gif/file48-2.png">
</blockquote>
<p>При этом на экране появится раздел результатов (кроме комментария он пока ничего не содержит),
а также панель индикаторов. Первый из индикаторов
(индикатор ввода) показывает
количество введенных исходных данных. Обратите внимание на то, что второй индикатор
(индикатор вывода) является неактивным: он выделяется серым цветом более светлого
оттенка и не содержит текстового заголовка.
Это объясняется тем, что индикатор вывода показывает количество результирующих данных,
полученных задачником от программы, а в нашем случае программа
не должна передавать задачнику никакие данные; вместо этого ей необходимо создать файл и заполнить его
требуемыми значениями. Для заданий подобного типа (обычно это задания, связанные с обработкой файлов)
индикатор вывода не используется.
</p>
<hr>
<h3>Ввод всех исходных данных без создания требуемого файла</h3>
<p>Изменим программу, заменив в заголовке цикла число <code>3</code> на <code>4</code>, и вновь запустим программу. Теперь все данные,
необходимые для выполнения задания, введены в программу (это видно по индикатору ввода). Однако задание не выполнено, поскольку результирующий файл
не создан. Поэтому решение опять признано ошибочным с диагностикой &laquo;<i>Результирующий файл не найден</i>&raquo;:</p>
<blockquote>
<img border="0" src="gif/file48-3.png">
</blockquote>
<hr>
<h3>Пример программы, приводящей к ошибке времени выполнения</h3>
<p>Добавим в тело цикла после процедуры <code>Assign</code> вызов процедуры <code>Reset</code>, обеспечивающий открытие существующего файла:</p>
<blockquote>
<code><pre>
<b>uses</b> PT4;
<b>var</b>
i: integer;
s: string;
f: <b>array </b>[1..4] <b>of</b> <b>file</b> <b>of</b> integer;
<b>begin</b>
Task('File48');
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
<b>begin</b>
read(s);
Assign(f[i], s);
Reset(f[i]);
<b>end</b>;
<b>end</b>. </pre></code>
</blockquote>
<p>Теперь запуск программы приведет к сообщению об ошибке &laquo;<i>Error System.IO.FileNotFoundException</i>&raquo;:</p>
<blockquote>
<img border="0" src="gif/file48-4.png">
</blockquote>
<p>Сообщение, начинающееся со слова <i>Error</i>, означает, что при работе программы произошла <i>ошибка времени выполнения</i>
(Runtime Error). После слова <i>Error</i> указывается имя этой ошибки (в данном случае <i>System.IO.FileNotFoundException</i>,
то есть ошибка ввода-вывода, связанная с тем, что файл не найден)
и краткое ее описание на английском языке.</p>
<p>Сообщение об ошибке времени выполнения появится и в разделе &laquo;Список ошибок&raquo;
окна <b>PascalABC.NET</b>:</p>
<blockquote>
<img border="0" src="gif/file48-5.png">
</blockquote>
<hr>
<h3>Создание пустого результирующего файла</h3>
<p>Для того чтобы избежать ошибки времени выполнения, отсутствующий файл результатов следует открыть
не процедурой <code>Reset</code>, а процедурой <code>Rewrite</code>, которая
и обеспечит создание этого файла. Далее, после завершения работы с файлами, открытыми в программе,
их необходимо закрыть процедурой <code>Close</code>. Добавим в программу соответствующие операторы:</p>
<blockquote>
<code><pre>
<b>uses</b> PT4;
<b>var</b>
i: integer;
s: string;
f: <b>array </b>[1..4] <b>of file of</b> integer;
<b>begin</b>
Task('File48');
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
<b>begin</b>
read(s);
Assign(f[i], s);
<b>if</b> i &lt; 4 <b>then</b> Reset(f[i])
<b>else</b> Rewrite(f[i]);
<b>end</b>;
{ * }
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
Close(f[i]);
<b>end</b>. </pre></code>
</blockquote>
<p>Комментарий <code>{ * }</code> расположен в том месте программы, в котором можно выполнять операции ввода-вывода для всех
четырех файлов: они уже открыты процедурами <code>Reset</code> или <code>Rewrite</code> и еще не закрыты процедурой <code>Close</code>.</p>
<p>Запуск этого варианта программы не приведет к ошибке времени выполнения; более того, результирующий файл будет создан.
Однако созданный файл останется пустым, то есть не содержащим ни одного элемента. Поэтому при запуске программы
на информационной панели появится сообщение &laquo;<i>Ошибочное решение</i>&raquo;,
а в строке, которая должна содержать элементы результирующего файла, появится текст <code>EOF:</code> (особое значение EOF для
указателя текущей файловой позиции означает, что данный файл существует, но не содержит ни одного элемента):</p>
<blockquote>
<img border="0" src="gif/file48-6.png">
</blockquote>
<hr>
<h3>Пример программы, использующей неправильные типы для файловых данных</h3>
<p>Во всех ранее рассмотренных вариантах программы мы не использовали операции ввода-вывода для файлов.
Поэтому тип файлов не играл никакой роли: вместо типа <code><b>file of</b> integer</code> мы могли использовать любой другой файловый тип,
например, <code><b>file of</b> real</code>, и результат выполнения программы был бы тем же самым.</p>
<p>Тип файловых элементов становится принципиально важным, если в программе используются операции ввода-вывода
для данного файла. Чтобы продемонстрировать это на примере нашей программы, внесем в нее следующие изменения:
в описании массива <code>f</code> файловых переменных тип <code>integer</code> заменим на <code>real</code>, в раздел описаний добавим описание
переменной <code>a</code> типа <code>real</code>, в раздел операторов (в позицию, помеченную комментарием <code>{ * }</code>)
добавим следующий фрагмент:</p>
<blockquote>
<code><pre>
<b>for</b> i := 1 <b>to</b> 3 <b>do</b>
<b>begin</b>
read(f[i], a);
write(f[4], a);
<b>end</b>;
</pre></code>
</blockquote>
<p>Данный фрагмент обеспечивает считывание <i>одного</i> элемента для каждого из трех исходных файлов
и запись этих элементов в результирующий файл (в требуемом порядке). Подчеркнем, что мы <i>неправильно</i> указали
типы файлов; тем не менее, компиляция программы пройдет успешно, а после ее запуска не произойдет
ошибок времени выполнения.</p>
<p>Результат работы программы будет неожиданным:</p>
<blockquote>
<img border="0" src="gif/file48-7.png">
</blockquote>
<p>Судя по экранной строке с содержимым результирующего файла, в него будут записаны не три, а <i>шесть элементов</i>,
по два начальных элемента из каждого исходного файла. Объясняется это тем, что после связывания файлов
с файловыми переменными типа <code><b>file of</b> real</code> элементами файлов стали считаться <i>вещественные числа</i>
(занимающие в памяти по 8 байтов), тогда как &laquo;на самом деле&raquo;, то есть по условию задания, элементами файлов
являются целые числа (занимающие в памяти по 4 байта). Поэтому считывание из файла и последующая запись в файл
одного &laquo;вещественного элемента&raquo; фактически приводит к считыванию и записи блока данных размером 8 байтов,
содержащего <i>два</i> последовательных целочисленных элемента исходного файла.</p>
<p>Итак, мы выяснили, что ошибки, связанные с несоответствием типов файлов, не выявляются при компиляции
и не всегда приводят к ошибкам времени выполнения. Это следует иметь в виду, и при появлении &laquo;странных&raquo;
результирующих данных начинать поиск ошибки с проверки типов файловых переменных.</p>
<hr>
<h3>Исправление ошибки, связанной с неверными типами файловых данных</h3>
<p>Заменим в нашей программе все описания <code>real</code> на <code>integer</code>:</p>
<blockquote>
<code><pre>
<b>uses</b> PT4;
<b>var</b>
i: integer;
s: string;
f: <b>array </b>[1..4] <b>of file of</b> integer;
a: integer;
<b>begin</b>
Task('File48');
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
<b>begin</b>
read(s);
Assign(f[i], s);
<b>if</b> i &lt; 4 <b>then</b> Reset(f[i])
<b>else</b> Rewrite(f[i]);
<b>end</b>;
<b>for</b> i := 1 <b>to</b> 3 <b>do</b>
<b>begin</b>
read(f[i], a);
write(f[4], a);
<b>end</b>;
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
Close(f[i]);
<b>end</b>. </pre></code>
</blockquote>
<p>Мы получим все еще неверное, но вполне &laquo;понятное&raquo; решение: первые три элемента результирующего файла
совпадают с контрольными (то есть &laquo;правильными&raquo;), а прочие элементы отсутствуют:</p>
<blockquote>
<img border="0" src="gif/file48-8.png">
</blockquote>
<hr>
<h3>Верное решение</h3>
<p>Приведем, наконец, верное решение задания
<code>File48</code>:</p>
<blockquote>
<code><pre>
<b>uses</b> PT4;
<b>var</b>
i, a: integer;
s: string;
f: <b>array </b>[1..4] <b>of file of</b> integer;
<b>begin</b>
Task('File48');
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
<b>begin</b>
read(s);
Assign(f[i], s);
<b>if</b> i &lt; 4 <b>then</b> Reset(f[i])
<b>else</b> Rewrite(f[i]);
<b>end</b>;
<b>while</b> <b>not</b> Eof(f[1]) <b>do</b>
<b>for</b> i := 1 <b>to</b> 3 <b>do</b>
<b>begin</b>
read(f[i], a);
write(f[4], a);
<b>end</b>;
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
Close(f[i]);
<b>end</b>. </pre></code>
</blockquote>
<p>От предыдущего варианта данное решение отличается добавлением заголовка цикла <code><b>while not</b> Eof(f[1]) <b>do</b></code>,
который обеспечивает считывание всех элементов из исходных файлов (напомним, что по условию задания все исходные
файлы имеют <i>одинаковый размер</i>) и запись их в результирующий файл в нужном порядке.
После запуска этого варианта мы получим сообщение &laquo;<i>Верное решение. Тест номер 1 (из 5)</i>&raquo;,
а после пяти подобных запусков &#151; сообщение &laquo;<i>Задание выполнено!</i>&raquo;:</p>
<blockquote>
<img border="0" src="gif/file48-9.png">
</blockquote>
<hr>
<h3>Просмотр результатов выполнения задания</h3>
<p>Щелкнув мышью на метке &laquo;Результаты (F2)&raquo;, расположенной в правом верхнем
углу окна задачника, или нажав клавишу <b>F2</b>, мы можем вывести на экран <i>окно результатов</i>,
в котором будет перечислены все наши попытки решения задачи:</p>
<blockquote>
<code><pre>
File48 a08/09 12:43 Ознакомительный запуск.
File48 a08/09 12:50 Введены не все требуемые исходные данные.
File48 a08/09 12:52 Результирующий файл не найден.
File48 a08/09 12:53 Error System.IO.FileNotFoundException.
File48 a08/09 12:57 Ошибочное решение.--3
File48 a08/09 13:06 Задание выполнено!
</pre></code>
</blockquote>
<p>Для закрытия окна результатов достаточно нажать клавишу <b>Esc</b>.
Окно результатов можно отобразить на экране и после закрытия окна задачника и возврата в среду PascalABC.NET.
Для этого надо использовать команду меню &laquo;Модули | Просмотреть результаты&raquo;, кнопку
<img border="0" src="gif/Results.gif" width="16" height="17"> или клавиатурную
комбинацию <b>Shift+Ctrl+R</b>.
</p>
</body>
</html>