Этап 3. Ввод вопросов из файла Файл 147340219 С. 1 из 5 Этап 3. Ввод вопросов из файла Файловая структура приложения остается той же (см. этап 2, п. 2.1.2, рис. 2.1). Рекомендация. Результаты каждого этапа разработки удобно хранить в отдельном каталоге. Основным содержанием этапа является решение задачи формирования и предъявление вопроса (этап 2, общая схема работы приложения, задача А2.2, рис. 2.3). Разработка выполняется по шагам. Выполнив коррекцию приложения на очередном шаге, отладьте и оттестируйте полученную программу. Только убедившись в правильности ее работы, переходите к следующему шагу! При описании отдельных шагов приводятся только фрагменты программы, где вносимые изменения заключены в рамку; остальной текст служит для определения места изменения. 3.1. Уточнение формы хранения вопросов в файле Хранящиеся в файле вопросы могут быть пронумерованы, т.е. номера могут входить в строку с вопросом. Это корректнее с точки зрения контроля правильности набора вопросов (особенно если их много) и проще с точки зрения вывода вопроса с номером. Другой вариант – хранение в файле ненумерованных вопросов и вывод номеров отдельно от вопросов. Минусы этого варианта – большая вероятность ошибок при наборе вопросов и проверке правильности файла. Однако этот вариант более интересен с методической точки зрения и более гибок. Его плюсы: - можно продемонстрировать возможности форматного вывода переменных разных типов; - имеется возможность вывода того же номера в различных видах, например, в тексте «Вы ответили на ... вопросов из...»; - удобно отслеживать изменение номеров, т.е. вывод заодно является отладочным. Компромиссный вариант: создать файл с нумерованными вопросами, но номер вопроса выводить отдельно как значение переменной $num. В процессе отладки дублирование номеров только улучшит контроль правильности вывода вопросов. Так сейчас и сделано в примере. По завершении этого шага отладки номера из файла убрать и еще раз проверить правильность вывода вопросов. Файл с текстом вопросов прилагается к материалам этапа 3. 3.2. Замена заглушки задачи А2.2 чтением первого вопроса из файла 3.2.1. Вопрос размещен в одной строке файла Файл должен быть открыт, обработан и закрыт в процессе работы модуля, т.е. в течение одного сеанса взаимодействия с сервером. Если файл не закрыт программным путем, он будет закрыт автоматически, но, возможно, с непредсказуемыми последствиями в виде искажения или потери данных. Для работы с файлом вопросов используем следующие локальные переменные модуля: $files – строка, содержащая имя файла с вопросами; используется только для более общей записи оператора открытия файла; $fp – указатель на обрабатываемый файл. Для чтения строк из файла следует использовать функцию fgets. Форматная функция fscanf читает строку только до первого пробела. Модификация вывода вопроса в связи с раздельным выводом номера и текста вопроса Способов вывести номер строки и самое строку как значения соответствующих переменных – множество. Как один из вариантов по аналогии с языком Си в примере используется форматный вывод. Однако, поскольку вывод осуществляется не на экран, а на страницу, которая затем отображается броузером, возникают некоторые накладки. Броузер отображает не более одного символа пробела из идущих подряд в строке. Все остальные пробелы, идущие последовательно, присутствуют в Этап 3. Ввод вопросов из файла Файл 147340219 С. 2 из 5 коде страницы, но не важны для броузера. Чтобы их отобразить, надо заменить их на специальный символ &nbsp; - т.н. «жесткий пробел». Следующий момент связан с указанием количества позиций для целого числа. Для номеров вопросов записан формат %2d, т.е. вывод в 2 позиции; для однозначных номеров в этом случае первым символом должен быть пробел, но он в броузере не отображается. Поэтому в строку формата включен символ &nbsp, после чего номер корректно отображается в двух позициях. Ведущие обычные пробелы присутствуют только в коде страницы. Поскольку данные в Интернете в силу их разнородности рациональнее всего рассматривать как строки, форматные операторы – это решение только для ситуаций, где оно эффективно. В данном случае оно не самое рациональное, но приведено как возможный вариант. Другие способы основаны на том, что данные не имеют заданного заранее типа, а трактуются в соответствии с получаемыми ими значениями. Это позволяет формировать выводимую строку как последовательность текстов и переменных и использовать для вывода этой строки более простые операторы print или echo. Две модификации такого способа вывода также приводятся. Изменения в файле voprosy.php <!-- Далее начинается динамически формируемая часть страницы --> <?php //Присвоение переменным программы переданных значений из внешнего массива $HTTP_POST_VARS $num_max=$HTTP_POST_VARS['num_max']; $num=$HTTP_POST_VARS['num']; $ball=$HTTP_POST_VARS['ball']; //Формирование номера текущего вопроса $num++; if ($num<=$num_max) //вопросы не исчерпаны { //Формирование страницы с вопросом //Чтение из файла с именем $files и указателем $fp вопроса $vopr $files="FILES/voprosy.txt"; $fp=fopen($files, "r"); //Открытие файла для чтения //Чтение текущего вопроса из файла $vopr=fgets($fp,255); fclose($fp); //Закрытие файла print("<font size=\"3\">\n"); //Вывод номера и текста текущего вопроса printf(" &nbsp;%2d. %s \n",$num,$vopr); //Другой способ вывода номера и текста текущего вопроса $vopr="&nbsp;$num. $vopr"; // Дополнение строки номером print($vopr); //Вывод строки Выбирается одно из трех //И еще короче print("&nbsp;$num. $vopr"); print(" </font>\n"); //Вывод на страницу формы для ответа ........................................... Отладка Выберите вариант вывода по вкусу. Внесите изменения в текст программы. После исправления возможных ошибок приложение должно формировать такую же страницу с вопросом, как и на этапе 2. Возможные отклонения должны быть минимальными и оправданными. Так, в примере при форматном выводе неизбежно получается лишний пробел перед номером, зато однозначные номера сдвинуты на позицию по сравнению с двузначными (хотя это было бы существенным, только если бы они выводились один под другим в столбик). В альтернативных вариантах номер всегда печатается с одной позиции. Этап 3. Ввод вопросов из файла Файл 147340219 С. 3 из 5 3.2.2. Вопрос размещен в нескольких строках файла (или: вместе с вопросом должно быть считано и выведено еще несколько строк) В общем случае речь идет просто о считывании из файла и выводе на динамически формируемую HTML-страницу нескольких строк, будь то строки вопроса или иная информация. Для определенности рассмотрим следующую модификацию нашего теста. Предположим, вопросы теста таковы, что вместо однотипных ответов «всегда», «часто», «иногда», «никогда» каждому вопросу сопоставлено 4 различных ответа (свои для каждого вопроса); верен только один ответ. В таком случае просто занести тексты ответов в форму нельзя. Самым простым решением будет: - вывод на страницу вопроса и нумерованных вариантов ответов; - сопоставление радиокнопкам формы номеров вариантов ответов. В файл вопросов вслед за вопросом можно занести варианты ответов. Поскольку число радиокнопок формы фиксировано, фиксированным должно быть и число вариантов ответов. Пусть для определенности каждому вопросу в файле вопросов соответствует 5 строк текста – строка с вопросом и 4 строки с четырьмя вариантами ответов. Тогда для чтения текста «вопрос + ответы» операции чтения и вывода на страницу должны быть повторены 5 раз, что можно реализовать с помощью цикла. Предлагаемый вариант, построенный на основе чтения одной строки, приведен ниже. Пусть по-прежнему считываемая строка называется $vopr. Пусть $k – номер текущей считываемой строки, относящейся к обрабатываемому вопросу. Целесообразно точки зрения контроля правильности пронумеровать и вопросы, и варианты ответов непосредственно в файле. Тогда содержимое файла вопросов примет вид: 1. Я чувствую постоянную усталость, вызванную требованиями ко мне на работе и дома. 1) Всегда 2) Часто 3) Иногда 4) Никогда ............. В исходном варианте сценария вопрос считывался из файла, файл закрывался, и после этого вопрос выводился на страницу. Чтобы сохранить это разделение ввода и вывода и в минимальной степени менять сценарий, в результате ввода должны быть получены 5 строк. Этого можно добиться двумя способами: - использовать массив строк; тогда для вывода массива потребуется использовать специальные конструкции языка PHP; - по мере считывания строк присоединять их к уже считанным; в итоге получим одну объединенную строку. Второй вариант в данном случае значительно разумнее и проще. Объединение строк осуществляется операцией «точка»: между объединяемыми строками ставится точка. Выводимые строки должны быть разделены на странице тегом <br>, который присоединим к уже сформированной части вопроса. Фрагмент сценария с вводом и выводом вопроса и ответов: …………. //Формирование страницы с вопросом //Чтение из файла с именем $files и указателем $fp вопроса $vopr и вариантов ответа $files="FILES/voprosy.txt"; $fp=fopen($files, "r"); //Открытие файла для чтения //Чтение текущего вопроса из файла $vopr=""; //очистка строки вопроса for ($k=1; $k<=5; $k++) //Чтение 5 строк: вопроса и 4-х вариантов ответа $vopr="$vopr<br>".fgets($fp,255); //Чтение текущей строки с присоединением ее к предыдущим Этап 3. Ввод вопросов из файла Файл 147340219 С. 4 из 5 fclose($fp); //Закрытие файла print("<font size=\"3\">\n"); //Вывод номера и текста текущего вопроса printf(" &nbsp; %s \n", $vopr); //Вывод вопроса и 4-х вариантов ответа, объединенных в одну строку print(" </font>\n"); //Вывод на страницу формы для ответа ........................................... Замечание. От того, по какой схеме будут располагаться правильные ответы, будет зависеть сложность их анализа. Например, если всегда располагать правильный ответ под номером 3, то число правильных ответов будет равно общему числу нажатий третьей радиокнопки. При произвольном размещении правильного ответа можно все вопросы разбить не более чем на 4 группы: вопросы, где правильным является ответ № 1; вопросы, где правильным является ответ № 2 и т.д. 3.3. Чтение всех вопросов теста из файла Общая схема При открытии файла его указатель устанавливается на начало первого вопроса, тогда как нам нужно положение начала вопроса с номером $num. В данном случае самый простой и надежный способ установки указателя на начало нужного вопроса – пропустить уже обработанные вопросы, т.е. последовательно считать их без обработки. Прямой доступ, т.е. установка указателя в нужное положение посредством соответствующей функции, в данном случае невозможен или крайне нецелесообразен. Просто рассчитать это положение, исходя из номера вопроса, нельзя, так как строки-вопросы имеют разные длины. Можно было бы сохранять и передавать через внешний массив положение указателя по завершении чтения очередного вопроса; тогда потребовалось бы включить в форму еще один скрытый элемент или использовать иные средства передачи данных. Кроме того, нет уверенности, что при этом не возникнет извечных проблем с обработкой конца строки .. и т.д. и т.п. Пусть $i – текущий номер пропускаемого вопроса (локальная переменная модуля). Добавления в файле voprosy.php <!-- Далее начинается динамически формируемая часть страницы --> <?php //Присвоение переменным программы переданных значений из внешнего массива $HTTP_POST_VARS $num_max=$HTTP_POST_VARS['num_max']; $num=$HTTP_POST_VARS['num']; $ball=$HTTP_POST_VARS['ball']; //Формирование номера текущего вопроса $num++; if ($num<=$num_max) //вопросы не исчерпаны { //Формирование страницы с вопросом //Чтение из файла с именем $files и указателем $fp текущего вопроса $vopr $files="FILES/voprosy.txt"; $fp=fopen($files, "r"); //Открытие файла для чтения // Пропуск ($num-1)обработанных вопросов < Пропуск ($num-1)обработанных вопросов > //Чтение текущего вопроса из файла < Чтение текущего вопроса из файла > fclose($fp); //Закрытие файла print("<font size=\"3\">\n"); //Вывод номера и текста текущего вопроса < Вывод номера и текста текущего вопроса > print(" </font>\n"); //Вывод на страницу формы для ответа ……………….. Подзадачи, решение которых зависит от числа строк вопроса, выделены жирным шрифтом. Пусть номер вопроса включен в текст вопроса (т.е. вопросы пронумерованы в файле). Этап 3. Ввод вопросов из файла Файл 147340219 С. 5 из 5 Вопрос размещен в одной строке файла Пропуск ($num-1)обработанных вопросов for ($i=1; $i<=$num-1; $i++) $vopr=fgets($fp, 255); Чтение текущего вопроса из файла $vopr=fgets($fp,255); Вывод номера и текста текущего вопроса printf(" То же, что в п. 3.2.1 &nbsp;%2d. %s \n",$num,$vopr); Вопрос размещен в нескольких строках файла Пропуск ($num-1)обработанных вопросов for ($i=1; $i<=$num-1; $i++) //Чтение 5 строк for ($k=1; $k<=5; $k++) $vopr=fgets($fp,255); //Чтение текущей строки из файла Чтение текущего вопроса из файла $vopr=""; //очистка строки вопроса for ($k=1; $k<=5; $k++) //Чтение 5 строк: вопроса и 4-х вариантов ответа $vopr="$vopr<br>".fgets($fp,255); //Чтение текущей строки из файла с присоединением ее к предыдущим Вывод номера и текста текущего вопроса printf(" &nbsp; %s \n", $vopr); //Вывод вопроса и 4-х вариантов ответа, объединенных в одну строку Отладка Для отладки необходимо изменить константу $num_max в файле index.htm. Сначала разумно задать небольшое число вопросов (5 – 6). При правильной работе программы задать константу $num_max равной общему числу вопросов и протестировать программу еще раз.