目录
篇名
概述
我的CGIscript可在指令列下执行但无法从浏览器执行。您能不能帮我修修看?
如何去除文章中的HTML标签?
如何萃取URLs?
如何从user端上传资料?如何在另一台机器上开一个档案?
如何在HTML中做pop-upmenu(跳出式选单)?
如何抓HTML档案?
如何解开或产生Web上那些冠的码?
如何【将requests】转向到另一页去?
如何替网页加上密码?
要怎麽用Perl来编辑.htpasswd和.htgroup这两个档案?
如何防范使用者藉由填我的CGI表格来做坏事?
如何解读、萃取email标头资料?
如何解译CGI表格?
如何验证email位址?
如何解MIME/BASE64字串?
如何根据使用者帐户名称自动合成email位址?
我的程式如何送/读email?
如何找出我的主机名/网域名/IP位址?
如何抓新闻讨论群的文章或群组名录?
如何抓/丢FTP档案?
如何用Perl做RPC?
作者及版权事宜
——————————————————————————–
篇名
perlfaq9-网路连线(原文版Revision:1.16,Date:1997/04/2318:12:06.中文版$Revision:1.13$,$Date:1997/07/1220:44:25$)
——————————————————————————–
概述
本篇涵盖网路连线、Internet,还有几个关於WWW的问题。
——————————————————————————–
我的CGIscript可在指令列下执行但无法从浏览器执行。您能不能帮我修修看?
当然,但您恐怕付不起雇我们的签约金:-)
说真的,如果您能够先证明您已读过下列这几个FAQs,但遇到的问题并不单纯、非叁言两语即可回答的话,那麽您post到comp.infosystems.www.authoring.cgi上(如果是有关HTTP、HTML,或CGI通信协定)的问题可能也会得到口气和缓而有用的答覆。表面上看似Perl,但骨子里是CGI之类的问题,如果post到comp.lang.perl.misc人家可能就不会这麽乐意地接受了。
几个实用的FAQs分别是:
http://www.perl.com/perl/faq/idiots-guide.html
http://www3.pair.com/webthing/docs/cgi/faqs/cgifaq.shtml
http://www.perl.com/perl/faq/perl-cgi-faq.html
http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html
http://www.boutell.com/faq/
【译者】上面第叁份文件,Perl-CGI-FAQ的中译版可在http://2Ti.com/cgi-bin/2T/perl/perl-cgi-faq-chi/处取得。最後一份(WWWFAQ)的中译版可自http://www.acer.net/document/cwwwfaq/取得。
——————————————————————————–
如何去除文章中的HTML标签?
最正确(尽管不是最快)的方法是使用HTML::Parse模组(可由CPAN取得,是所有写Web程式者必备的libwww-perl套件的一部分)。
许多人尝试用简陋的正规表示式来解决这个问题,譬如说像s/<.*?>//g,但这个式子在很多情况下会失败,因为要处理的字串可能会跨越断行字元,也可能含有被quote【跳脱】的箭头号,或有HTMLcomment出现;再加上一些疏忽,譬如,人们常忘了转换如<的entities(跳脱字元)。
以下这个「简陋」的方法对大多数的档案都有效:
#!/usr/bin/perl-p0777
s/<(?:[^>“]*|([“]).*?\1)*>//gs
如果您想要更完整的解法,请看叁部曲的striphtml程式,http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/striphtml.gz。
——————————————————————————–
如何萃取URLs?
一个快速但不完美的做法是
#!/usr/bin/perl-n00
#qxurl-tchrist@perl.com
print”$2\n”whilem{
<\s*
A\s HREF\s*=\s*([“])(.*?)\1
\s*>
}gsix;
这个版本并不替相对式写法的URLs作调整,也不懂代换bases【
——————————————————————————–
如何从user端上传资料?如何在另一台机器上开一个档案?
如果是HTML表格的话,您可以使用multipart/form-data的编码格式。CGI.pm(可自CPAN取得)中的start_multipart_form()这个method就是为此设计的,它和startform()这个method是两回事。
——————————————————————————–
如何在HTML中做pop-upmenu(跳出式选单)?
用
——————————————————————————–
如何抓HTML档案?
有一个方法是,如果您的系统上装有lynx一类的文字模式的HTML浏览器的话,那麽可以这麽做:
$html_code=`lynx-source$url`;
$text_data=`lynx-dump$url`;
收录在CPAN里的libwww-perl(LWP)模组则提供了更强的方法来做这件事。它不但可钻过proxies,而且也不需要lynx:
#printHTMLfromaURL
useLWP::Simple;
getprint”http://www.sn.no/libwww-perl/”;;
#printASCIIfromHTMLfromaURL
useLWP::Simple;
useHTML::Parse;
useHTML::FormatText;
my($html,$ascii);
$html=get(“http://www.perl.com/”;);
defined$html
ordie”CantfetchHTMLfromhttp://www.perl.com/”;;
$ascii=HTML::FormatText->new->format(parse_html($html));
print$ascii;
——————————————————————————–
如何解开或产生Web上那些冠的码?
以下是一个解码的实例:
$string=”http://altavista.digital.com/cgi-bin/query?pg=q&;what=news&fmt=.&q=+cgi-bin +perl.exe”;
$string=~s/([a-fA-F0-9]{2})/chr(hex($1))/ge;
编码比较困难一点,因为您不能盲目地把所有非字母数字的字元(\W)都一律转换成十六进位的跳脱码。很重要的是有特殊意义的字元,如/和?便不可以转换。要做得正确,最简单的方法大概是使用现成的URI::Escape模组,而不要重新发明轮子。这个模组是libwww-perl套件(LWP)的一部分,可自CPAN取得。
——————————————————————————–
如何【将requests】转向到另一页去?
在您的回应中不要使用Content-Type这个标头,相反地,用Location:这个标头。按正式规定,应当URL:才是正确的标头。因此CGI.pm模组(可由CPAN取得)两个标头都送:
Location:http://www.domain.com/newpage
URI:http://www.domain.com/newpage
要注意的是,由於servers采用「最高效率化」的运作方式,故在这些标头中如果使用相对式的URLs可能会产生奇怪的後果。
——————————————————————————–
如何替网页加上密码?
不一定,要看情况。您需要读您server的使用手册,或者查查看上头所列的几个FAQs。
——————————————————————————–
要怎麽用Perl来编辑.htpasswd和.htgroup这两个档案?
HTTPD::UserAdmin和HTTPD::GroupAdmin等模组为这些档案提供了统一的物件导向介面,尽管这些档案可能以各种不同的格式储存。这些资料库可能是纯文字格式、dbm、BerkeleyDB或任何DBI相容的资料库驱动程式(drivers)。HTTPD::UserAdmin支援`Basic和`Digest这两个认证模式所用的档案。以下是一例:
useHTTPD::UserAdmin();
HTTPD::UserAdmin
->new(DB=>”/foo/.htpasswd”)
->add($username=>$password);
——————————————————————————–
如何防范使用者藉由填我的CGI表格来做坏事?
阅读CGIsecurityFAQ,(可在http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html取得),还有PerlCGIFAQ,在http://www.perl.com/CPAN/doc/FAQs/cgi/perl-cgi-faq.html。
简单一句话:使用tainting(沾腥?)这项功能(详见perlsec)。它会让所有不在您的script中、来路不明的资料(譬如,CGI参数)无法放到eval或system等呼叫中使用。除了使用tainting之外,绝对不要使用单一参数的方式下参数给system()或exec(),而应将欲执行的指令及其参数放在一个序列或阵列里面,再传给system()或exec(),这样便可避免globbing【即被shell先做解译】。
——————————————————————————–
如何解读、萃取email标头资料?
如果您只需要一个「快而脏」的解法的话,您可以试试这个从再版的“ProgrammingPerl第222页中拿出来的例子:
$/=;
$header=
$header=~s/\n\s //g;#将延续行合并成单行
head=(UNIX_FROM_LINE,split/^([-\w] ):\s*/m,$header);
譬如说,您若想保留所有Received栏位资料的话【因Received栏位通常不止一个】,这个解法便不太行了。一个完整的解法是使用收录在CPAN的Mail::Header模组(MailTools套件的一部分)。
——————————————————————————–
如何解译CGI表格?
很多人忍不住要自己写程式来处理这部分的工作,所以您们大概都看过一大堆其中有$ENV{CONTENT_LENGTH}和$ENV{QUERY_STRING}的程式码。没错,这麽写是可以行得通,但事实上也有很多在网路上出没的这类程式根本不能用!
请不要忍不住去重新发明轮子【译者:这是英文的说法(reinventingthewheels),也就是浪费时间做人家做过的事的意思】。请改用CGI.pm或CGI_Lite.pm(可自CPAN取得)。如果您被困在无模组的perl1..perl4的土地上,您可以试看看cgi-lib.pl(可至http://www.bio.cam.ac.uk/web/form.html取得)。
——————————————————————————–
如何验证email位址?
无法度。
如果没有寄封信到一个位址去试试看它会不会弹回来(即使是这麽做您还得面对停顿的问题),您是无法确定一个位址是否真的存在的。即使您套用email标头的标准规格来做检查的依据,您还是有可能会遇到问题,因为有些送得到的位址并不符合RFC-822(电子邮件标头的标准)的规定,但有些符合标准的位址却无法投递。
许多人试图用一个简单的正规表示式,例如/^[\w.-] \@([\w.-]\.) \w $/来消除一些通常是无效的email位址。不过,这样做也把很多合格的位址给一起滤掉了,而且对测试一个位址有没有希望投递成功完全没有帮助,所以在此建议大家不要这麽做;不过您可以看看:http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/ckaddr.gz。这个script真的彻底地依据所有的RFC规定来做检验(除了内嵌式comments外),同时会排除一些您可能不会想送信去的位址(如BillClinton【美国柯林顿总统】或您的postmaster),然後它会确定位址中的主机名称可在DNS中找得到。这个script跑起来不是很快,但至少有效。
不少CGIscripts的作者使用另一个替代的方案:用一个简单的正规表示式,(如上头的那个)。如果一个位址能让这个式子对得上的话,那麽就接受这个位址。如果这个位址对不上这个式子的话,便再向使用者讯问,以确定她们填入的这个位址正确无误。
——————————————————————————–
如何解MIME/BASE64字串?
MIME-tools套件(可自CPAN取得)不但可处理这个问题而且有许多其他的功能。有了这个套件,解BASE64码就变得像这麽容易:
useMIME::base64;
$decoded=decode_base64($encoded);
一个比较直接的解法是先做一点简单的转译,然後使用unpack()这个函数的“u格式:
tr#A-Za-z0-9 /##cd;#去除非base64字元
tr#A-Za-z0-9 /#-_#;#转换成uu码格式
$len=pack(“c”,32 0.75*length);#计算长度字元
printunpack(“u”,$len.$_);#uu解码後print
——————————————————————————–
如何根据使用者帐户名称自动合成email位址?
在支援getpwuid【UNIX系统呼叫】、$<这个变数,和Sys::Hostname模组(标准perl发行的一部分)的系统上,您可试试这样的做法:
useSys::Hostname;
$address=sprintf(s@s,getpwuid($<),hostname);
有的公司对email位址有统筹规画,因此这麽一来您可能会合成出不被公司的email主机接受的位址。所以如果有这类的顾虑的话,您应该直接向users要他们的email位址。而且,并不是所有能跑Perl的系统都像Unix一样,可以很容易得到这些资料。
CPAN里的Mail::Util模组(MailTools套件的一部分)中有一个mailaddress()函数,它会试着去猜user的email位址。这个函数使用比上面的code聪明的方法,它会参考安装时所得到的设定资料,但是错误仍可能发生。所以,再一次地,最好的方法通常是直接问user本人。
——————————————————————————–
我的程式如何送/读email?
送信:CPAN上头的Mail::Mailer模组(MailTools套件的一部分)只适合在Unix上使用,但利用到Net::SMTP的Mail::Internet模组则没有这个限制。读信:用CPAN上的Mail::Folder模组(MailFolder套件的一部分)或是用CPAN上头的Mail::Internet模组(也是MailTools套件的一部分)。
#送信
useMail::Internet;
useMail::Header;
#设定使用哪台主机
$ENV{SMTPHOSTS}=mail.frii.com;
#制作标头
$header=newMail::Header;
$header->add(From,gnat@frii.com);
$header->add(Subject,Testing);
$header->add(To,gnat@frii.com);
#制作本文
$body=Thisisatest,ignore;
#产生mail物件
$mail=newMail::Internet(undef,Header=>$header,Body=>\[$body]);
#送出
$mail->smtpsendordie;
——————————————————————————–
如何找出我的主机名/网域名/IP位址?
长久以来许多code都很草率地直接呼叫`hostname`这个程式来取得主机名。虽然这麽做很方便,但也同时增加了移植到其他平台上的困难。这是一个很典型的例子,在方便和可移植性之间作抉择,不论选哪一边,必须付出一些牺牲和代价。
Sys::Hostname这个模组(标准perl发行的一部分)可用来取得机器的名字,然後您便可利用gethostbyname()这个系统呼叫来找出该机的IP位址了(假定您的DNS运作正常)。
useSocket;
useSys::Hostname;
my$host=hostname();
my$addr=inet_ntoa(scalar(gethostbyname($name||localhost)));
至少在Unix底下,取得DNS网域名最简单的方法大概要算是直接从/etc/resolv.conf这个档案里面找。当然,这麽做的前提是resolv.conf这个档案的设定必须照惯例的格式,还有就是这个档案必先存在才行。
(Perl在非Unix系统下尚需要一有效的方法来测出机器和网域名)
——————————————————————————–
如何抓新闻讨论群的文章或群组名录?
使用Net::NNTP或News::NNTPClient模组,两者皆可自CPAN下载。这些模组让抓群组名录这类的差事变得这麽容易:
perl-MNews::NNTPClient
-eprintNews::NNTPClient->;new->list(“newsgroups”)
——————————————————————————–
如何抓/丢FTP档案?
LWP::Simple模组(可自CPAN下载)可以抓,但不能丢档案。Net::FTP模组(也可自CPAN下载)虽比较复杂,但可用来丢、也能抓档案。
——————————————————————————–
如何用Perl做RPC?
有一个DCE::RPC模组正在发展阶段(但尚未完成)。一旦完成後它会随着DCE-Perl这个套件发行(可由CPAN下载)。至於ONC::RPC这样的模组则还没听说有人在发展。
——————————————————————————–
作者及版权事宜
Copyright(c)1997TomChristiansenandNathanTorkington.着作权所有,Allrightsreserved。有关使用、(转)发行事宜,详见perlfaq。
中译版着作权所有:萧百龄及两只老虎工作室。本中译版遵守并使用与原文版相同的使用条款发行。