Отправляет email-рассылки с помощью сервиса Sendsay

Perl - подпишись и учись!

  Все выпуски  

В этом выпуске рассматривается функции pack и unpack, работа с файлами записей фиксированной длины и прямой доступ к файлу в Perl.


Информационный Канал Subscribe.Ru


 Hi, All!  
    Приветствую всех!(А именно 7614 подписчиков)

Сегодня выпуск нашей рассылки будет посвящён работе сфайлами, а точнее сказать работе с файлами, которые содержат в себе записи фиксированной длины. В процессе рассказа примером нам будет гостевая книга. Записи гостевой будут храниться в фалах вида 'num.cdb', где num - номер соответствующей "базы" (начиная с 1), по 999 записей в каждом таком файле. Если файл переполняется, то создаётся файл "num+1.cdb". Начнём.

#!/usr/bin/perl -w

use strict; #нужно для корректного написания текста
#Задаём основные переменные
my ($pathtofiles) = '/path/to/files'; #путь к файлам баз данных
my ($lock_allow) = 0; #блокировать ли файлы при доступе к ним: 0-нет, 1-да
my $mainhost='http://mysersver.com'; #адрес сайта в И-нете
my $numonpage=10; #кол-во отображаемых сообщений на одной странице

#функции отображения верха/низа страницы
sub header{ #верх html-ки
print "Content-type:text/html\n\n";
print '<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=windows-1251">
<META http-equiv="Content-Language" content="ru">
</head><body>
';
}

sub footer{ #низ html-ки
print "</body></html>";}

sub down_form{
my $buffer;
if ($ENV{'REQUEST_METHOD'} eq 'POST'){read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});}
else {$buffer=$ENV{'QUERY_STRING'};}


@pairs = split(/&/, $buffer);
foreach $pair (@pairs) {
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$value =~ s/<!--(.|\n)*-->//g;
$input{$name} = $value;
}
return %input;
}

