WebMaster/Programming/Perl/
Обработка форм.
Ну вот ,вы уже знаете достаточно,кое в чем уже успели
приобрести опыт, пришло время перейти к очень важной теме - обработке
форм. При всей простоте (кажушейся) это едва ли не самое главное
предназначение всего стандарта CGI . Куда бы вы не зашли на любой
уважающий себя сайт,везде вы встретите формы, которые вам предложат
заполнить.В этом деле можно положится только на CGI, так как Java
и JavaScript ,выполняющиеся на страничке у клиента не имеют доступа
к серверу,на котором находится сайт.
Коротко вспомним о том что происходит при рассматриваемом процессе
поближе,так сказать на трезвую голову ;). Итак браузер требует
у сервера определенный URL (это может быть как простой документ,так
и сгенерированый CGI) в этом документе может содержаться форма.Отображая
такой документ браузер также выводит элементы формы (кнопки, поля
ввода, поля ввода пароля, переключатели, радио-кнопки, списки,
текстовые области,скрытые поля). И со всем этим добром пользователь
может взаимодействовать.К форме естественно имеет доступ и встроеный
язык программирования JavaScript -он может как использовать форму
для своих нужд,не передавая CGI,так и помогать пользователю в
заполнении формы.
После того,как пользователь заполнил форму он нажимат кнопку Submit
которая говорит, что форму надо отправить на сервер. Браузер собирает
все имена и значения элементов формы ,кодирует их методом urlencode
и в зависимости от указаного в тэге FORM метода вызывает GET или
POST с указаным URL,передавая ему данные. На сервере CGI-скрипту
это попадает (в зависимости от метода) либо в переменную QUERY_STRING
либо на STDIN.Скрипт может проверить данные ,занести их в какую
нибудь базу данных,может как yahoo выполнить какой-нибудь поиск,
может что-нибудь вычислить......да мало ли что он может,все зависит
только от нашей фантазии..... В конце концов скрипт выдает браузеру
ответ,который он и отображает.В этом ответе может содержаться
все что вашей душе угодно от сообщения об удачном или неправильном
запросе до таких ответов,по сравнению с которыми yahoo и altavista
подвиснут от зависти, главное чтоб вам и тем кто посещает ваш
сайт это нравилось.;)
Ну а теперь немного о синтаксисе элементов форм
,их описании и самое главное особенностях при обработке CGI-скриптом.
Итак немного экскурс в HTML:
FORM
<FORM action="http://......cgi" method="GET"|"POST"
enctype="encodingType" name="formName" target="windowName"
onSubmit="Handler"> </FORM>
Атрибуты:
action
как раз и задает тот URL,который будет и обрабатывать форму, если
он опущен,то текущий URL документа(а он-то может быть сгенерирован
нашим скриптом).
method
задает метод GET или POST
enctype
обычно не задается,для форм он application/x-www-form-urlencoded
-по умолчанию, и поддерживается всеми CGI скриптами.Но если вы
уж очень хотите чтобы браузер послал вам данные в другом формате
(например text/plain) то можете указать этот тип кодировки,только
потом не жалуйтесь,что ваш скрипт не может разделить поля,или
вообще начинает глючить когда пользователь ввел какой-то спецсимвол.
name
Задается для JavaScript,чтоб обращатся к форме по имени,а не по
номеру. Для CGI не играет ни какой роли,так как внутреннее для
браузера.
target
Может Определять в какой фрейм отправить полученую информацию.Имеет
значение во фреймосодержащих документах.Прозрачен для CGI обработки
данных.
onSubmit
Определяет JavaScript -обработчик активизации формы.Применяется
для проверки JavaScript'ом правильности заполнения.Опять таки
прозрачен для CGI.
Пример типичной формы: <FORM action="http://www.uic.nnov.ru/~paaa/cgi-bin/test.cgi"
method="POST"> .........Поля формы......... </FORM>
Форма может содержать элементы.Элементы имеют имена,которые
используются дл кодирования пар имя=значение.Некоторые Элементы
не передаются CGI,а используются JavaScript для управления,например
кнопки.Некоторые поля передаются только в тех случаях, когда в
них что-то выбрано,например списки и переключатели.Остальные поля
передаются всегда, даже когда они пустые.
Например: <FORM action="http://www.doom/cgi-bin/test.cgi">
Your Name:<INPUT name="Name"><BR> E-Mail:<INPUT
name="Email"><BR> Are you doomer:<INPUT
type="checkbox" name="doomer" value="Yes">
<INPUT type="submit" value="Send Form!">
</FORM>
Допустим вы ввели имя lesha и адрес paaa@uic.nnov.ru,при
этом выбрали переключатель После нажатия кнопки будет отправлен
вот такой запрос:
http://www.doom/cgi-bin/test.cgi?Name=lesha&Email=paaa@uic.nnov.ru&doomer=Yes
Если же вы не выбрали переключатель,то запрос будет таким:
http://www.doom/cgi-bin/test.cgi?Name=lesha&Email=paaa@uic.nnov.ru
,как видите элемент doomer не вошел в строку запроса
Теперь попробуйте оставить поля редактирования пустыми:
http://www.doom/cgi-bin/test.cgi?Name=&Email=
Эти элементы (Name и Email) присутствуют и сообщают что они пустые.
Кнопка(button)
<INPUT type="button" name="buttname" value="Текст
На Кнопке" onClick="Handler">
В форме изображается кнопка,при нажатии которой вызывается JavaScript-обработчик
заданый атрибутом onClick ,атрибут name служит для JavaScript-именования
кнопки а не дл передачи CGI.Так как значение кнопки не передается
CGI, value задает Текст,изображаемый на кнопке.
<FORM onSubmit="return false;"> <INPUT type="button"
value="Просто Кнопочка" onClick="alert('Нажали
на кнопку!');"> </FORM>
Submit
<INPUT type="submit" name="submitName"
value="Отправить Форму" onClick="Handler">
Кнопка,предназначеная для передачи формы.Опять же,сама не передается,а
служит только для управления. текст на ней задается атрибутом
value.
<FORM onSubmit="alert('Нечего Посылать!');return false;">
<INPUT type="Submit" value="Послать!">
</FORM>
Reset
<INPUT type="reset" name="resetName" value="Очистить"
onClick="Handler">
Кнопка очистки формы.При ее нажатиивсем измененым элементам возвращается
значение по умолчанию.
<FORM onSubmit="return false;"> <INPUT name="something"><BR>
<INPUT type="reset" value="Очистить!">
</FORM>
Поле ввода(text)
<INPUT [type="text"] name="textName" value="textValue"
size=число [обработчики]>
Применяется очень часто,поэтому тип "text" служит для
INPUT по умолчанию,его не надо каждый раз указывать.Имя поля,задаваемое
name является обязательным для CGI (в отличии от JavaScript,где
элементы формы можно индексировать по номерам,а имена для удобства
и читабельности кода служат).Можно задать значение по умолчанию
атрибутом value,которое будет после загрузки докумета.атрибут
size позволяет задать размер поля.Также может содержать обработчики
onBlur,onChange,onFocus,onSelect.
<FORM onSubmit="return false;"> <INPUT name="something"
size=30 value="Введите что-нибудь"> </FORM>
Текстовая Область(textarea)
<TEXTAREA name="textareaName" rows="число"
cols="число" wrap="hard"|"soft">
TextToEdit </TEXTAREA>
Область многострочного редактирования.Размеры в
строках и столбцах задаютс атрибутами rows и cols.Значения атрибута
wrap "hard" и "soft" -означают соответственно
мягкую или жесткую разбивку на строки (в большинстве случаев ето
не существенно). На что следует действительно обратить внимание
так это на символ,используемый для указания перехода на новую
строку. В Windows это '\r\n' а в Unix '\n',так что если это для
вас существенно,то приводите преобразование,например так:
$my_text =~ s/\r\n/\n/g;
<FORM onSubmit="return false;"> <TEXTAREA name="MyText"
rows=7 cols=30> Тут можно что-нибудь написать </TEXTAREA>
</FORM>
Тут можно что-нибудь написать
Поле ввода пароля(password)
<INPUT type="password" name="passName"
size=число value="passValue">
Очень похоже на поле ввода,отличается тем что вместо символов
в нем отображаютс символы '*'.Служит для ввода пользователем пароля.
<FORM onSubmit="return false;"> Пароль: <INPUT
type="password" name="yourpass" size=30>
</FORM>
Пароль:
Скрытое поле(hidden)
<INPUT type="hidden" name="hiddName" value="hidValue">
Поле не отображаемое на экране.Но оно имеет имя и значение и следовательно
передается в форму. Служит для того (и очень часто програмисты
его применяют) чтоб передавать скрипту какую нибудь информацию.Например,если
ваш скрипт обрабатывает несколько форм разных типов,то в скрытом
поле каждой формы можно указать с какой формой конкретно вы имеете
дело. Так как это ваша внутренняя кухня то нечего пользователю
мозолить глаза этой информацией.
<FORM onSubmit="return false;"> Этого сдесь вам
не видно,поле-скрытое. <INPUT type="hidden" name="formNum"
value="3"> </FORM>
Этого сдесь вам не видно,поле-скрытое.
Переключатель(checkbox)
<INPUT type="checkbox" name="checkboxname"
value="checkboxValue" [checked] onClick="Handler">Text
В отличии от кнопки,атрибут value сдесь не задает на надпись на
переключателе,а его значение(внутреннее).Поэтому если надо что-то
подписать,пишите рядом в ним. Может быть сразу выбраным если указан
атрибут checked .Если value не указано то значение по умолчанию
"on" .Передается только в том случае,когда выбран.
<FORM onSubmit="return false;"> <INPUT type="checkbox"
name="inet" value="Yes" checked>Доступ
к Интернет </FORM>
Доступ к Интернет
Радио-кнопка(radio)
<INPUT type="radio" name="radioName" value="radioVal1"
[checked] onClick="Handler">Text
В отличие от checkbox может быть несколько радиокнопок с одинаковым
параметром name ,но с разными value,из них передается только та,что
выбрана.Одна из них может быть изначально выбрана по умолчанию
checked.Например:
<FORM onSubmit="return false;"> Вы уверены?<BR>
<INPUT type="radio" name="Radbut" checked>Yes
<INPUT type="radio" name="Radbut">No
</FORM>
Вы уверены?
Yes No
Список(select)
<SELECT name="SelectName" size=число [multiple] [обработчики]
> <OPTION value="optionValue1" [selected]>Опция
1 <OPTION value="optionValue2" [selected]>Опция
2 <OPTION value="optionValue3" [selected]>Опция
3 ..... <OPTION value="optionValueN" [selected]>Опция
N </SELECT>
Задает список,позволяющий выбрать одну (или несколько)
опций из списка. Если атрибут multiple не указан,то создается
простой выпадающий список,в котором можно выбрать только одну
из опций.Его значение всегда передается,т.к. всегда хоть одно
выбрано. Если указан атрибут multiple,то во первых можно указать
размер видимой части списка атрибутом size (Если опций больше
появится скролинг).Во вторых передаются только выбраные опции
,т.е.Он может передатся несколько раз ?SelectName=opt1&SelectName=opt2&SelectName=opt9
если выбраны скажем несколько опций.А может и не разу,если ничего
не выбрано из списка. Можно задавать обработчики onBlur,onChange,onFocus.
<FORM onSubmit="return false;"> Ваш цвет:<BR>
<SELECT name="singleSel"> <OPTION value="white">Белый
<OPTION value="black">Черный <OPTION value="magenta">Фиолетовый
<OPTION value="green">Зеленый <OPTION value="red">Красный
</FORM>
Ваш цвет:
БелыйЧерныйФиолетовыйЗеленыйКрасный
<FORM onSubmit="return false;"> Какие сорта пива
вы пили:<BR> <SELECT name="miltiSel" multiple
size=4> <OPTION value="Балтика">Балтика <OPTION
value="Толстяк">Толстяк <OPTION value="Премьер">Премьер
<OPTION value="Хольстен">Хольстен <OPTION value="Бавария">Бавари
<OPTION value="Coca-Cola ;)">Coca-Cola ;) </SELECT>
</FORM>
Какие сорта пива вы пили:
БалтикаТолстякПремьерХольстенБавариCoca-Cola ;)
Небольшая Помощь JavaScript
Для CGI-програмиста конечно JavaScript -это иной мир,вы можете
спокойно пропустить этот абзац,если вы не знаете JavaScript,так
как написаное в нем к CGI не относится, а скорей к самим формам
и дизайну сайта.Я скажу пару слов о том как JavaScript может оказать
посильную помощь в проверке правильности заполнения форм.Все проверки
конечно можно и нужно делать на сервере,но когда имеешь дело с
рассеяным пользователем, то для него заполнение простой формы
превратится в мучение.Поясню на примере,в форме есть какие-то
обязательные поля,например имя.Если пользователь забыл его указать
то скрипт скажет ему об этом в сообщении он исправит это, допустим
что-нибудь еще не так ввел .... Только на передачу данных по сети
может уходить масса времени.А на обработку на локальной машине-доли
секуды.
Вот Например как это можно применить JavaScript для предварительного
контроля правильности. Допустим простейшая форма содержит имя
и возраст.Имя не должно быть пустым, а возраст должен состоять
из цифр.
<HTML>
<HEAD>
<SCRIPT language="JavaScript">
<!-- function IsNumber(data)
{ var NumStr="0123456789"; var ch;var
count; for(var i=0;i<data.length;i++)
{ ch=data.substring(i,i+1); if(NumStr.indexOf(ch)!=-1)count++;
}
if(counter==data.length)return true; else return
false; } function IsEmpty(data){ if(data.length==0)return true;
else return false; } function IsFormOk(f){ if(IsEmpty(f.Name.value)){
alert('Имя не должно быть пустой строкой'); return false; } if(!IsNumber(f.Age.value)){
alert('Возраст должен состоять из цифр'); return false; } return
true; } //--></SCRIPT></HEAD> <BODY> <FORM
action="http://www.test.ru/cgi-bin/test.cgi" onSubmit="IsFormOk(this.form)">
Your Name:<INPUT name="Name"><BR> Your age:<INPUT
name="Age"><BR> <INPUT type="submit"
value="Послать Данные"> </FORM> </BODY></HTML>
Ну вот ,на этом можно закончить это краткое введение
в HTMLые формы.
Итак,У нас на входе скрипта данные формы,закодированые методом
urlencode Положеные в Переменную QUERY_STRING или подаваемые на
STDIN.Мы должны вопервых их получить. if($ENV{'REQUEST_METHOD'}
eq 'GET'){#Анализируем метод,GET или POST $query=$ENV{'QUERY_STRING'};
} elsif($ENV{'REQUEST_METHOD'} eq 'POST'){ sysread(STDIN,$query,$ENV{'CONTENT_LENGTH'});
}
Вот,мы уже считали наш запрос в переменную $query.Теперь
пришло самое время ее обработать. Мы знаем что поля разделены
символом '&' значит используем его в качестве разделителя
функции split: @formfields=split(/&/,$query);
Вот разделили,а теперь организуем цикл foreach по
полученым полям @formfields
foreach(@formfields){ if(/^Name=(.*)/){$name=urldecode($1);}
if(/^Age=(.*)/){$age=urldecode($1);} }
Сдесь выражение в регулярном выражении в круглых
скобках (.*) после знака '=',запоминаетс в скалярную переменную
$1 ,которая затем и декодируется нашей старой и знакомой функцией
urldecode (я предупреждал,что она будет почти в каждой вашей CGI-программе)
sub urldecode{ #очень полезная функция декодировани local($val)=@_;
#запроса,будет почти в каждой вашей CGI-программе $val=~s/\+/
/g; $val=~s/%([0-9a-hA-H]{2})/pack('C',hex($1))/ge; return $val;
}
Так мы проходим по всем полям,которые нам переданы.Это
стандартный подход,он годится в качестве шаблона.У вас может возникнуть
вопрос,а что делать если вам переданы данные от списка у которого
задана возможность выбора нескольких элементов и данные поступают
в таком виде: Sel=opt1&Sel=opt2&Sel=opt9. Тут тоже нет
никаких проблем,просто запихиваем эти поступающие значения в массив.
foreach(@formfields){ ..... if(/^Sel=(.*)/){push @Sel,urldecode($1);}
..... }
И потом спокойно оперируем с Полученым Массивом
@Sel.
На этом можно так сказать заканчивается шаблонная часть скрипта
и начинается содержательная, которая зависит только от вашей фантазии.....
Вы можете сколько угодно анализировать полученые значения,обращатся
при этом к различным файлам .Если вы к этому приложите фантазию,то
кто знает что получится....
А Пока Ради примера я вам напишу скрипт,который ведет социологическое
исследование насчет курения и отношения к нему.Может он слишком
массивен для данного пособия, но зато он наглядно показывает как
достаточно простыми средствами можно проводить социологические
исследования. <HTML><!-- HTML файл с формой,можете повесить
его себе на сайт! -> <HEAD><TITLE>Социологический
опрос насчет курения</TITLE></HEAD> <BODY> <CENTER><H1>Социологический
опрос насчет курения</H1></CENTER> <FORM action="cgi-bin/smoketest.cgi">
<TABLE> <TR><TD>Ваш возраст:</TD><TD><INPUT
name="age"></TD></TR> <TR><TD>Вы
курите(Y/N):</TD> <TD><INPUT type="radio"
name="smoke" value="Yes" checked>Да <INPUT
type="radio" name="smoke" value="No">Нет</TD></TR>
<TR><TD>Как вы относитесь если рядом кто-то курит?</TD>
<TD><SELECT name="sm_near"> <OPTION value="0">Резко
негативно <OPTION value="1">Негативно <OPTION
value="2" selected>Мне все равно <OPTION value="3">Позитивно
<OPTION value="4">Резко позитивно </SELECT>
</TD></TR> <TR><TD>Сколько вы выкуриваете
в день?</TD> <TD><SELECT name="sm_day">
<OPTION value="0">Ни сколько <OPTION value="1">1
сигарету <OPTION value="2">2 сигареты <OPTION
value="5">около 5 <OPTION value="0.5pac">полпачки
<OPTION value="pac">пачку <OPTION value="2pac">2
пачки <OPTION value="more">больше </SELECT>
</TD></TR> <TR><TD>Как давно вы начали
курить?</TD> <TD><SELECT name="sm_stage">
<OPTION value="noatall">Не начинал <OPTION
value="onetime">Бросил <OPTION value="0.5year">Полгода
<OPTION value="1year">Год <OPTION value="2year">2
Года <OPTION value="5year">5 Лет <OPTION value="more">Больше
</SELECT> </TD></TR> <TR><TD>Считаете
ли вы это опасным для своего здоровья?</TD> <TD><SELECT
name="sm_danger"> <OPTION value="0">Очень
Опасно <OPTION value="1">Думаю,что да <OPTION
value="2" selected>Не знаю <OPTION value="3">Может
самую малость <OPTION value="4">Нет,Безопасно.
</SELECT> </TD></TR> <TR><TD>Хотите
ли вы бросить?</TD> <TD><SELECT name="sm_nosmoke">
<OPTION value="0">Уже бросаю <OPTION value="1">Думаю
бросить <OPTION value="2" selected>Иногда <OPTION
value="3">Очень Редко <OPTION value="4">Никогда.
</SELECT> </TD></TR> <TR><TD><INPUT
type="submit" value="Послать Данные"></TD>
<TD><INPUT type="reset" value="Очистить
Форму"></TD></TR> </TABLE> </FORM>
</BODY></HTML>
А вот скрипт для его обработки: #!/usr/bin/perl
#smoketest.cgi $datafile="smoke.dat"; sub urldecode{
local($val)=@_; $val=~s/\+/ /g; $val=~s/%([0-9a-hA-H]{2})/pack('C',hex($1))/ge;
return $val; } sub print_err{ print "Content-Type: text/html\n\n";
print "<HTML><HEAD><TITLE>Error!!</TITLE></HEAD>";
print "<BODY><CENTER><H1>@_</H1>";
print "</BODY></HTML>"; exit; } if($ENV{'REQUEST_METHOD'}
eq 'GET'){$query=$ENV{'QUERY_STRING'};} elsif($ENV{'REQUEST_METHOD'}
eq 'POST') {sysread(STDIN,$query,$ENV{'CONTENT_LENGTH'});} if($query
ne ''){ @formfields=split(/&/,$query); foreach(@formfields){
if(/^age=(.*)/){$age=urldecode($1);} if(/^smoke=(.*)/){$smoke=urldecode($1);}
if(/^sm_near=(.*)/){$sm_near=urldecode($1);} if(/^sm_day=(.*)/){$sm_day=urldecode($1);}
if(/^sm_stage=(.*)/){$sm_stage=urldecode($1);} if(/^sm_danger=(.*)/){$sm_danger=urldecode($1);}
if(/^sm_nosmoke=(.*)/){$sm_nosmoke=urldecode($1);} } if((!$age)||($age=~/\D/)){
print "Content-Type: text/html\n\n"; print "<HTML><BODY><H2>Возраст
введен неправильно,должен состоять из цифр.</H2>";
print "<FORM><INPUT type=\"button\" value=\"Вернуться
назад к Анкете\""; print "onClick=\"history.back();\"></FORM>";
print "</BODY></HTML>"; } $anket_str=join('\t',($age,$smoke,$sm_near,$sm_day,$sm_stage,$sm_danger,$sm_nosmoke));
open(DATA,">>$datafile") || print_err("Cannot
open $datafile $!"); print DATA "$anket_str\n";
close(DATA); } open(DATA,"$datafile") || print_err("Cannot
open $datafile $!"); @AllData=<DATA>; close(DATA);
$total=$#AllData; foreach(@AllData){ ($age,$smoke,$sm_near,$sm_day,$sm_stage,$sm_danger,$sm_nosmoke)=split(/\t/,$_);
$smok_total++ if ($smoke eq 'Yes'); $nosmok_total++ if ($smoke
eq 'No'); if($age<16){$age16_total++; if($smoke eq 'Yes'){$age16_sm++;}else{$age16_nosm++;}
} if(($age>16)&&($age<=18)){$age16_18_total++; if($smoke
eq 'Yes'){$age16_18_sm++;}else{$age16_18_nosm++;} } if(($age>18)&&($age<=20)){$age18_20_total++;
if($smoke eq 'Yes'){$age18_20_sm++;}else{$age18_20_nosm++;} }
if($age>20){$age20_total++; if($smoke eq 'Yes'){$age20_sm++;}else{$age20_nosm++;}
} if($sm_near eq '0'){$near0++;} if($sm_near eq '1'){$near1++;}
if($sm_near eq '2'){$near2++;} if($sm_near eq '3'){$near3++;}
if($sm_near eq '4'){$near4++;} if($sm_day eq '0'){$day0++;} if($sm_day
eq '1'){$day1++;} if($sm_day eq '2'){$day2++;} if($sm_day eq '5'){$day5++;}
if($sm_day eq '0.5pac'){$dayhalfpac++;} if($sm_day eq 'pac'){$daypac++;}
if($sm_day eq '2pac'){$day2pac++;} if($sm_day eq 'more'){$daymore++;}
if($sm_stage eq 'noatall'){$stagenoatall++;} if($sm_stage eq 'onetime'){$statgeonetime++;}
if($sm_stage eq '0.5year'){$stagehalfyear++;} if($sm_stage eq
'1year'){$stage1year++;} if($sm_stage eq '2year'){$stage2year++;}
if($sm_stage eq '5year'){$stage5year++;} if($sm_stage eq 'more'){$stagemore++;}
if($sm_danger eq '0'){$danger0++;} if($sm_danger eq '1'){$danger1++;}
if($sm_danger eq '2'){$danger2++;} if($sm_danger eq '3'){$danger3++;}
if($sm_danger eq '4'){$danger4++;} if($sm_nosmoke eq '0'){$stopsmoke0++;}
if($sm_nosmoke eq '1'){$stopsmoke1++;} if($sm_nosmoke eq '2'){$stopsmoke2++;}
if($sm_nosmoke eq '3'){$stopsmoke3++;} if($sm_nosmoke eq '4'){$stopsmoke4++;}
} ######### print "Content-Type: text/html\n\n"; print
"<HTML><HEAD><TITLE>Результаты обработки
данных</TITLE></HEAD>"; print "<BODY
bgcolor=\"yellow\">"; unless($total){print "<H1>Еще
нет данных</H1></BODY></HTML>";exit;} print
"<CENTER><H1>Результаты обработки данных</H1></CENTER>";
print "<BR>\n"; print "Обработано анкет:
$total<BR>\n"; print "Общие данные Всего:<BR>\n";
print "Курящие:$smok_total (".(($smok_total/$total)*100)
."%)<BR>\n"; print "Некурящие:$nosmok_total
(".(($nosmok_total/$total)*100)."%)<BR>\n";
print "<TABLE>\n"; print "<TR><TD
colspan=4>Возрастные группы:(<16,16..18,18..20,>20)</TD></TR>\n";
print "<TR><TD>Возраст</TD><TD>Курящие</TD><TD>Некурящие</TD><TD>Всего</TD></TR>\n";
print "<TR><TD><16:</TD><TD>$age16_sm</TD>";
print "<TD>$age16_nosm</TD><TD>$age16_total</TD></TR>\n";
print "<TR><TD>16..18:</TD><TD>$age16_18_sm</TD>";
print "<TD>$age16_18_nosm</TD><TD>$age16_18_total</TD></TR>\n";
print "<TR><TD>18..20:</TD><TD>$age18_20_sm</TD>";
print "<TD>$age18_20_nosm</TD><TD>$age18_20_total</TD></TR>\n";
print "<TR><TD>>20:</TD><TD>$age20_sm</TD>";
print "<TD>$age20_nosm</TD><TD>$age20_total</TD></TR>";
print "</TABLE>\n"; print "<TABLE>\n";
print "<TR><TD colspan=2>Отношение когда кто-то
курит рядом:(%)</TD></TR>\n"; print "<TR><TD>Резко
негативно</TD><TD>".(($near0/$total)*100)."</TD></TR>\n";
print "<TR><TD>Негативно </TD><TD>".(($near1/$total)*100)."</TD></TR>\n";
print "<TR><TD>Мне все равно </TD><TD>".(($near2/$total)*100)."</TD></TR>\n";
print "<TR><TD>Позитивно </TD><TD>".(($near3/$total)*100)."</TD></TR>\n";
print "<TR><TD>Резко позитивно</TD><TD>".(($near4/$total)*100)."</TD></TR>\n";
print "</TABLE>\n"; print "<TABLE>\n";
print "<TR><TD colspan=2>В среднем выкуривают:(%)</TD></TR>\n";
print "<TR><TD>не курят: </TD><TD>".(($day0/$total)*100)."</TD></TR>\n";
print "<TR><TD>1 сигарету:</TD><TD>".(($day1/$total)*100)."</TD></TR>\n";
print "<TR><TD>2 сигареты:</TD><TD>".(($day2/$total)*100)."</TD></TR>\n";
print "<TR><TD>5 сигарет: </TD><TD>".(($day5/$total)*100)."</TD></TR>\n";
print "<TR><TD>полпачки: </TD><TD>".(($dayhalfpac/$total)*100)."</TD></TR>\n";
print "<TR><TD>пачку: </TD><TD>".(($daypac/$total)*100)."</TD></TR>\n";
print "<TR><TD>2 пачки: </TD><TD>".(($day2pac/$total)*100)."</TD></TR>\n";
print "<TR><TD>больше: </TD><TD>".(($daymore/$total)*100)."</TD></TR>\n";
print "</TABLE>\n"; print "<TABLE>\n";
print "<TR><TD colspan=2>Стаж курения:(%)</TD></TR>\n";
print "<TR><TD>Не начинал</TD><TD>".(($stagenoatall
/$total)*100)."</TD></TR>\n"; print "<TR><TD>Бросил
</TD><TD>".(($statgeonetime/$total)*100)."</TD></TR>\n";
print "<TR><TD>Полгода </TD><TD>".(($stagehalfyear/$total)*100)."</TD></TR>\n";
print "<TR><TD>Год </TD><TD>".(($stage1year
/$total)*100)."</TD></TR>\n"; print "<TR><TD>2
Года </TD><TD>".(($stage2year /$total)*100)."</TD></TR>\n";
print "<TR><TD>5 Лет </TD><TD>".(($stage5year
/$total)*100)."</TD></TR>\n"; print "<TR><TD>Больше
</TD><TD>".(($stagemore /$total)*100)."</TD></TR>\n";
print "</TABLE>\n"; print "<TABLE>\n";
print "<TR><TD colspan=2>Курение опасно:(%)</TD></TR>\n";
print "<TR><TD>Очень Опасно </TD><TD>".(($danger0/$total)*100)."</TD></TR>\n";
print "<TR><TD>Думаю,что да </TD><TD>".(($danger1/$total)*100)."</TD></TR>\n";
print "<TR><TD>Не знаю </TD><TD>".(($danger2/$total)*100)."</TD></TR>\n";
print "<TR><TD>Может самую малость</TD><TD>".(($danger3/$total)*100)."</TD></TR>\n";
print "<TR><TD>Нет,Безопасно. </TD><TD>".(($danger4/$total)*100)."</TD></TR>\n";
print "</TABLE>\n"; print "<TABLE>\n";
print "<TR><TD colspan=2>Хотели ли вы бросить:(%)</TD></TR>\n";
print "<TR><TD>Уже бросаю </TD><TD>".(($stopsmoke0/$total)*100)."</TD></TR>\n";
print "<TR><TD>Думаю бросить</TD><TD>".(($stopsmoke1/$total)*100)."</TD></TR>\n";
print "<TR><TD>Иногда </TD><TD>".(($stopsmoke2/$total)*100)."</TD></TR>\n";
print "<TR><TD>Очень Редко </TD><TD>".(($stopsmoke3/$total)*100)."</TD></TR>\n";
print "<TR><TD>Никогда. </TD><TD>".(($stopsmoke4/$total)*100)."</TD></TR>\n";
print "</TABLE>\n"; print "</BODY></HTML>";