sub ISOtime{#функция, возвращающая время в формате ISO(yyyymmdd)
my $timex=shift;
($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime($timex);
$year+=1900;
$mon+=1;
if ($min < 10) {
$min = "0$min";
}
if ($hour < 10) {
$hour = "0$hour";
}
if ($mon < 10) {
$mon = "0$mon";
}
if ($mday < 10) {
$mday = "0$mday";
}
$date=$year.$mon.$mday.$hour.$min;
return $date;}

sub lockfile #функция блокировки файла
{
my $attemps=0;
my $handle=shift;
until (flock($handle,2)){
sleep 10;
if(++$count > 50){
print "<center><h1><font color=red>Попробуйте позже</font></h1></center>";
exit;
}
}
}

sub unlockfile #функция разблокировки файла
{
my $handle=shift;
flock($handle,8);
}

sub mysort #функция возвращает отсортированный по возрастанию массив
{
@ar=@_;

for($k=$#ar;$k>=1;$k--){
for($i=0;$i<=$k-1;$i++){
if($ar[$i]>$ar[$i+1]){
$b=$ar[$i];
$ar[$i]=$ar[$i+1];
$ar[$i+1]=$b;
}
}
}

sub getbase{ #функция возвращает номер текущей базы
my ($path)=@_;
opendir FDIR,$path;
readdir FDIR;
readdir FDIR;
my @files=readdir(FDIR);
my $max=0;

foreach $filename(@files){
if ($filename=~m/(\S)+\.cdb/){
$filename=~s/\.cdb//i;
if($max<int($filename)){$max=int($filename);}
}
}

return $max;
}

sub zapnum{ #функция возвращает количество записей в базе
my $Fhandle= shift;
my @stat=stat($Fhandle);

my $num=int($stat[7]/1035);
return $num;
}


my $base=getbase($pathtofiles);#определяем текущую базу
if($base==0){$base=1;}
my %input = down_form;#считываем переданные параметры

if($input{do} eq 'add'){#добавление записи
my ($packformat) = "A3A12A20A1000"; #формат упаковки, котрый ипользуется функциями pack и unpack

#Действия, выполняемые со строкой, помещённой в pack, по стилю напоминают те,
#которые осуществляются функцией printf: каждый элемент состоит из символа,
#за которым следует необязательное число повторений. Наш формат указывает, что
#сначала идёт строка из 3 символов, далее строка из 12, из 20 и из 1000 символов,
# причём все эти строки дополняются пробелами

#Работаем с файлом хранящим номер последней записи в текущей базе
open(FLE,$pathtofiles."/counter.txt");
if($lock_allow){lockfile(*FLE);}
my ($id)=<FLE>;
if($lock_allow){unlockfile(*FLE);}
close(FLE);

if($id==999){$id=1; $base++;}#проверяем на переполнение по числу записей, если это так, то создаём новую базу
else{$id=$id+1;}

open(FLE2,">".$pathtofiles."/counter.txt");
if($lock_allow){lockfile(*FLE2);}
print FLE2 $id;
if($lock_allow){unlockfile(*FLE2);}
close(FLE2);


while(length($id)<3){
$id="0".$id;
}

my $err = '';#проверяем на простейшие ошибки
if ($input{text} eq ''){$err.="<font color=red>Не введён текст.</font><br>"}
if (length($input{text})>1000){$err.="<font color=red>Длина текста превышает 1000 символов.</font><br>"}
if($err){
header;
print $err;
footer;
exit;
}

open(FLE3,">>".$pathtofiles."/$base.cdb");#записываем данные в конец файла базы
if($lock_allow){lockfile(*FLE3);}
print FLE3 pack($packformat,$id,ISOtime(time()),$input{name},$input{text});
if($lock_allow){unlockfile(*FLE3);}
close(FLE3);
print "Status: 302\n";#делаем переадресацию(редирект) на главную страничку гостевой
print "Location: $mainhost/cgi-bin/gbook.pl\n\n";

}
else{#вывод доступных записей на экран
header;


my $base=getbase($pathtofiles);#определяем текущую базу

my @bases;#собираем данные о доступных базах записей
for(my $i=1;$i<=$base;$i++){
open(FLE4,"<".$pathtofiles."/$i.cdb");
if($lock_allow){lockfile(*FLE4);}
$bases[$i]=zapnum(*FLE4);
if($lock_allow){unlockfile(*FLE4);}
close(FLE4);
}

my $numofzap=0;#определяем общее количество записей
for(my $i=1;$i<=$base;$i++){
$numofzap+=$bases[$i];
}

unless(exists($input{page})){$input{page}=1;}#определяем номера записей для данной страницы вывода
unless(int($input{page})>0){$input{page}=1;}
if(int($input{page})>($numofzap/$numonpage)){$input{page}=int($numofzap/$numonpage)+1}
$input{page}=int($input{page});
my $firstonpage=$numofzap-($input{page}-1)*$numonpage;
my $lastonpage=$numofzap-($input{page})*$numonpage+1;
if($lastonpage<=0){$lastonpage=1;}


my $sum=0;#номера базы и номера записи от начала записи для каждой записи, которую нужно вывести
my (%hashbase,%hashnum);
for(my $k=1;$k<=$base;$k++){
$sum=$sum+$bases[$k];
for(my $i=$lastonpage;$i<=$firstonpage;$i++){
if (($i<=$sum)and(!exists($hashbase{$i}))){
$hashbase{$i}=$k;
$hashnum{$i}=$i-$sum+$bases[$k];
}
}}

my $key;#получение собственно данных из баз, в которых содержатся записи, необходимые для вывода
#Причём, необходимо отметить, что мы считываем в каждом случае не всю базу, а только нужную запись,
#т.к. мы используем не последовательный доступ к файлу, а прямой
#Разница состоит в том, что для того чтобы считать n-тую строку при последовательном доступе мы
#должны будем считать n-1 строку, предшествующую нужной, что естественно "замедляем и нагружает".
#В данном же случае мы считываем только то, что нам надо считать.

my($vid,$vtime,$vname,$vtext);
my(%htime,%hname,%htext);
my ($packformat) = "A3A12A20A1000";
foreach $key(mysort keys %hashbase){
#print $key," ";
open(FLE5,"<".$pathtofiles."\/$hashbase{$key}\.cdb");
if($lock_allow){lockfile(*FLE5);}
seek(FLE5,1035*($hashnum{$key}-1),0);
read(FLE5,$_,1035);
($vid,$vtime,$vname,$vtext)=unpack($packformat,$_);
$htime{$key} = $vtime;
$hname{$key} = $vname;
$htext{$key} = $vtext;

if($lock_allow){unlockfile(*FLE5);}
close(FLE5);
}


my $pftime="A4A2A2A2A2";#формат ISO времени
my ($vyear,$vmonth,$vday,$vhour,$vmin);

foreach $key(keys %htime){#распаковываем время в нормальный вид
($vyear,$vmonth,$vday,$vhour,$vmin)=unpack($pftime,$htime{$key});
$htime{$key}=$vday.'.'.$vmonth.'.'.$vyear.' '.$vhour.':'.$vmin;
}

#выводим записи
print "<table width=100% bgcolor=#E5E5E5 cellpadding=0 cellspacing=1>";
my $counter=1;
my $bgcolor;
foreach $key(sort {$b<=>$a} keys %htime){
if (($counter % 2)==0){$bgcolor="#FFFFFF"}
else{$bgcolor="lightgrey";}
print "<tr bgcolor=$bgcolor><td width=150 valign=top align=left>$hname{$key}<br>$htime{$key}</td><td valign=top><table width=100% cellpadding=0 cellspacing=0><tr><td width=1>&nbsp;</td><td>$htext{$key}</td></tr></table></td></tr>";
$counter++;
}
print "</table>";

#Ниже можно не комментировать, всё должно быть и так понятно
if($numofzap>$numonpage){
print "<center>";
unless($input{page}==1){my $num=int($input{page})-1;
print "<a href='$mainhost/cgi-bin/gbook.pl?page=$num'><<< Предыдущие $numonpage сообщений</a>&nbsp;&nbsp;"}
if($numonpage<=$numofzap-$input{page}*$numonpage){my $num=int($input{page})+1; print "&nbsp;&nbsp;<a href='$mainhost/cgi-bin/gbook.pl?page=$num'>Следующие $numonpage сообщений >>></a>"}
elsif(0<$numofzap-$input{page}*$numonpage){
my $num=int($input{page})+1;
my $num2=$numofzap-$input{page}*$numonpage;
my $str;
if($num2==1){$str="Следующее 1 сообщение >>>";}
elsif(($num2>=2)and($num2<=4)){$str="Следующие $num2 сообщения >>>";}
else{$str="Следующие $num2 сообщений >>>";}
print "&nbsp;&nbsp;<a href='$mainhost/cgi-bin/gbook.pl?page=$num'>$str</a>"}
print "</center>";}

print"<form action='gbook.pl' method=post>
<table>
<tr><td valign=top>Name</td><td>&nbsp;</td><td valign=top><input type=text name='name' maxlength=20></td></tr>
<tr><td valign=top>Password</td><td>&nbsp;</td><td valign=top><input type=password name='pass'></td></tr>
<tr><td valign=top>Text</td><td>&nbsp;</td><td valign=top><input type=hidden name='do' value='add'><textarea name='text' Cols=40 Rows=5></textarea></td></tr>
<tr><td><input type=submit value=' OK '></td></tr>
</table>
</form>";


footer;
}

exit;

________________________________________
И ещё, если у вас есть вопросы, то задавайте их на форуме сайта(http://perl.dp.ua).
________________________________________

                С уважением, Дмитрий.

Another Banner Network

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное