大家论坛

 找回密码
 注册
查看: 6396|回复: 6

Perl常见问题集锦(FAQs)

[复制链接]

321

主题

773

帖子

4768

金币

大家网大学四年级

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

积分
3139
发表于 2008-12-25 16:50 | 显示全部楼层 |阅读模式
Perl是什麽?
Perl是一个高阶程式语言,由 Larry Wall和其他许多人所写,融合了许多语言的特性。它主要是由无所不在的 C语言,其次由 sed、awk,UNIX shell 和至少十数种其他的工具和语言所演化而来。Perl对 process、档案,和文字有很强的处理、变换能力,因此举凡有关快速原型设计、系统工具、软体工具、系统管理、资料库连结、图像程式设计、网路连结,和 WWW程式设计等之类的任务,都特别 适合用 Perl来做。这些特长不但使 Perl成为系统维护管理者和 CGI作者的宠儿,就连数学家、遗传学家、新闻从业者,甚至企业管理者也都用 Perl,所以或许您也该用。


--------------------------------------------------------------------------------

谁对 perl提供支援?由谁负责发展?它为什麽是免费的?
Perl自由开放的发行方式要归功於发烧前的 Internet的传统文化及其作者 Larry Wall。Perl是由使用者提供支援。现在 Perl的核心、标准程式库、选择性安装的模组,以及您现在正在阅读的使用说明都出自於义务者之手。详情请见 perl原始码发行版中所附的 README档案底部的私人注记。

值得一提的是,核心发展小组(称为 Perl Porters)的成员是一群高度热情奉献的人仕,全心投入发展出比您所能想像、用钱能买得到还要更好的免费软体。您可经由 news://genetics.upenn.edu/perl.porters-gw/ 和 http: //www.frii.com/~gnat/perl/porters/summary.html取得关於新近发展计画 的情报。

尽管 GNU计画将 Perl囊括在它的发行中,但是没有叫「GNU Perl」这样的东西。 Perl既非自由软体基金会所创,亦非由其负责维护。Perl的发行条款同时也较 GNU软体更来得开放。

如果您愿意,您可以购买商业性的 Perl支援。但对大多数使用者来说,非正式性的支援通常已相当足够。详情请见「到哪里可买到商业性的 Perl支援」一问的回 答。


--------------------------------------------------------------------------------

我该用哪一个版本的 Perl?
您绝对该用第五版。第四版不但老旧、功能较局限,而且已经不再维护了。它最後 一次的修正 (4.036)是在 1992年。Perl最新的量产发行版本是5.004。等到您 读这篇文章时,我们可能已经又发行了几个正式的除错版本,同时大概又会有些替 下一版 路的实验版出来。本文由此开始凡提及 Perl语言,皆以目前的量产发行 为准,除非另外特别注明。


--------------------------------------------------------------------------------

perl4和 perl5各代表什麽?
perl4 和 perl5 是对 Perl程式语言的两个不同版本的非正式称呼,因为说「perl5」要比说「第 5(.004)版的 Perl」要来得简单。但是有些人误将其会意为:perl5是一个单独的语言;这是不正确的。perl5只不过是对第五个主要发行版本(1994 年 10 月)常用的称呼罢了。就像 perl4是指第四个主要发行(1991年 3 月),还有 perl1(1988年 1月)、 perl2(1988年 6 月),以及 perl3(1989年 10 月)。

5.0的发行基本上是从零开始,所有程式码完全重新写过的版本。它已经被模组化、物件导向化、微调、精简化,及效率化,以致程式码几乎已变得和原来的不相同了。尽管如此,使用介面大致上仍然相同,而且和先前的版本之间保持了很高的 一致性。

为了避免「perl5是什麽语言?」这类的混淆,有些人索性完全避免「perl5」, 而单用「perl」来指称最近的 perl版本。其实用不着这麽累就是了。


--------------------------------------------------------------------------------

Perl的发展已稳定了吗?
融合了除错和新功能的量产发行在推出前皆经过广泛的测试。自 5.000发行以来, 我们平均一年才出版一次量产发行。

Larry 和 Perl发展小组有时候会修改语言的核心部分,但总是尽一切力量让新版 和旧版保持一致。因此,尽管不是所有的 perl4 scripts都能在 perl5 之下跑得天衣无缝,因升级而导致按照先前版本的 perl所写的程式无法使用的情形几乎不曾发生(除非该程式倚赖已经被去除的 bugs,或使用了极少数新加入的指令来 命名)。


--------------------------------------------------------------------------------

Perl难学吗?
Perl不但容易上手,也容易继续学下去。它看起来和大多数您可能已接触过的语言一样。所以如果您只写过 C 程式、或 awk script、shell script,或甚至只是 Excel的 macro(巨集),您已经在半路了。

大多数的任务只需要 Perl语言的一小部分即可完成。发展 Perl程式的座右铭即 是「不只一种方法可以达到」(TMTOWTDI; There's More Than One Way To Do It, 有时读作「堤姆投迪」)。因此,Perl的学习曲线是既平(易学)且长的 (如果您要的话,有一大堆够您学的)。

最後,Perl(通常)算是解译式的语言。也就是说您写了程式後不需经由一道中 间的编码过程即可测试;这让您可以很快、很容易地测试及除错。这个方便试验的 特性又让学习曲线变得更加平坦。

有助於修习 Perl 的一些事:UNIX经验、对几乎是任何一种程式语言的经验、了解 regular expressions(正规表示法),以及看得懂旁人写的程式的能力。如果您有什麽想用 Perl来做的事,那麽可能已经有前人做过了,而且实例通常可免费取得。还有别忘了新的 Perl模组。模组在这份 FAQ 的第叁部分有详细的讨论,还有【别忘了您的好朋友】 CPAN,这会在第二部分谈到。


--------------------------------------------------------------------------------

Perl和其他的程式语言比起来如何?例如 Java, Python, REXX, Scheme,或 Tcl?
Perl在某些地方比较好,某些地方较差。精确地说到底哪些方面好或坏通常视个 人偏好而定,所以在新闻讨论群中问这种问题很可能会掀起一场毫无建设性的圣战。

要比较各语言的异同最好的方法是试着用不同的语言写功能相同的程式。各程式语言都各有属於它们各自的新闻讨论群,您可从中学习(但希望您不是去和人辨论吵 架的)。


--------------------------------------------------------------------------------

我可以用 Perl来做【某种差事】吗?
Perl有足够的弹性和扩充性,从只需要写短短一行的档案处理工作到复杂的系统,几乎没有什麽做不到的。对有些人来说,Perl的是拿来做写 shell程式的理想替代品。其他人则用高阶的 Perl来替代处理许多原先需要用 C或 C++ 一类的低阶语言来达到的程式。哪些差事决定要用 Perl来处理,这一切都得看您(或许还有您的经理...)。

如果您有一个提供 API的程式库的话,您可用 C或 C++来写一个 Perl 延伸,然後便可透过它将程式库中的任何一部分动态载入您的 Perl主程式中。您也可以 反过来,用 C或 C++来写主程式,然後以即时动态载入的方式插入一些Perl程式码,产生一个威力强大的应用程式。

话虽如此,对解决某些特定的问题,使用小型、专精,专为特殊用途设计的语言总 是比较方便的。 Perl的设计是尽力地满足各种不同人的需要,因而不特别偏颇任何人。至於特殊功能语言的例子,随便举两个,譬如 prolog 和 matlab 便是。


--------------------------------------------------------------------------------

哪些场合下不适合用 Perl?
当您的主管禁止的时候 -- 不过请务必考虑把他们换掉 :-)。

说真的,如果您已经有用另一个语言写成的应用程式(而且写得很好)的时候,或 者是已经有替某些特定的工作设计的语言(例如:prolog, make),这个时候就不 需要用 Perl。

由於种种因素,Perl大概不太适合拿来做即时内嵌式系统、属於低层级的作业系统发展工作,例如周边设备的 drivers或环境转换码、复杂的多线共用记忆体应用程式,或非常大的应用程式。您会发现 Perl 本身便不是以 Perl写成的。

刚出炉的 Perl纯码编译器或许可帮忙去除一些上述的限制,但您要了解:Perl在本质上仍是一活性变数语言 (dynamically typed language),而非固性变数 (statically typed)。只要您不将核电厂或脑科手术监视器所用的程式放心地用 Perl来写,您自然就不会闯祸遭殃。这样 Larry晚上也可以睡得安稳些 --股市分析程式不在此限 :-)。


--------------------------------------------------------------------------------

「perl」和「Perl」有什麽不同?
二者差一个位元。喔,您不是说在 ASCII上的差别啊? :-) Larry现在用「Perl」来代表语言本身,而以「perl」来表示该语言的体现,即目前的解译器。因此,作者有句幽默小语说:「只有 perl可以解译 Perl」。要不要遵照这个用法是您的自由。举一反叁的话,我们可依样画葫芦地说「awk 和 perl」还有「Python 和 Perl」,但却不可将「awk 和 Perl」或是「Python 和 perl」摆在一起。


--------------------------------------------------------------------------------

Perl程式应算是 program还是 script?
都无所谓。

按标准术语来讲,program指已经由编译程序编译好、转为机器码,可多次执 行的程式;而 script则是每次执行时都必须透过一个解译程式来解译。然而,Perl程式严格说来,既非编译 (compiled) ,亦非解译式 (interpreted);因 Perl程式可转译成位元码形式存在(可说是某种 Perl虚拟机器 [virtual machine]),或转译为完全不同的语言,如 C或组合语言。所以光看原始码很难 说它到底是替纯解译器、或是 parse-tree解译器、位元码解译器,还是纯码编译器而写;因此这题很难给它一个确切的答案。


--------------------------------------------------------------------------------

JAPH是什麽?
这是过去一些在讨论群中自称 ``just another perl hacker'' 的人的签名档,约有一百个比较早期的,可在 http://www.perl.com/CPAN/misc/japh 取得。


--------------------------------------------------------------------------------

到哪儿可拿到 Larry Wall的智慧讽语 (witticisms)?
一百多条 Larry的讽语,源自他【在讨论群】的 posts或原始码,可在 http://www.perl.com/CPAN/misc/lwall-quotes 取得。


--------------------------------------------------------------------------------

我要如何取信、说服我的系统管理者/上司/属下使用第 5/5.004版的 Perl,而不去用其他的语言?
如果您的管理阶层或属下对没有支援的软体,或是未正式包含在所购买的作业系统中的软体存有戒心的话,您可以试着从有助他们自身利益这方面下手。因为如果程式设计师能由善加利用 Perl的结构、功能性、简单性,和威力而获得更大的生产力的话,那麽典型的管理者/上司/员工或许便可因而加以说服。此外,使用 Perl,总的来讲,和其他语言相较,或许也有助於减少交件的时间。强调这个论 点或许对说服他们会有帮助。

如果您的专题碰到瓶颈,特别是有关转译或测试方面的问题,那麽 Perl可以说绝 对会是一个既可行且快的解决之道。您在当说客的时候,千万别忘了要提:Perl已 被世界上许多大型的软硬体公司广泛、大量地使用,极为可靠、有效。事实上,现 Perl已成为许多 Unix业者所售的作业系统的标准配备了。而且如果您无法在 详尽的使用说明,包括这份 FAQ之中为您的问题找到解答的话,送封 post 到新闻讨论群即可。

如果您面对反对 perl升级的声音,那麽告诉他们 Perl发展小组已经完全不再维护或支援第四版的 perl了。perl5的另一个大卖点是它有大量的模组和延伸,可大大减少计画的发展时间。还有,告诉他们第四和第五版 Perl之间的差异就如 awk 和 C++的差别一样(嗯,或许没有差得那麽明显,但您知道我的意思就好)。如果您想得到支援而且想确保您现在所发展的软体在未来能继续工作的话,那麽您得跑有支援的版本。这大概也就是说要跑 5.004 版的,尽管 5.003 版仍算是不错(它只落後一年、一版)。不过因为有些严重的 bugs曾在 5.000 和 5.002版之间被消除,所以您至少应升级到比这几个版本高才是。
回复

使用道具 举报

321

主题

773

帖子

4768

金币

大家网大学四年级

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

积分
3139
 楼主| 发表于 2008-12-25 16:51 | 显示全部楼层
哪些平台上有 Perl?要到哪里去找?
Perl的标准发行版(由 perl发展小组负责维护)仅以原始码形式发行。您可在 http://www.perl.com/CPAN/src /latest.tar.gz处取得。这个档案的格式是 POSIX tar档案柜,再以 gzip格式压缩。这套原始码完全不需任何移植工作便可轻易地在绝大多数的 Unix系统(Perl的原生系统),以及 Plan 9、VMS、QNX、 OS/2 ,和 Amiga上编译安装完成。尽管有谣传说(即将推出的)第 5.004版或许能直接在 Windows NT上成功地编译和安装,但这尚待证实。适合 32 位元的微软系统和苹果系统、以执行档形式发行的 Perl各可在 http://www.perl.com/CPAN/ports/这个目录底下找到。由於这两个不属於标准发行的一部分,它们可能(事实上的确)和基本的 Perl有多方面的不同。要确切知道到底哪些地方不同,您得自行查阅它们各自的发行说明。这些差异可能是正面的(譬如它们可能附有一些原始码发行的 perl所没有的延伸,提供专属某一平台的特殊功能),亦或负面的(例如它们可能是植基於比较老旧的 Perl原始码发行 版)。

一个实用、专为 Win32 Perl使用者编写的 FAQ可在 http://www.endcontsw.com/people/evangelo/Perl_for_Win32_FAQ.html处取得。


--------------------------------------------------------------------------------

要如合取得以执行档形式发行的 Perl?
不管为什麽您的作业系统业者没有将 C编译器附在所卖的作业系统中,最好的方法是到网路上去抓一份 gcc的执行档,然後用它来编译 perl 。 CPAN 上所放的 gcc执行档仅专门提供几个特别难拿到免费编译器的平台,而不是给任何 Unix系 统的。

您的第一步应该是查看 http://www.perl.com/CPAN/ports这个档案,看看可以拿到哪些安装资料。 http: //www.cs.ruu.nl/~piet/perl5dos.html 提供了一份有关在 DOS上安装 perl 的资料;而 http: //www.cs.ruu.nl/~piet/perlwin3.html则是关於在 Windows 3.1上安装的资料。


--------------------------------------------------------------------------------

我的系统里没有 C编译器。要如何编译 perl?
因为您没有 C 编译器,您是没指望了,而您的经销商则该拿去当作祭拜列位升阳 神的供品。不过说这些风凉话无济於事。

您首先需要做的是替您的系统找一个 gcc的执行档。参阅和您的作业系统相关的 各 Usenet FAQs,看到哪里可以找到这种作业系统的 gcc执行档 。


--------------------------------------------------------------------------------

我直接将 Perl的执行档从一台机器上复制到另一台机器上,但是程式跑不起来。
那大概是您忘了复制程式库,或者是程式库的路径不同的关系。您真的应该在那台 要安装 perl的机器上将整套发行从头编译,然後打 make install来安装。其他的方法大多注定要失败。

有一个简单的方法可用来检查和确定东西有没有装对地方 --把编入 perl的 @INC阵列(perl用它来寻找程式库的路径)印出:

perl -e 'print join("\n",@INC)'

如果这个指令列出了任何在您系统上不存在的路径,那麽您或许得将适当的程式库移到这些地方,或者制做适当的 symlinks、aliases 或捷径。

您或许会想看看 How do I keep my own module/library directory? 。


--------------------------------------------------------------------------------

我抓回了原始码,试着编译 perl,但是 gdbm/dynamic loading/malloc/linking/...部分失败。要如何将它搞定?
细读 INSTALL这个档案,这是原始码发行版里面的一个档案。有时候自动设定程式 (Configure) 对某些较不寻常的系统、平台特质、或变异会不知所措。该档 案对该如何处这类的问题,大都有详细的说明。


--------------------------------------------------------------------------------

Perl有哪些模组和延伸? CPAN是什麽? CPAN/src/...又代表什麽?
CPAN代表的是「大 Perl档案库网络」(Comprehensive Perl Archive Network),一个在全世界数十台机器之间相互映射的巨大档案库。CPAN包含了原始码、对各非原生系统的移植、使用说明、程式,以及许多由第叁类团体所写的模组和延伸,从各商业品牌的资料库介面、到键盘/萤幕控制,乃至全球资讯网漫游及 CGI程 式皆一应具全。CPAN的总主机是ftp://ftp.funet.fi/pub /languages/perl/CPAN/,但您也可以透过这个位址:http://www.perl.com/CPAN/CPAN.html来自动连接一个在地理位置上最接近您的站。至於这个设计的运作原理,请看 http://www.perl.com/CPAN(最後头没有斜线)的说明。

CPAN/路径/... 是 CPAN站台上头的档案的命名规范。CPAN 代表一个 CPAN映射的基准目录,然後其馀的路径是由该目录到一个档案的路径。例如,如果您使用 ftp://ftp.funet.fi/pub/languages/perl/CPAN来做您的 CPAN 站,那麽 CPAN/misc/japh这个档案便可以从 ftp://ftp.funet.fi/pub/languages/perl/CPAN/misc /japh 抓下来。

由於目前 CPAN档案库中已经有数百个模组,因此几乎任何您所能想到的用途,大概都已经有现成的模组可以办到。目前在 CPAN/modules/by-category/ 底下的类 别包括了 perl核心模组、协助发展模组、作业系统介面、网路、周边设备、不同 processes间之沟通、资料型态工具、资料库介面、使用者介面、与其他语言介面、档名、档案系统、档案锁定、软体国际化及地方化、全球资讯网支援、伺服软体工具、档案库和档案压缩、图形变换处理、电子邮件及新闻讨论群、程式流程控制工具、filehandles和输入/输出、微软视窗模组,以及杂项模组 等。


--------------------------------------------------------------------------------

是不是有一个经 ISO【国际标准局】或 ANSI【美国国家标准局】认可的 Perl版本?
当然没有。Larry认为他得先被认可後然後才会轮到 Perl 。


--------------------------------------------------------------------------------

Perl的相关资料要上哪儿找?
perl的发行版中都附有完整的使用说明中。如果 perl已安装在您的机器上,那 麽使用说明应该也已经装在上头了:如果您用的是一个像 Unix的系统,您可以打 man perl。这同时会带领您到其他重要的使用说明页。如果您用的不是 Unix 式的系统,那麽查阅使用说明的方法会有所不同;譬如说,使用说明可能会以HTML 格式来存放。不管怎麽样,只要 perl正确地安装,查阅使用说明应该不成问题。

如果您的系统没有 man这个指令,或者是该指令安装不当,那麽您可以试试 perldoc perl。如果还不成,您可以在 /usr/local/lib/perl5/pod这个目录 下找使用说明。

如果以上的方法全失败,那麽您可求助於 CPAN/doc这个目录,该目录底下存有完 整的使用说明,有各种不同的格式,包括了原始的 pod格式、troff、html,以及 纯文字式。还有 http://www.perl.com/perl/info /documentation.html 这个网页可能也会对您有帮助。

另外值得一提的是,在 CPAN/authors/id/BMIDD这个目录底下有一个完整的 PDF 版本的使用说明。

市面上有许多本和 Perl有关的好书,详情请见下面一节的介绍。


--------------------------------------------------------------------------------

USENET上有哪些专门讨论 Perl的新闻讨论群?问题该投到哪里?
comp.lang.perl这个群组已不存在,它已被下列各群组所取代:

comp.lang.perl.announce公告相关事宜(有管制)
comp.lang.perl.misc一般问题讨论,很忙
comp.lang.perl.modules模组的使用和发展
comp.lang.perl.tk Perl Tk (及 X)的讨论

comp.infosystems.www.authoring.cgi WWW CGI程式写作相关问题研讨

news://genetics.upenn.edu/perl.porters-gw/ 还有一个 Perl发展小组 (Perl5-porters)用来连结邮递论坛的渠道。


--------------------------------------------------------------------------------

如果我想投程式原始码,该投到哪个板子上?
您应看程式的性质来决定该丢到哪个板子上,但也欢迎您交叉投递一份到 comp.lang.perl.misc上头去。如果您打算交叉投递到 alt.sources 的话,请务必遵照该板所规定的标准,包括标头的 Followup-To栏不可将 alt.sources 列入; 详见该板的 FAQ 。


--------------------------------------------------------------------------------

Perl书籍
市面上有许多有关 Perl 和/或 CGI程式设计的书。其中有些很好,有些还过得去,但也有不少根本不值得买。大部分的 Perl书都列在 Tom Christiansen所维护的 http://www.perl.com/perl/critiques/index.html 中,其中有的书有详尽 的评论。

毫无争议地,最权威的 Perl参考书要数以下这本,由 Perl的创始者及其信徒所 着,目前是第二版第四印:

Programming Perl(俗称「the Camel book;骆驼册」):
作者: Larry Wall, Tom Christiansen, and Randal Schwartz
ISBN 1-56592-149-6(英文版)
ISBN 4-89052-384-7(日文版)
(法文、德文版准备中)

请注意,O'Reilly【上面这本和以下两本书的出版社】的书是按颜色分类的 --土耳其玉【蓝绿】色(有人也许会说它是鸭毛绿)的封皮代表内容涵盖 perl5 ,而紫红(有人也许会说它是粉红)色的封皮则代表内容仅限於 perl4 。所以先看看 外皮再买!

底下是几本是笔者个人觉得有帮助的书。您个人的感觉、偏好或许会不同(但我们 希望不会)。

如果您是个扎实的系统程式设计高手的话,那麽直接拿骆驼册来学 Perl大概就足 够了。但是如果您没有这麽厉害的话,那麽先看看「骆马册」。这本书目前并不涵盖 perl5,但第二版已接近完成,应可在 1997年夏天前问市。

Learning Perl(俗称「the llama book;骆马册」)
作者: Randal Schwartz,由 Larry Wall作序
ISBN 1-56592-042-2(英文)
ISBN 4-89502-678-1(日文)
ISBN 2-84177-005-2(法文)
ISBN 3-930673-08-8(德文)

另一本出类拔粹的、同属 O'Reilly Perl系列的书是「帅猫头鹰」册。它从里到 外地对正规表示法 (regular expressions)加以剖析,其中有相当的份量是专门 针对 Perl写的:

Mastering Regular Expressions(the Cute Owls Book;可爱的猫头鹰册):
作者: Jeffrey Friedl
ISBN 1-56592-257-3

您可以向 O'Reilly & Associates订购以上这几本书,他们的电话是 1-800-998-9938(美加地区)和 1-707-829-0515(世界其他各地)。如果您有 O'Reilly的订购单,您可以传真 1-707-829-0104 。详情请到网路上的 http://www.ora.com/去看。

其他不由 O'Reilly 出版、笔者推荐的 Perl 丛书:

Cross-Platform Perl,(针对 Unix及 Windows NT)
作者: Eric F. Johnson
ISBN: 1-55851-483-X

How to Set up and Maintain a World Wide Web Site, (2nd edition)
作者: Lincoln Stein, M.D., Ph.D.
ISBN: 0-201-63462-7

CGI Programming in C & Perl,
作者: Thomas Boutell
ISBN: 0-201-42219-0

要提醒您的是以上这些书有的是针对某些应用领域(例如 WWW)而写,而非探讨一 般的程式写作。


--------------------------------------------------------------------------------

和 Perl有关的杂志
Perl Journal 是第一、也是唯一的一本专门探讨 Perl 的期刊,每季发行一次(为一纸上期刊,非电子期刊)。发行人、总编辑是 Jon Orwant ( orwant@tpj.com )。订阅资料可在 http://tpj.com,或透过 email 至 subscriptions@tpj.com取得。

除此之外,另外两本杂志也常登载高水准的 Perl 文章,它们是 Web Techniques(详见 http://www.webtechniques.com/ )和 Unix Review(http://www.unixreview.com /)。Randal Schwartz在 Web Techniques杂志的专栏可从 WWW 上的 http: //www.stonehenge.com/merlyn/WebTechniques/ 处取得。


--------------------------------------------------------------------------------

网路上的 Perl:透过 FTP和 WWW
如果您想达到最好(还有最省钱)的传输效果,那麽从下面所列的站台中任选其一, 从上头把完整的映射站名单抓下来。然後您可以从中挑选一个对您来说传输最快的 站台。请记住,底下 不是完整的 CPAN映射站名单。

http://www.perl.com/CPAN(自动反弹到其他的映射站)
http://www.perl.org/CPAN
ftp://ftp.funet.fi/pub/languages/perl/CPAN/
http://www.cs.ruu.nl/pub/PERL/CPAN/
ftp://ftp.cs.colorado.edu/pub/perl/CPAN/

http:/www.oasis.leo.org/perl/除了其他的东西外,还收集了有从第一一直到第 五版的 Perl原始码。


--------------------------------------------------------------------------------

有哪些讨论 Perl的邮递论坛(mailing lists)?
大部分的重要模组(如 tk、CGI 和 libwww-perl)有专属各自的 mailing lists。有关资料请参考这些模组的使用说明。以下的 mailing lists则是和 perl本 身有关的:

如果您订阅加入一个 mailing list,则您有义务知道该如何取消订阅。如果只是 苦苦地对着论坛哀求是不会被人接受的【因论坛的邮件位址和管理订阅的伺服软体 位址通常并不相同】。

MacPerl
这是讨论麦金塔 Perl的邮递论坛。详情请连络「 mac-perl-request@iis.ee.ethz.ch 」。
还有您可以由 Matthias Neeracher(MacPerl的创造及维护者)的网页 http://www.iis.ee.ethz.ch/~neeri/macintosh/perl.html 连到许多有趣的 MacPerl站台,以及事先编译好的应用程式/MPW发展工具。

Perl5-Porters
这是 perl的核心发展小组用来讨论语言本身除错和修改的邮递论坛。您可寄信到 perl5-porters-request@perl.org ,在信中正文的地方写上「help」,便可收到有关的订阅资料。
NTPerl
这是讨论 Win32 Perl5(Windows NT 和 Win95)的邮递论坛。如欲订阅,寄信到 ListManager@ActiveWare.com,在信中正文的地方写上:
subscribe Perl-Win32-Users

这个用 perl写的邮递论坛管理软体会自动找出您的位址,然後将您加入名单中。 如果您要取消订阅,寄信到同一个地方,在正文处注明:

unsubscribe Perl-Win32-Users

您也可以连到 http://www.activeware.com/ ,然後选择「Mailing Lists」,便可加入或离开这个邮递论坛了。

Perl-Packrats
这是用来讨论 perl资料的储存管理等相关事宜,特别是有关大 Perl档案库网路(CPAN)的邮递论坛。有意订阅者可 email majordomo@cis.ufl.edu ,文中注明:
subscribe perl-packrats

这个也是用 perl写的邮递论坛管理软体会自动找出您的位址,然後将您加入名单 中。如果您要取消订阅,寄信到同一个地方,於正文处将相同的订阅指令前加上 「un」,像这样:

unsubscribe perl-packrats


--------------------------------------------------------------------------------

存有 comp.lang.perl.misc posts的档案库
试过 Deja News或 Alta Vista 了吗?

ftp.cis.ufl.edu:/pub/perl/comp.lang.perl.*/monthly有自 1989年 12月起(缺 1991年 8月份)至 1993年 12 月止的完整收藏。每个月的 posts 是以一个大档案的方式存放。

您可能会想要一个功能比较完备的讯问和抽取系统,而不只是能将档案名称列印出来;最好是一个使用索引作快速寻找的引擎,且至少可以按作者、日期、主题、 thread(一如 trn),或许还有关键字,来作搜索。作者所知道最好的方法是 MH 套件的 pick指令,但是如果拿它来搜索上万篇文章的话真的是很慢。

如果您曾经找到、或知道到哪儿可找到残缺的部分,请告知: perlfaq-suggestions@perl.com


--------------------------------------------------------------------------------

Perl训练课程
虽然有些大型的训练公司提供各种 Perl的训练课程,但您可能会较顷向於找真正 密切投入 Perl发展的人来教您。Perl 发展小组中两位知名的成员 -- Tom Christiansen 和 Randal Schwartz ,加上二人手下的众爪牙,联手 提供各项有关 Perl的专业入门讲座和研讨课程。这些课程包括了公开座谈,私人企业员工训练计画,以及直接飞到科罗拉多和奥勒冈上课等。详情请见 http://www.perl.com/perl/info/training.html


--------------------------------------------------------------------------------

如何购买商业版本的 Perl?
在某种程度上来说,Perl 已经算是商业软体了:您可以把 Perl的发行约定拿来细读给您的经理听。各发行版都附有这份条例清楚、明确的公约。Perl有广大的使用者及广泛的文献。comp.lang.perl.*等新闻讨论群组和各电子邮递论坛更是对各种疑难杂症提供迅速的解答。 Perl 传统上一直是由 Larry、许多软体设计工程师,以及成千上万的程式写作者提供支援,大夥协力让人人过更美好的日子。

尽管如此,有些主管坚持只向附售後保证的公司下订单,这样子出了问题才可以告 他们,故以上的回答可能无法令这类的经理满意。或许是这类的主管觉得亦步亦 趋的扶持支援及很强的合约义务有其必要。市面上有卖用玻璃纸密封包装的 Perl 光碟,您可以试试看,或许对您的经理有效。

不然您可以购买使用支援的契约。虽然 Cygnus在过去曾提供这项服务,他们现在已不再出售 Perl的支援合约了。取而代之、填补这个空档的是 the Paul Ingram Group 为此专设的 The Perl Clinic。以下是一则他们的广告:【未翻】

``Do you need professional support for Perl and/or Oraperl? Do you need a support contract with defined levels of service? Do you want to pay only for what you need?''

``The Paul Ingram Group has provided quality software development and support services to some of the world's largest corporations for ten years. We are now offering the same quality support services for Perl at The Perl Clinic. This service is led by Tim Bunce, an active perl porter since 1994 and well known as the author and maintainer of the DBI, DBD::Oracle, and Oraperl modules and author/co-maintainer of The Perl 5 Module List. We also offer Oracle users support for Perl5 Oraperl and related modules (which Oracle is planning to ship as part of Oracle Web Server 3). 20% of the profit from our Perl support work will be donated to The Perl Institute.''

如需进一步的资料,可连络 The Perl Clinic:

Tel: +44 1483 424424
Fax: +44 1483 419419
Web: http://www.perl.co.uk/
Email: perl-support-info@perl.co.uk or Tim.Bunce@ig.co.uk


--------------------------------------------------------------------------------

如果发现 bugs要向何处报告?
如果您发现 perl解译器或标准发行中的模组有 bugs ,想报知 perl发展小组的 话,请使用 perl发行中所附的 perlbug程式,或将您的报告 email到 perlbug@perl.com

如果您想报告的 bug是有关某个非标准发行的 perl(详见「哪些平台上有 Perl ?」一题的答案)、某可执行档形式的发行,或是某非标准的模组(譬如 Tk、CGI 等),那麽请参考它所附的使用说明,以确定最合适报告 bugs的地方。

详情请见使用手册中 perlbug一篇(附於 perl5.004 或以後版本中)。


--------------------------------------------------------------------------------

什麽是 perl.com、 perl.org,和 Perl Institute?
perl.org 是 Perl Institute 的正式传播媒介。TPI (The Perl Institute)的座右铭是「帮助他人帮 Perl助人」(或差不多这个意思)。这是一个非营利性的组织,主旨在支援 perl的发展、文献,和传播。目前 TPI 的领导人包括了 Larry Wall 、 Tom Christiansen,和 Randal Schwartz,这些名字或许您曾在这篇文章的其他地方见过。

perl.com 是 Tom Christiansen注册的网域。早在 perl.org成立之前他便创立了这个站台,做为公共服务之用。这是 Perl国度的公共电台,所有 Perl 东西的资讯交易所,该站的网页不接受登载任何商业广告、光鲜的 GIF 看板,或者是 (啊!) Java applets。


--------------------------------------------------------------------------------

如何学着用 Perl来写物件导向程式?
perltoot(附在 5.004、或更新版之中)是一个很好的起点。此外, perlobj 、 perlref ,及 perlmod 都是很有用的参考资料;而 perlbot则还提供了一些非常好的技巧和心得。
回复 支持 反对

使用道具 举报

321

主题

773

帖子

4768

金币

大家网大学四年级

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

积分
3139
 楼主| 发表于 2008-12-25 16:51 | 显示全部楼层
我如何作 (任何事)?
你到 CPAN(见 perlfa)找过了吗?也许别人已经写了某个模组可以解决你的 问题。你查过相关的说明文件了吗 (man pages)?以下是一份概要的索引:

物件 (Objects) perlref, perlmod, perlobj, perltie
资料结构 (Data Structures) perlref, perllol, perldsc
模组 (Modules) perlmod, perlmodlib, perlsub
正规表示法 (Regexps) perlre, perlfunc, perlop
升级至 Perl5 (Moving to perl5) perltrap, perl
与 C连结 (Linking w/C) perlxstut, perlxs, perlcall, perlguts, perlembed
杂项 (Various) http://www.perl.com/CPAN/doc/FMTEYEWTK/index.html
(不是说明文件,但还是很有用)

perltoc里有一份粗略的 perl 说明文件组的目录。


--------------------------------------------------------------------------------

如何以互动的方式使用 Perl?
典型的作法是使用 perldebug(1)说明文件里提到的 Perl 除虫器,在一个「空的」(译者:即不存在的)程式上执行,像这样:

perl -de 42

接下来所打入的任意合法 Perl程式码皆会立刻被评估。同时,你可以检查符号表 (symbol table)、取得堆叠的记录 (stack backtraces)、检视变数值、设定阻断点 (set breakpoints) 以及其他符号式除虫器 (symbolic debuggers) 所能作的动作。


--------------------------------------------------------------------------------

有 Perl shell吗?
基本上来说,没有。Shell.pm模组 (是 perl 标准套件之一)只是叫 perl 将非 Perl语言的命令当作 shell的命令来试着执行看看罢了。perl原始码套件中的 perlsh,功能简易,也很无趣,不过仍可能是你所要的。


--------------------------------------------------------------------------------

如何替我的 Perl程式除虫?
你用过 -w吗?

你试过 use strict吗?

你是否检查过每个系统呼叫 (system call)所传回的值?

读了 perltrap说明文件吗?

你试过 perldebug里所提到的 Perl除虫器吗?


--------------------------------------------------------------------------------

如何检测 (profile)我的 perl程式?
你该自 CPAN抓取 Devel::DProf 模组,并且使用 perl 标准套件所附的 Benchmark.pm。 Benchmark.pm让你测量程式码的某部份在执行上所花的时间,而 Devel::DProf则详细地替你分析哪一部份的程式用掉多少时间。


--------------------------------------------------------------------------------

如何替我的 Perl程式作交叉参考 (cross-reference)?
随着新发行的 alpha版 Perl编译器(它不在一般标准套件里)而来的 B::Xref模组可 以替你的 Perl程式制作 cross-reference报告。用法是:

perl -MO=Xref[,OPTIONS] foo.pl


--------------------------------------------------------------------------------

有 Perl专用的美化列印程式 (pretty-printer)吗?
C有 indent(1)可以将原始码格式美化,但 Perl并没有能做得像它那麽好的东西。扫瞄器 (scanner) 和分析器 (parser) 间复杂的反馈 (feedback)(把 vgrind 和 emacs等程式搞混的就是这反馈)使得撰写一个独立的 Perl 分析器成了一项艰巨的挑战。

当然,若你直接照 perlstyle里面的指示写程式,就根本没有必要重新安排格式。

你所用的编辑器可以并也应能帮你把原始码的格式弄漂亮些。像 emacs的 perl-mode就能帮你把大部分 (但非全部)的程式码排列得漂亮些,而其它普通的编辑器也能提供一定程度的协助。

如果你试着使用 vgrind程式从雷射印表机印出漂亮的原始码,可以参考: http://www.perl.com/CPAN/doc/misc/tips/working.vgrind.entry ,但是碰到复杂的程式码可能就不能全然令人满意了。


--------------------------------------------------------------------------------

有 Perl的 ctags吗?
有个简单的在 http://www.perl.com/CPAN/authors/id/TOMC/scripts/ptags.gz 也许符合你的需要。


--------------------------------------------------------------------------------

哪里有 vi用的 Perl巨集?
ftp://ftp.perl.com/pub/vi/toms.exrc有完整的 Tom Christiansen之 vi设定档, 它是给 vi模拟器用的标准测试档 (standard benchmark file)。它与 nvi配合得最好,巧的是,这个出自 Berkeley的编辑器也可以内嵌一个 Perl直译器 --参看 http://www.perl.com/CPAN/src/misc


--------------------------------------------------------------------------------

给 emacs用的 perl模式又要去哪抓呢?
从大约 Emacs 19.22版 (version 19 patchlevel 22)起,已内含了 perl-mode.el及 perl 除虫器的支援。它们应该会和标准的 Emacs 19版一起出货。

在 perl原始码的目录下,你会找到一个叫作 ``emacs'' 的目录,里面包括一个 cperl-mode 可以把程式中的关键字上色、提供内文相关的协助以及其它方便的功能。

注意:``main'foo''(其中的单引号)会让 emacs的 perl-mode生病,并且会弄乱内 缩 (indentation) 与精华 (hilighting)。不过你本来就该用 ``main::foo''的 (译者按: main'foo 是表示模组或 package的旧式写法;新式的 [perl5的]写法是 main::foo)。


--------------------------------------------------------------------------------

如何在 Perl里使用 curses?
CPAN里的 Curses模组提供了一个通往 curses 程式库的动态载入物件模组介面。


--------------------------------------------------------------------------------

X或 Tk如何与 Perl配合呢?
Tk这个完全以 Perl 为基础,物件导向化的介面,让你不用学 Tcl也可以使用 Tk工具组。Sx则是 Athena Widget set专用的介面。两者都可在 CPAN取得。


--------------------------------------------------------------------------------

如何不靠 CGI或 Tk之助作出简单的目录(选单)?
http://www.perl.com/CPAN/authors/id/SKUNZ/perlmenu.v4.0.tar.gz 是个以 curses为基础的模组,可以达成你的要求。


--------------------------------------------------------------------------------

我可以动态地将 C常式载入 Perl吗?
若你的系统架构有支援的话,标准 perl 套件便应该有此功能(介由 DynaLoader 这个模组)。详情请参看 perlxstut 。


--------------------------------------------------------------------------------

什麽是 undump?
看下个问题。


--------------------------------------------------------------------------------

如何让我的 Perl程式跑得更快些?
最好是能设计一个较好的演算法 (algorithm),这通常会让程式有大不相同的表现。 骆驼书第八章里有些你或许想知道的增进效率小技巧。

其它方法包括自动载入较少使用的 Perl 程式码。请参看标准 perl 套件中的 AutoSplit及 AutoLoader模组的用法。或当你能断定程式执行效率的瓶颈在何处时,用 C来写那个部份,就像用组合语言来撰写 C程式的瓶颈部份一样。与此法相近的是使用以 C撰写瓶 颈部份的模组 (例如 CPAN中的 PDL 模组)。

在某些情况下,使用後端的编译器把程式编译成位元码 (byte code)(可节省编译时间) 或是将 perl程式转编译为 C程式的作法值得一试;这些作法绝对会节省编译的时间并且有时能省一些[但不多]执行时间 。请参考“编译你的 Perl程式”这个问题的答案。

如果你目前是将你的 perl直译器动态连结到 libc.so的话,重新作一份静态连结到 libc.a的 perl直译器可以提高 10-25%的执行效能。虽然这会使你的 perl直译器变得更胖,但你的 Perl程式 (及程式设计者) 或许会因此而感谢你。详情请参考 perl标准套件原始码版本中的 INSTALL 档案。

一些未经证实的报告中宣称有些使用 sfio的 Perl直译器表现得比没有用 sfio的还好 (针对於 IO频繁的应用程式)。想试试看?参考 perl套件原始程式版中的 INSTALL 档案,尤其是 ``Selecting File IO mechanisms''这一段。

使用 undump程式把编译後的档案格式存到硬碟里以加快执行的速度已经是老掉牙的手法了。它已不再是个可行的方法,因为这方法只有几种平台能用,况且它终究不是个治本之 道。


--------------------------------------------------------------------------------

如何让我的 Perl程式吃少一点的记忆体?
当问题变成时间与空间的交易时, Perl 几乎总是用记忆体来帮忙解决问题。 Perl中的纯量 (Scalar) 耗掉的记忆体比 C中的字串形态还多,阵列又更多, 更别谈杂凑阵列了 (Hashes)。关於这一点,我们当然还有很多工作得作,近来发布的版本,已开始针对这些问题做改进了。例如, 5.004 版中, 重复的杂凑阵列索引值 (duplicate hash keys) 由使用它的杂凑阵列共用,这样就不用再重新定份位置给它了。

在某些情况下,使用 substr()或 vec()来模拟阵列有很大的好处。例如,一个有上千 个布林代数值的阵列将占用至少 20,000位元组的空间,但是它可以被转变为一个 125位元组的位元向量 (bit vector)以节省相当可观的记忆体。标准套件中的 Tie::SubstrHash模组也能够帮助特定形态的资料结构节省些记忆体。若你正在和一些特殊的资料结构奋战 (例如,矩阵),用 C写的模组所耗掉的记忆体可能低於同功能并用 Perl写的模组。

另一件值得一试的是,查一下你的 Perl是以系统内的 malloc 还是 Perl内含的 malloc 编译起来的。不论是哪个,试着换成另一个,再看看这是否造成任何差别。关於 malloc的资讯可在 perl标准套件原始码版中的 INSTALL 档案找到。键入 perl -V:usemymalloc就可以知道你是否在使用 perl的 malloc。


--------------------------------------------------------------------------------

把指标传回到区域资料是不安全的做法吗?
不,Perl的资源回收 (garbage collection)系统会解决此问题。

sub makeone {
my @a = ( 1 .. 10 );
return \@a;
}

for $i ( 1

print $many[4][5], "\n";

print "@many\n";


--------------------------------------------------------------------------------

我如何释放一个阵列或杂凑阵列以缩小我的程式尺寸?
你无法这麽作。系统配置给程式的记忆体是覆水难收。这也是为何执行很长一段时间的 程式有时会重新执行 (re-exec)它们自己的原因。

然而,在使用你的变数时,明智地用 my()来定义执行范围,可让 Perl在脱离该范围後 将它们所占的空间释放给其它部份的程式。 (注:my()的变数也比全域变数执行起来快 10%。)当然,一个全域变数永远没有超出范围的时候,所以你无法将它占用的空间自动重新分配,不过,把它 undef() 或/和 delete()会有相同的效果。总之,在 Perl里,你并不能/应该去担心太多有关记忆体定址与解除这件事,而我们连添加这项功能(资料形态的预先定址),目前都已在进行中。


--------------------------------------------------------------------------------

如何让我的 CGI脚本 (script)执行起来更有效率?
除了使一般 Perl程式加快或缩小的平常手段外,一个 CGI 程式还有其他的顾虑。也许它每秒会被执行好几次。每次它执行时,重新编译所花的时间、加上定址所需的 1 MB以上的系统记忆体,就是一个大杀手。光是编译成 C 是没啥帮助的 ,因为瓶颈在於整个程序开始时所负担的包袱 (start-up overhead) 。

最起码有两种较流行的方法可以避免这些包袱。一种解法是将 mod_perl 或是 mod_fastcgi其中一个模组加在你所执行的 Apache HTTP server (可从 http://www.apache.org/取得)。有了 mod_perl 和 Apache::*模组 (从 CPAN取得),httpd执行时会带起一个内 嵌的 Perl直译器,而它会预先编译你的程式,并在不产生其它子程序的情况下用同一个定址空间来执行。Apache 扩充模组亦给 Perl一个连通 server API 的管道,所以用 Perl写的模组可以做到任何 C写的模组所具备的功能。而有了 FCGI模组 (自 CPAN取得),你以 sfio (参看 perl标准套件原始码版本中的 INSTALL档案) 和 mod_fastcgi (从 http://www.fastcgi.com/取得)模组编译成的 Perl 直译器将使你的每个 perl程式变成一个固定的 CGI 背景程序 (daemon process)。

这些方法对你的系统与你撰写 CGI程式的方法都有超乎想像之外的影响,所以请小心地 探索它们。


--------------------------------------------------------------------------------

如何隐藏 Perl程式的原始码?
删除它。 :-) 说真的,有一些具有不同“安全”等级的方法(大部分都不能令人满意)。

然而,首先,你 不能拿走读取权,不然你的程式怎麽被解译或是编译呢? (不过那也并不表示一个 CGI程式的原始码可以被使用者读取。)所以你得让档案权限停留在 0755这个友善的阶段。

有些人认为这是个安全上的漏洞。不过若你的程式作的是不安全的事情,光仰赖别人 看不见这些漏洞、不知从何下手,那麽它依然是不安全的。其实对有些人来说他们并 不需要看见程式原始码便可能判定并揭露这些不安全的部份。透过隐瞒达到的安全, 就是不修正臭虫反而隐藏它们,实际上是没有安全性可言的。

你可以试着透过原始码过滤模组 (CPAN中的 Filter::*)来替原始码加密。但高手也许有 办法将其解密还原。你也可以用下面提到的 byte code 编译器与直译器,但高手也有可能反解译它。你可以试试後面提到的原生码编译器 (native-code compiler),但高手也有可 能反组译它。这些手段都需要不同难度的技巧才能让别人拿到你的原始码,但没有一种能 够很确定地隐藏它。(这对每种语言来说都为真,不是只有 Perl)

如果你所担心的是别人自你的程式码中获利,那麽一纸权限执照是能提供你法律上安全的唯一途径。注册你的软体并且写份权限说明,再加上一些具威胁性的句子像“这是 XYZ公司未出版的专有软体。你能撷取它并不代表你具有使用的权限...”之类云云。当然,我们不是律师,所以若你想要你的执照中每一句话在法庭上都站得住脚,就去见个律师吧。


--------------------------------------------------------------------------------

如何把我的 Perl程式码编译成 byte code或 C?
Malcolm Beattie已经写了一个多功能的後端编译器,可以从 CPAN取得,它就能做到这两项功能。1997 年二月是 alpha测试版的最後几个阶段,这代表着若你是个程式设计 员而非寻找万灵解药的人,那麽参与其测试就会充满趣味。

请了解光是编译成 C 其本身或在本质上并不能保证它就会跑得快更多。那是因为除了 在运气好的状况中有一堆可以衍生成出来的原生形态外,平时的 Perl 执行系统环境依然 存在因此依然会花差不多长的执行时间与占用差不多大小的记忆空间。大多数程式能省下 来的不过是编译时间,这使执行速度顶多快 10-30%。有些罕见的程式能真正从中受利 (例如增快好几倍),但这还得配合原始码的微调。

Malcolm 将会主导 Perl 5.005 版的发展并试着将其编译器与多执行绪部份的工作融合进主要的发行版本里。

你或许会惊讶地发现,现行版本的编译器做出来的执行档大小跟你的 Perl直译器一样大,有时更大些。那是因为依照现在的写法,所有的程式皆转成一个被 eval()的大叙述。只要建造一个动态连结的 libperl.so程式库,并将之连结起来,你就可以戏剧性地减少这 种浪费。参看 perl 原始码套件中的 INSTALL pod档案以获得更详尽的讯息。如果你用这方法连结你主要的 perl执行档,就能使它变得很渺小。举例来说,在作者之一的系 统里, /usr/bin/perl只有 11k“小”而已!


--------------------------------------------------------------------------------

如何才能让 '#!perl'在 [MS-DOS,NT,...]下作用?
OS/2下只要用:

extproc perl -S -your_switches

当作 *.cmd档案的第一行 (-S 是因 cmd.exe中其 `extproc'处理的臭虫才要的)。DOS使用者应先制作一个相对的 batch 档案然後将它以 ALTERNATIVE_SHEBANG 的方式写成程式。(更多讯息在原始码版本的 INSTALL档案里)

若安装 Activeware版的 Win95/NT 专用 Perl,它会更动 Registry的内容,把 .pl 的扩充档名与 perl 直译器结合。如果你安装另一版本或是用 WinGCC建构你自己的 Win95/NT用 Perl,那你就得自己更动 Registry的内容了。

麦金塔的 perl程式将会有适当的创造者与形态 (Creator and Type),所以双击它们就会执行这些 perl 应用程式。

重要:不论你做什麽,请千万不要因为觉得沮丧,就把 perl 直译器丢到你的 cgi-bin目录下,好让你的 web 伺服器能执行你的程式。这是一个非常大的安全漏洞。花点时间想 想怎样才是正确的做法吧。


--------------------------------------------------------------------------------

我能利用命令列写出有用的程式吗?
可以。详情请看 perlrun。以下有些范例 (假设用的是标准的 Unix shell引言规则)。

#把第一栏和最後一栏相加
perl -lane 'print $F[0] + $F[-1]'

#辨别是否为文字档
perl -le 'for(@ARGV) {print if -f && -T _}' *

#移除 C程式中的说明
perl -0777 -pe 's{/\*gs' foo.c

#让档案年轻一个月,躲避追杀的魔鬼 (daemon)
perl -e '$X=24*60*60; utime(time(),time() + 30 * $X,@ARGV)' *

#找出第一个未用的 uid
perl -le '$i++ while getpwuid($i); print $i'

#显示合理的使用说明路径 (manpath)
echo $PATH | perl -nl -072 -e '
s![^/+]*$!man!&&-d&&!$s{$_}++&&push@m,$_;END{print"@m"}'

好吧,最後一个例子事实上是「perl程式困惑化」竞赛 (Obfuscated Perl)的 参赛作品。 :-)


--------------------------------------------------------------------------------

为何一行的 perl程式无法在我的 DOS/Mac/VMS系统上运作?
问题通常出在那些系统的命令解译器对於参数的引用与 Unix shells 所作的解释不同,而後者很不幸的是这些一行 perl 的生父。在某些系统,也许你得把单引号改成双引号,但这却是你万万 不可在 Unix或 Plan9系统上作的事。你也许还得把一个 %改成 %%。

例如说:

# Unix
perl -e 'print "Hello world\n"'

# DOS,等。
perl -e "print \"Hello world\n\""

# Mac
print "Hello world\n"
(然後执行 "Myscript"或按 Shift-Command-R)

# VMS
perl -e "print ""Hello world\n"""

问题是,这些方法没有一个是完全可靠的:它都得看命令解译器的脸色。在 Unix中,前两者通常可以用。在 DOS下,两者可能都没有用。若 4DOS是命令解译器,下面此法可能比 较有希望:

perl -e "print "Hello world\n""

在 Mac 下,端视你所用的环境为何。 MacPerl所附的 shell,或是 MPW, 其所支援的参数格式有不少都蛮像 Unix shells的,除了它自在地使用 Mac 的非 ASCII字元当成控制字元。

恐怕我得说这问题并没有一般解。白话一点说,它真是一团乱。

[部份答案是由 Kenneth Albanowski 所提供的。]


--------------------------------------------------------------------------------

我得去哪里学 Perl的 CGI或是 Web程式设计呢?
就模组来说,去 CPAN抓 CGI 和 LWP 两个模组。就书本来看,参考关於书那部份里特别和 web 相关的问题。若有与 web相关的疑难杂症,像“为何我收到 500错误”或“它在命令列模式下跑得好好的,怎麽不能在浏览器下正常执行”时,请参看:

The Idiot's Guide to Solving Perl/CGI Problems, by Tom Christiansen
http://www.perl.com/perl/faq/idiots-guide.html

Frequently Asked Questions about CGI Programming, by Nick Kew
ftp://rtfm.mit.edu/pub/usenet/news.answers/www/cgi-faq
http://www3.pair.com/webthing/docs/cgi/faqs/cgifaq.shtml

Perl/CGI programming FAQ, by Shishir Gundavaram and Tom Christiansen
http://www.perl.com/perl/faq/perl-cgi-faq.html

The WWW Security FAQ, by Lincoln Stein
http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html

World Wide Web FAQ, by Thomas Boutell
http://www.boutell.com/faq/

(译者:上面第叁份文件,Perl-CGI-FAQ的中译版可在 http://2Ti.com/cgi-bin/2T/perl/perl- cgi-faq-chi/ 处取得。最後一份(WWW FAQ)的中译版可自 http://www.acer.net/document /cwwwfaq/ 取得。)


--------------------------------------------------------------------------------

在哪可以学到用 Perl作物件导向程式设计?
perltoot是个好开始,然後你可以再参考 perlobj 和 perlbot。Perltoot直到 5.004版本才诞生,但你可以从 http://www.perl.com/CPAN/doc/FMTEYEWTK/下取得 (pod、html,或 postscript 格式)。


--------------------------------------------------------------------------------

哪里可以学到将 C与 Perl相连结? [h2xs, xsubpp]
若你要从 Perl程式呼叫 C,就自 perlxstut开始向 perlxs ,xsubpp ,及 perlguts前进。反之,则读 perlembed ,perlcall ,及 perlguts 。别忘了 你可以从各模组的作者如何写他们的模组及解决他们的问题中学到很多。


--------------------------------------------------------------------------------

我已经读了 perlembed, perlguts,等等,但我仍然无法将 perl嵌入我的 C程式,我做错了什麽?
自 CPAN 下载 ExtUtils::Embed 套件,然後执行 `make test'。如果测试成功,就一遍又一遍地读那些 pod 说明档案。若它失败了,参看 perlbug并送一份内有 make test TEST_VERBOSE=1 与 perl -V输出的报告。


--------------------------------------------------------------------------------

当我试着执行我的程式时,我收到某项讯息。它代表什麽意思?
perldiag有一份完整的 perl错误与警告讯息列表,并附有说明文字。你也可以用 splain程式 (伴随 perl而来)去解释这些错误讯息:

perl program 2>;diag.out
splain [-v] [-p] diag.out

更改你的程式让它替你解释这些讯息也可以:

use diagnostics;



use diagnostics -verbose;


--------------------------------------------------------------------------------

什麽是 MakeMaker?
此模组 (亦为标准 perl 套件之一部份)设计的目的是要替一个模组从一 Makefile.PL 中自动撰写出一个 Makefile。详情请看 MakeMaker 。
回复 支持 反对

使用道具 举报

321

主题

773

帖子

4768

金币

大家网大学四年级

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

积分
3139
 楼主| 发表于 2008-12-25 16:52 | 显示全部楼层
我该如何使用正规表示式才不至於写出不合语法且难以维护的程式码?
以下提供叁个技巧使得你的正规表示式易懂又好维护。

在正规表示式外围作注解。
用 Perl的注解方式描述你所作的事以及你如何作到它。
#把每行变成「第一个字、冒号,和剩馀的字元数」这样的格式。
s/^(\w+)(.*)/ lc($1) . ":" . length($2) /ge;

在正规表示式内部作注解。
/x修饰子会要直译器忽略正规表示式内的任意空白 (在特定字元类别 [character class]中例外),同时也让你在式子中使用平常的注解方法。你应该能想像得到, 加上一些空白与注解帮助会有多大。
/x让你把下面这行:

s{'"]*|"gs;

变成:

s{ '"] * #有零个以上、不是 >;、 ',或 "的字元
| #或者是
".*?" #一段双引号圈起来的区域 (吝啬式对应)
| #或者是
'.*?' #一段单引号圈起来的区域 (吝啬式对应)
) + #以上区域出现一次或多次
>; #箭头括弧区结束
}{}gsx; #用空字串来替换;也就是杀掉

虽然它看来还是不够简明易懂,但至少大大有助於解释这个模式 (pattern)的意义。

换个不同的区隔字元 (delimiter)。
尽管我们平常都把正规表示式的模式 (patterns)想作是以 /字元来区隔,但实际上用几乎任何字元来作都行。perlre文件中有说明。例如,上面的 s///便是用大括号来当区隔字元的。选择另一个区隔字元可以免除在模式中得避开 (quote)区隔字元的困扰。例如:
s/\/usr\/local/\/usr\/share/g; #选错区隔字元的後果【译注:
#常被戏称为「搭牙签」症候群 ;-)】

s#/usr/local#/usr/share#g; #这样不是好多了?!


--------------------------------------------------------------------------------

我无法对应到超过一行的内容,哪里出了问题?
若不是你的字串里少了换行字元,就是你在模式里用了错误的修饰子。

有很多方法将多行的资料结合成一个字串。如果你希望在读入输入资料时自动得到 这项功能,你得重新设定 $/变数 (若为段落,设成 '';若要将整个档案读进一字 串,设成 undef ),以容许你一次能读入一行以上的输入。

请参考 prelre,其中有选择 /s或 /m (或二者都用)的说明: /s让万用字元 (``.'')能对应到换行字元【译注:通常换行字元不在 ``.'' 的对应范围内】, /m则让 ``^''和 ``$''两个符号能够对应到任何换行字元的前後,而不只是像平常 那样只能对应到字串头尾。你所需要确定的是你的确有个多行的字串。

例如说,以下这个程式会侦测出同一段落里重覆的字,即使它们之间有换行符号相隔 (但是不能隔段)。在这个例子里,我们不需要用到 /s,因为我们并未在任何要跨行对应的正规表示式中使用 ``.''。我们亦无需使用 /m,因为我们不想让 ``^''或 ``$''去对应 到字串中每个换行字元前後的位置。但重点是,我们得把 $/ 设成与内定值相异的值,否则我们实际上是无法读入一个多行的资料的。

$/ = ''; #读入一整段,而非仅是一行。
while ( ) {
while ( /\b(\w\S+)(\s+\1)+\b/gi ) {
print "在段落 $.找到重复的字 $1\n";
}
}

以下的程式能找出开头为 ``From ''的句子 (许多邮件处理程式都会用到这个功能):

$/ = ''; #读入一整段,而非仅是一行。
while ( ) {
while ( /^From /gm ) { # /m使得 ^也会对应到 \n之後
print "开头为 From的段落 $.\n";
}
}

以下的程式会抓出在一个段落里所有夹在 START与 END之间的东西。

undef $/; #把整个档案读进来,而非只是一行或一段
while ( ) {
while ( /START(
}


--------------------------------------------------------------------------------

我如何取出位於不同行的两个模式间之内容?
你可以使用看起来有点怪的 Perl ..运算元 (在 perlop文件中有说明):

perl -ne 'print if /START/ .. /END/' file1 file2 ...

如果你要的是整段文字而非各单行,你该使用:

perl -0777 -pe 'print "$1\n" while /START(.*?)END/gs' file1 file2 ...

但是当 START和 END之间的东西作巢状(内含)式分布 (nested occurrences)的时候 ,你便得面对本篇中所提到的对称式文字对应的问题。


--------------------------------------------------------------------------------

我把一个正规表示式放入 $/但却没有用。错在哪里?
$/必须是个字串,不能是一个正规表示式。Perl得留一手,让 Awk还有点可骄傲 之处。 :-)

事实上,如果你不介意把整个档案读入记忆体的话,不妨试试看这个:

undef $/;
@records = split /your_pattern/, ;

Net::Telnet模组 (CPAN里有)具有一项功能,可监视着输入流 (input stream)、等待特定的模式出现,或是在规定时间到了还没等到时,送出逾时 (timeout)讯息。

##开一个有叁行的档案
open FH, ">;file";
print FH "The first line\nThe second line\nThe third line\n";
close FH;

##取得一个可读/写的档案处理把手
$fh = new FileHandle "+ $fh);

##等到第二行出现了,就把第叁行印出来。
$file->;waitfor('/second line\n/');
print $file->;getline;


--------------------------------------------------------------------------------

如何在 LHS端【译注:式子中运算元左端部份】作不区别大小写式的替换,但在 RHS端【右端】保留大小写区别?
答案端看你如何定义「保留大小写区别」(preserving case)。下面这个程式依照每个 字母的顺序,在替换动作完成後保留原来的大小写。如果用来替换的字其字母数比被替 换者多,那麽最後一个字母的大小写就会被用作决定替换字剩馀字母的大小写之依据。

#原作者为 Nathan Torkington,经 Jeffrey Friedl调整
#
sub preserve_case($$)
{
my ($old, $new) = @_;
my ($state) = 0; # 0 = no change; 1 = lc; 2 = uc
my ($i, $oldlen, $newlen, $c) = (0, length($old), length($new));
my ($len) = $oldlen $oldlen) {
if ($state == 1) {
substr($new, $oldlen) = lc(substr($new, $oldlen));
} elsif ($state == 2) {
substr($new, $oldlen) = uc(substr($new, $oldlen));
}
}
return $new;
}

$a = "this is a TEsT case";
$a =~ s/(test)/preserve_case($1, "success")/gie;
print "$a\n";

这会印出:

this is a SUcCESS case


--------------------------------------------------------------------------------

如何使 \w对应到附重音记号 (accented)的字元?
请参考 perllocale说明文件。


--------------------------------------------------------------------------------

如何作一个适合不同 locale【译注:国家、地区在文字编码上各自的惯例】的 /[a-zA-Z]/对应?
一个字母可以用 /[^\W\d_]/表示,不论你的 locale为何。非字母则可用 /[\W\d_]/表示 (假定你不把 ``_''当成字元)。


--------------------------------------------------------------------------------

在一个正规表示式里如何引入 (quote)变数?
Perl解析器於间隔字元不是单引号时,会展开正规表示式里的 $variable及 @variable变数。同时也要记得,一个 s///替换式右侧部份是当成双引号括起来处理的 (详情请参看 perlop说明文件)。更别忘记,任何一个正规表示式里的特殊字元都会先被解译、处理, 除非你在替换模式前加 \Q。以下即为一例。

$string = "to die?";
$lhs = "die?";
$rhs = "sleep no more";

$string =~ s/\Q$lhs/$rhs/;
# $string现在成了 "to sleep no more"

少了 \Q,则这个正规表示式同时也会错误地对应到 ``di''。【译注:因为 /die?/ 这个式子表示 ``di''後头的 ``e''可有零个或一个】


--------------------------------------------------------------------------------

/o到底是干麽用的?
当你在一个正规表示式里用一个变数来作对应时,每次通过它时都要重新评估一次(re- evaluation),有时甚至要重新编译 (recompilation)。/o会在第一次用到那个变数 时把它锁定。在一个无变数的正规表示式里面,此情形永远为真,而且事实上,当你整 个程式在被编译成内部(位元)码的同时,你所用的模式亦然。

除非在模式里有变数转译的情况发生,否则使用 /o是无关痛痒的。在模式中有变数并且又有 /o修饰子的情况下,正规表示式引擎则既不会知道也不会去管这个模式在 第一次评估之後其中变数是否又有所改变。

/o常被用来额外提高执行效率。当重覆评估无关紧要 (因为事先知道该变数的 值不会改变);或是在有些罕见的情况下,故意不让正规表示式引擎察觉到变数值已改变 时,便可透过此一手段,避免持续评估,来达到提高效率的目的。

下面以一个 ``paragrep'' (「段落grep」)程式作范例:

$/ = ''; #使用段落模式
$pat = shift;
while () {
print if /$pat/o;
}


--------------------------------------------------------------------------------

如何使用正规表示式将档案中 C语言样式的注解删掉?
虽然这件事实际上做得到,但却比你想像中更加困难。例如下面的单行小程式 (one-liner):

perl -0777 -pe 's{/\*gs' foo.c

只能在大部分(但非全部)的情况下成功。你知道,这程式对某些种类的 C程式显得太 简陋、单纯了,尤其是那些被双引号括起来、看似注解的字串。针对它们,你需要像 这个 Jeffrey Friedl所写的这样的程式:

$/ = undef;
$_ = ;
s#/\*[^*]*\*+([^/*][^*]*\*+)*/|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|\n+|.[^/"'\\]*)#$2#g;
print;

当然,这程式可以用 /x加上空白与注解使它更容易让人看懂。


--------------------------------------------------------------------------------

我能用 Perl的正规表示式去对应成对的符号吗?
虽然 Perl的正规表示式比「数学的」正规表示式要来得强大,因为它们有追溯前段 (\1之类)这样方便的功能,但它们仍然不够强大。你依然得用非正规表示式 的技术去解析这类文字,譬如像两端用小括号或大括号包含起来的文字。

你可以在 http://www,或 (和 )。

CPAN中的 C::Scan模组包含一个这样的副常式供内部使用,但无说明文件。


--------------------------------------------------------------------------------

有人说正规表示式很贪婪,那是什麽意思?该如何避免它所带来的问题?
大部分的人所说的贪婪是指正规表示式会尽可能地对应到最多的东西。技术上来说,真正贪婪 的是量化子 (?, *, +,{})而非整个模式;Perl较喜欢作区域性的贪婪以得 到立即的快感,而不是对整个式子的贪婪。如欲使用同样的量化子作非贪婪式对应的话 【译注:即所谓的吝啬 (stingy)式对应】,用 (??, *?, +?, {}?)。例如:

$s1 = $s2 = "I am very very cold";
$s1 =~ s/ve.*y //; #贪婪式;结果为 I am cold
$s2 =~ s/ve.*?y //; #吝啬式;结果为 I am very cold

注意到在第二个替换中一碰到 ``y''就停止整个对应了吗? *?量化子有效率地告诉正 规表示式引擎,一但对应到一个模式,就马上把控制权移交下去,这行为就好比你手上有 个烫手山芋时所会采取的行动一样。


--------------------------------------------------------------------------------

如何处理每一行的每个字?
用 split函数:

while () {
foreach $word ( split ) {
#在此作你想对 $word作的动作
}
}

请注意这里所谓的字和英文中对字的定义不同;它们可能只是一段连续的、非空白的 字元罢了。

若欲处理的是一连串纯字母的话,可以考虑用:

while () {
foreach $word (m/(\w+)/g) {
#在此作你想对 $word作的动作
}
}


--------------------------------------------------------------------------------

我如何印出文字出现频率或行出现频率的纲要?
要作到这点,你得解读、分析输入字元流内的每个字。在此我们假设所谓的「字」 局限於一串由字母、连字号,或撇号所组成的字,而非前一问题中提到的一串 非空白字元集合那种概念:

while () {
while ( /(\b[^\W_\d][\w'-]+\b)/g ) { # "`sheep'"会漏失掉
$seen{$1}++;
}
}
while ( ($word, $count) = each %seen ) {
print "$count $word\n";
}

如果你要算行数,则用不着使用正规表示式:

while () {
$seen{$_}++;
}
while ( ($line, $count) = each %seen ) {
print "$count $line";
}

如果你希望这些输出经过排列,请参看有关 Hashes的那部分。


--------------------------------------------------------------------------------

如何能作近似对应?
参考 CPAN里的 String::Approx模组。


--------------------------------------------------------------------------------

我如何有效率地一次对应多个正规表示式?
下面是个超没效率的例子:

while () {
foreach $pat (@patterns) {
if ( /$pat/ ) {
# do something
}
}
}

要避免以上的方法,要不你就选用 CPAN 中几个实验性的正规表示式扩充模组其中一个 (对你的目的来说可能效率还是不够好),或是自己写个像下面这样的东西 (自 Jeffrey Friedl书中的一个函式所得到的灵感):

sub _bm_build {
my $condition = shift;
my @regexp = @_; #这里不可用 local(),得用 my()
my $expr = join $condition =>; map { "m/\$regexp[$_]/o" } (0..$#regexp);
my $match_func = eval "sub { $expr }";
die if $@; # $@【错误变数】里面有东西;这不该出现!
return $match_func;
}

sub bm_and { _bm_build('&&', @_) }
sub bm_or { _bm_build('||', @_) }

$f1 = bm_and qw{
xterm
(?i)window
};

$f2 = bm_or qw{
\b[Ff]ree\b
\bBSD\B
(?i)sys(tem)?\s*[V5]\b
};

# 我 /etc/termcap
while ( ) {
print "1: $_" if &$f1;
print "2: $_" if &$f2;
}


--------------------------------------------------------------------------------

为何我用 \b作字界搜寻时会失败呢?
有两个常见的错误观念是将 \b做为 \s+的同义词,还有把它当成界定空白及非空白字元间的边界。两者都不对。\b是介於一个 \w字元和 \W 字元之间的部分(亦即 \b是一个「字」的边缘)。它是一个长度为 0的标的物,就像 ^、$,以及所有其它的标示字元 (anchors)一样,在对应时并不消耗、占掉任何字元。perlre使用说明中对各正规表示式超字元 (metacharacters)的特性和使用都有做解释。

以下是错误使用 \b的例子,并附上修正:

"two words" =~ /(\w+)\b(\w+)/; #错误!
"two words" =~ /(\w+)\s+(\w+)/; #正确

" =matchless= text" =~ /\b=(\w+)=\b/; #错误!
" =matchless= text" =~ /=(\w+)=/; #正确

虽然它们也许不能作到你以为它们能作的事,但 \b及 \B仍然相当有用。要看看正确使用 \b的范例,请参考「如何於多行文字中抓出重复字」一问题内所附之范例。

\Bis\B这个模式是使用 \B的一个例子。它只会对应到出现在一个字内部的 ``is'',例如 ``thistle'',但不会对应到 ``this''或 ``island''。


--------------------------------------------------------------------------------

为什麽每当我用 $&, $`,或 $'时程式的速度就慢下来了呢?
因为不管在程式中哪一个角落,一旦 Perl看见你需要这类的变数时,它就得 在每次模式对应时准备好提供这些变数的值。$1, $2 等等的使用也是以同样的方式处理的。所以每当你的模式中含有捕捉用的小括号 (capturing parentheses)时, 你就得付出同样的代价。但若你从不在你的程式中用到 $&等这些东西,那麽 没有捕捉用小括号的正规表示式就不用付出额外的速度作代价。所以,请尽可能避免使用 $&, $'及 $`,但若真的无法避免 (有些演算法的确需要它们),就尽量用糸 吧,反正你已经付出代价了。


--------------------------------------------------------------------------------

正规表示式中的 \G能给我什麽好处?
\G在一个对应式或替换式中要和 /g修饰子合起来用 (若无 /g它就会被忽眷 。它是用来标示上一个成功的模式对应完成後所停在的位置,亦即 pos()点。

例如说,你有一行信件文字是按标准的 mail及 Usenet记法 (就是以 >; 字元作开始)作成引言的,而你现在要把每个开头的 >;都换成 :。那麽你可以用下面的方法来作:

s/^(>;+)/':' x length($1)/gem;

或者使用 \G,更简单也更快:

s/\G>;/:/g;

更复杂的方法可能要用到记号赋予器 (tokenizer)。下面看来像 lex语法分析器程式 码的例子是 Jeffrey Friedl提供的。它在 5.003 版因为其版本中的臭虫而无法执行,但在 5.004或以上的版本的确可行。(请注意到 /c的使用,它的存在是为了防止 /g在对应失败时将搜寻位置归零到字串的开始。)

while () {
chomp;
PARSER: {
m/ \G( \d+\b )/gcx && do { print "number: $1\n"; redo; };
m/ \G( \w+ )/gcx && do { print "word: $1\n"; redo; };
m/ \G( \s+ )/gcx && do { print "space: $1\n"; redo; };
m/ \G( [^\w\d]+ )/gcx && do { print "other: $1\n"; redo; };
}
}

当然,上面这个本来也可以写成像

while () {
chomp;
PARSER: {
if ( /\G( \d+\b )/gcx {
print "number: $1\n";
redo PARSER;
}
if ( /\G( \w+ )/gcx {
print "word: $1\n";
redo PARSER;
}
if ( /\G( \s+ )/gcx {
print "space: $1\n";
redo PARSER;
}
if ( /\G( [^\w\d]+ )/gcx {
print "other: $1\n";
redo PARSER;
}
}
}

但是这麽作就不能让那些正规表示式的式子上下对齐一目了然了。


--------------------------------------------------------------------------------

Perl正规表示引擎是 DFAs或 NFAs?它们是 POSIX相容的吗?
尽管 Perl的正规表示式看似 egrep(1)程式的 DFAs (deterministic finite automata,决定式有限自动机)特性,但事实上为了具备「退回原路」(backtracking) 与「追溯前段」( backreferencing)的功能,它们实作时是用 NFAs (non-deterministic finite automata,非决定式有限自动机)的。并且它们亦非 POSIX式的,因为那样会造成在所有情况下都有最差的表现。(似乎有些人较注重确 保一致性,即使那同时也确保了缓慢的速度)。你可以在 Jeffrey Friedl所着的 ``精通正规表示式'' (Mastering Regular Expressions)一书中 (O'Reilly出版) ,获得所有你想知道关於这些事的所有细节(在 perlfa里面有该书的详细资料)。


--------------------------------------------------------------------------------

在无递回的场合下用 grep或 map有什麽不对?
严格地说来,没有什麽不对。不过就格式的角度看来,这样会造成不易维护的程式码。 因为你是使用了他们的副作用 (side-effects)而非使用他们的传回值,不幸的是, 副作用容易让人搞混。无递回式的 grep()在写法上不如 for (嗯,技术上说是 foreach啦)回圈。


--------------------------------------------------------------------------------

如何对应多位元组字母所构成的字串?
这很难,并且还没有好的方法。Perl 并不直接支援多位元组字母。它假装一个位元组和 一个字母是同义的。下面提供的方法来自 Jeffrey Friedl,他有一篇登在 Perl期刊 (The Perl Journal)第五期的文章讨论的正是这个问题。

假设有一种怪异的火星语编码协定,其中每两个大写的 ASCII字母代表一个火星 字母 (譬如 ``CV''这两个位元组代表一个火星字母,就像 ``SG''、``VS''、``XX'',等双字元组一样)。至於其它位元则和在 ASCII 里一样表示单一字元。

所以,像 ``I am CVSGXX!''这样的火星字串用了 12个位元去表示九个字母 'I',' ' ,'a','m',' ','CV','SG','XX','!'。

现在假设你要搜索这个字母:/GX/。Perl并不懂火星语,所以它会找到 ``I am CVSGXX!''中 ``GX'' 这两个位元,即使事实上这个字母并不在其中:它之所以看来像是在那儿是因为 ``SG''和 ``XX''紧临在一起罢了,实际上并非真有 ``GX''存在。这是个大问题。

以下有些处理的方法,虽然都得付出不少代价:

$martian =~ s/([A-Z][A-Z])/ $1 /g; #让相邻的「火星」位元不再相邻
print "找到 GX了!\n" if $martian =~ /GX/;

或像这样:

@chars = $martian =~ m/([A-Z][A-Z]|[^A-Z])/g;
#上面那行在理念上近似於: @chars = $text =~ m/(.)/g;
#
foreach $char (@chars) {
print "找到 GX了!\n", last if $char eq 'GX';
}

这样也可以:

while ($martian =~ m/\G([A-Z][A-Z]|

不然乾脆这样:

die "对不起,Perl尚未支援火星文 )-:\n";

除此之外,CPAN里面有个范例程式能将半宽 (half-width)的片假名转成全宽 (full-width) [以 Shift-JIS或 EUC编码的],这是拜 Tom之赐才有的成果。

现在已有很多双 (和多)位元编码法被广泛的采用。这些方法中有些是采用 1-,2-, 3-,及 4位元组字母,混合使用。
回复 支持 反对

使用道具 举报

321

主题

773

帖子

4768

金币

大家网大学四年级

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

积分
3139
 楼主| 发表于 2008-12-25 16:52 | 显示全部楼层
我能拿到 Perl的 BNF/yacc/RE吗?
不行,引用 Chaim Frenkel的话:“Perl的语法无法被简化到可以用 BNF 表示。解析Perl的工作是分散於 yacc、lexer、烟雾和镜子之间。”


--------------------------------------------------------------------------------

$@%*这些符号是什麽意思?我怎麽知道何时该使用他们呢?
他们都是指定形态 (type)用的符号,如同 perldata里所详述的:

$纯量值 (scalar) (数字,字串或参考值 [reference])
@阵列
%杂凑阵列 (关连阵列)
*代表同一个变数名的所有类形。在第四版中它们常用来达到指标
(pointers)的功能,但现在在新版的 perl中这个角色已被参
考值 (reference)取代了。

虽然这些符号在某些场合下可省略,但建议你随处都用。

有些其他的符号你可能会碰到但却不是指定形态用的有:

这是用来从一个档案把手 (filehandle)里输入一份记录
\取某样东西的参考值 (reference)

注意 不是用来指定档案的形态,亦非此把手的名字。它只是 将这个运算子用在 FILE这个把手上。在纯量的情境 (scalar context) 下,它自 FILE 把手一次读入一行 (嗯,该说一笔记录,参看 $/),在序列情境 (list context)下,则一次将 全部的内容读 入。当对档案使用开、关或其它 之外的动作、或甚至只是提到把 手时,切记不要使用 。下面的用法是正确的:eof(FH) , seek(FH, 0,2) 以及 ``copying from STDIN to FILE''。


--------------------------------------------------------------------------------

字串加引号或使用分号及逗号是否绝对必要/还是完全没必要?
通常一个没有冠上形态符号的字 (bareword)是不需被纳入引号里的,但在大多数 的情况下或许该这麽做 (在use strict下则是必须的)。但由一个简单的字(不 能是一个已定义的副函数之名称)所构成的索引值,和 =>;左端的运算子,都会被视为已纳入引号了:

这些是和这些一样的
------------ ---------------
$foo{line} $foo{"line"}
bar =>; stuff "bar" =>; stuff

一个区块末端的分号可有可无,一个序列的最後一个逗号亦同。良好的写作风格 (参看perlstyle)中建议除了在单行程式 (one-liners)的情况外都将他们加上去:

if ($whoops) { exit 1 }
@nums = (1, 2, 3);

if ($whoops) {
exit 1;
}
@lines = (
"There Beren came from mountains cold",
"And lost he wandered under leaves",
);


--------------------------------------------------------------------------------

我如何跳过一些传回值?
一种方法是将传回值当作序列来对待,然後用索引来指名其中的某个位置:

$dir = (getpwnam($user))[7];

另一种方法就是在等号左端用 undef 作元素:

($dev, $ino, undef, undef, $uid, $gid) = stat($file);


--------------------------------------------------------------------------------

我如何暂时滤掉警告讯息?
$^W变数 (在 perlvar中有说明)控制一个区块在执行期 (runtime)的警告讯息:

{
local $^W = 0; #暂时关掉警告讯息
$a = $b + $c; #我知道这些变数可能未定义
}

注意,像所有的标点符号变数一样,目前不能对 $^W用 my,只能用 local()。

一个发展中的新 use warnings编译器指挥模组 (pragma) 提供了更精细的控制。好奇宝宝们应该翻翻 perl5-porters 邮件论坛的档案库以获得更详细的说明。


--------------------------------------------------------------------------------

什麽是一个扩充 (extension)?
一种从 Perl呼叫编译好的 C程式码的方法。阅读 perlxstut是个多了解扩充(extensions)的好方法。


--------------------------------------------------------------------------------

为何 Perl运算子的优先顺序和 C的不一样?
事实上它们是相同的。所有 Perl自 C借过来的运算子都具备与原来在 C 中相同的优先顺序。问题出在那些 C没有的运算子,特别是那些将其右方一律当成序列情境对待的函数,例如 print, chmod, exec等等。这类的函数被称作“序列运算子”(list operators),在 perlop的优先顺序表中就是这麽称呼。

一个常犯的错误像是:

unlink $file || die "snafu";

这会被解译器看成是:

unlink ($file || die "snafu");

要避免此问题,须加上括号或是用超低优先的 or运算子:

(unlink $file) || die "snafu";
unlink $file or die "snafu";

这些“英文的”运算子 (and, or, xor,及 not)是刻意设计成较一般序列运算子低的优先顺序,这就是为了解决前述的状况。

另一个拥有出人意料的优先顺序者为指数。它甚至高於负号,这使得 -2**2变成负四而非正四。他同时也会“向右靠”(right-associate),意思是说 2**3**2 代表二的九次方,而不是八的平方。


--------------------------------------------------------------------------------

我如何宣告/生成一个资料结构 (structure)?
一般来说,我们不 ``宣告''一个结构。用一个 (通常是匿名的) 杂凑阵列的参考值 (hash reference)即可。参看 perlref 以及 perldsc,里面有更多资料。以下是一个范例:

$person = {}; #新的不具名杂凑阵列
$person->;{AGE} = 24; #把 AGE栏的值设成 24
$person->;{NAME} = "Nat"; #将 NAME栏设成 "Nat"

如果你要的是更严谨的写法,看看 perltoot 。


--------------------------------------------------------------------------------

我如何创造出一个模组 (module)?
一个模组就是一个放在同名档案里的包裹(package)。例如,Hello::There模组会 放在Hello/There.pm。 perlmod 里有详尽说明。Exporter 也会很有帮助。如果你正在写一个 C 或是混合了 C及 Perl 的模组,那麽你就该读 perlxstut 。

下面是个方便的样板,你也许希望在撰写第一个模组时将他派上用场。记得要改名 字。

package Some::Module; #假设是 Some/Module.pm

use strict;

BEGIN {
use Exporter ();
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);

##设定版本以备版本检查之用;去掉 "#"号即可使用。
## $VERSION = 1.00;

#如果有使用 RCS/CVS,那应该考虑将下一行保留,
#但是小心两位数版本编号可能造成的影响。
$VERSION = do{my@r=q$Revision: 1.1 $=~/\d+/g;sprintf '%d.'.'%02d'x$#r,@r};

@ISA = qw(Exporter);
@EXPORT = qw(&func1 &func2 &func3);
%EXPORT_TAGS = ( ); #例如: TAG =>; [ qw!name1 name2! ],

#整个包裹要输出的全域变数(exported package globals)在此,
#还有其他选择要输出的函数。
@EXPORT_OK = qw($Var1 %Hashit);
}
use vars @EXPORT_OK;

#没有输出的全域变数在此。
use vars qw( @more $stuff );

#起始包裹内的全域变数,首先是要输出的那几个。
$Var1 = '';
%Hashit = ();

#接下来是其他的 (还是能以 $Some::Module::stuff的方式撷取他们的值)
$stuff = '';
@more = ();

#所有以档案为范围的变数名都
#必须在让後面的函数使用前先创造出来。

#以档案为范围的变数名在此。
my $priv_var = '';
my %secret_hash = ();

#下面是一个以档案为限的函数,当作一个闭包
#可以用 &$priv_func的方式呼叫它;但不能使用原型定义
my $priv_func = sub {
#程式码放在这里
};

#不论是否要输出,都记得要将你的函数造出来。
#别忘了在 {}括号间放些有趣的内容。

sub func1 {} #没有定义原型
sub func2() {} #定原型为 void
sub func3($$) {} #定原型为两个纯量值

#这个函数虽然未被输出,但是可以被呼叫
sub func4(\%) {} #定原型为一个杂凑阵列的参考值

END { } #模组清洁大队在此 (global destructor)

1; #模组必须传回真值


--------------------------------------------------------------------------------

我如何创造一个类别 (class)?
perltoot 里面有对於类别和物件的介绍, perlobj 和 perlbot 也有。


--------------------------------------------------------------------------------

我如何辨别一个变数是否被污染了(tainted)?
参考 Laundering and Detecting Tainted Data。以下是个范例 (里面没有用到任何系统呼叫,因为 kill() 没有将任何程序交给讯号):

sub is_tainted {
return ! eval { join('',@_), kill 0; 1; };
}

然而,此方法会触发 -w参数的警告讯息。目前并无任何不会触发 -w的方法可下侦测变数污染 -- 就把警告讯息当成是在提醒你,该把所有可能被污染的资料给 ``漂白'' (untaint)。

【译注:这里所提的 ``被污染'' (tainted),指的是当使用 -T这个参数时,或当 perl程式做 setuid或 setgid 模式执行时 (在 UNIX 下), perl会自动将有安 全顾虑的变数列为受污染, 也就是 tainted。除非先做解除污染 (untaint) 处理,否则 perl不会容许受污染的变数做出可能危害系统的举动。因此 CGI程式应尽可能地使用 -T这个参数以策安全。】


--------------------------------------------------------------------------------

闭包 (closure)是啥?
关於闭包的说明,请看 perlref 。

闭包 (closure)是个精确但又很难解释的电脑名词。在 Perl 里面,闭包是以 匿名函数的形式来实现,具有持续参照位於该函数范围之外的文字式变数值的能力。 这些外部的文字变数会神奇地保留它们在闭包函数最初定义时的值 (深连结)。

如果一个程式语言容许函数递回另一个函数的话 (像 Perl 就是),闭包便具有意 义。要注意的是,有些语言虽提供匿名函数的功能,但却无法正确处理闭包; Python 这个语言便是一例。如果要想多了解闭包的话,建议你去找本功能性程式 设计的教科书来看。Scheme这个语言不仅支援闭包,更鼓励多加使用。

以下是个典型的产生函数的函数:

sub add_function_generator {
return sub { shift + shift };
}

$add_sub = add_function_generator();
$sum = &$add_sub(4,5); # $sum现在是 9了

闭包用起来就像是个 函数样板,其中保留了一些可以在稍後再填入的空格。 add_function_generator() 所递回的匿名函数在技术上来讲并不能算是一个闭包, 因为它没有用到任何位在这个函数范围之外的文字变数。

把上面这个例子和下面这个 make_adder()函数对照一下,下面这个函数所递回的匿名函数中使用了一个外部的文字变数。这种指名外部函数的作法需要由 Perl递回一个适当的闭包,因此那个文字变数在匿名函数产生之时的值便永久地被锁进闭 包里。

sub make_adder {
my $addpiece = shift;
return sub { shift + $addpiece };
}

$f1 = make_adder(20);
$f2 = make_adder(555);

这样一来 &$f1($n) 永远会是 20加上你传进去的值 $n ,而 &$f2($n) 将 永远会是 555加上你传进去的值 $n。$addpiece的值会在闭包中保留下来。

闭包在比较实际的场合中也常用得到,譬如当你想把一些程式码传入一个函数时:

my $line;
timeout( 30, sub { $line = } );

如果要执行的程式码当初是以字串的形式传入的话,即 '$line = ' ,那麽 timeout() 这个假想的函数在回到该函数被呼叫时所在的范围後便无法再撷取 $list这个文字变数的值了。


--------------------------------------------------------------------------------

何谓变数自杀而我又该如何防止它?
变数自杀指的是 (暂时或是永久)地失去一个变数的值。造成这个现象的原因是做范围界定的 my() 和 local()和闭包或 foreach()回圈变数及函数参数相互影响 所致。

从前【在旧版 perl的时代】大家写程式的时候很容易因为这样而不小心把变数值 给弄丢。但现在 perl提供了一些保护措施,因此犯这种错的机率要小多了。

my $f = "foo";
sub T {
while ($i++ 10, that =>; 20 } );

func( \&some_func );
func( sub { $_[0] ** $_[1] } );

传递档案把手
要创造出可以传递给函数使用的档案把手,你可以用 *FH或 \*FH (这叫 ``typeglobs'' --请参看 perldata ),或是使用旧名 FileHandle的 IO::File模组以动态方式来产生档案把手亦可,这两个模组都附在标准 Perl 版本内。
use Fcntl;
use IO::File;
my $fh = new IO::File $filename, O_WRONLY|O_APPEND;
or die "Can't append to $filename: $!";
func($fh);

传递正规表示式
想要将正规表现式传来传去,你需要的将是使用 CPAN 里一个实验性的正规表现式模组( Nick Ing-Simmons的 Regexp 或 Ilya Zakharevich的Devel::Regexp),来传递字串,并且使用一个能捕捉例外情况的 eval叙述,或者你自己可以发明其他非常非常聪明的方式来做。以下就是一个如何以字串当作正规表现式,传入一个做比较的函数的例子:
sub compare($$) {
my ($val1, $regexp) = @_;
my $retval = eval { $val =~ /$regexp/ };
die if $@;
return $retval;
}

$match = compare("old McDonald", q/d.*D/);

确定绝对不要用以下的写法:

return eval "\$val =~ /$regexp/"; #错误

不然某人可以靠双引号括起来的字串以及 eval 双重解译的本质而偷偷插入 shell指令来作坏事。例如:

$pattern_of_evil = 'danger $ { system("rm -rf * &") } danger';

eval "\$string =~ /$pattern_of_evil/";

想学非常非常聪明的方法的读者可以参考 O'Reilly 出的 Mastering Regular Expressions这本书,作者是 Jeffrey Friedl。其中第 273页的 Build_MatchMany_Function()特别的有趣。在 perlfa中可以找到有关本书 的资料。

如何传递方法 (methods)
你可以用下面的方法传递一个物件方法给一个函式:
call_a_lot(10, $some_obj, "methname")
sub call_a_lot {
my ($count, $widget, $trick) = @_;
for (my $i = 0; $i $trick();
}
}

不然你就用个闭包 (closure) 把物件和它的方法以及其参数都包在一起:

my $whatnot = sub { $some_obj->;obfuscate(@args) };
func($whatnot);
sub func {
my $code = shift;
&$code();
}

你也可以研究 UNIVERSAL 类别中的 can()方法 (附於标准 Perl 版本中)。


--------------------------------------------------------------------------------

我如何生成一个静态变数?
就像与 Perl相关的其他事情一样,``条条大路通罗马'' (TMTOWTDI)。对其他语言来说叫做 ``静态变数 '' (static variable)的东西,在 Perl里面可能是一个函数私有的变数(只有该函数自己看得到,且在不同的呼叫间保持定值),或是一个档案私有(file-private)变数(只有同一个档案中的函数才看得到)。

以下就是实作函数私有变数的程式:

BEGIN {
my $counter = 42;
sub prev_counter { return --$counter }
sub next_counter { return $counter++ }
}

prev_counter() 和 next_counter() 将会共用一个於编译时起始的私有变数 $counter。

要宣告一个档案私有(file-private)变数,你仍然得使用 my(),将它放在档案开头处最外围。假设现在是在 Pax.pm 这个档案里:

package Pax;
my $started = scalar(localtime(time()));

sub begun { return $started }

当用 use Pax或 require Pax载入此模组时,这个变数就会被起始。不过它不会被资源回收,像其他出了有效范围的变数那样,因为 begun()函数要用到它,但是没有其他函数能撷取它。这个变数不能以 $Pax::started 的形式来撷取,因为它所存在的范围与此包裹无关。它存在的范围是这个档案。可想见地,一个档案里可以放好几个包裹,而所有的包裹都撷取同一个私有变数,但从另一个档案中,即使是属於同一个包裹 (package),也不能取得它的值。


--------------------------------------------------------------------------------

动态与文字式(静态)范围界定 (scoping)有何不同? Local()和 my()呢?
local($x) 将全域变数 $x的原值存起来,并在此函数执行期间赋予一个新 值,此值可以从此函数所呼叫的其他函数里看见。这整个步骤是在执行期间完成的,所以才叫做动态范围选取 (dynamic scoping)。local()影响的是全域变数,或者称作包裹变数或动态变数。

my($x)会创造一个只能在目前这个函数里看得见的新变数。这个步骤是在编译 期完成(compile-time),所以称作文字式或是静态范围选取。my()总是作用在私 有变数,也称作文字式变数或(不当地)称作静态(范围选取)变数。

例如:

sub visible {
print "var has value $var\n";
}

sub dynamic {
local $var = 'local'; #授予 $var这个全域变数
visible(); #一个暂时的新值
}

sub lexical {
my $var = 'private'; #新的私有变数,$var
visible(); # (无法从此函数外看到)
}

$var = 'global';

visible(); #会印出 global
dynamic(); #会印出 local
lexical(); #会印出 global

你可以发现在整个过程中 ``private''这个值都印不出来。那是因为 $var的值只存在於lexical() 函数的区块里面,对它所呼叫的函数来说是看不到的。

总结来说,local()不会产生你想像中的私有、区域变数。它只是将一个暂时的值 授予一个全域变数。如果你要的是私有的变数,那麽 my() 才是你要找的。

参看 perlsub ,里面有更详尽的解说。


--------------------------------------------------------------------------------

当同一个范围中有一个文字式变数时,我该如何去撷取同名的动态变数?
你可以透过符号式参考值 (symbolic references),把 use strict "refs"设定取掉。然後使用 $ {'var'} ,而非 $var。

local $var = "global";
my $var = "lexical";

print "lexical is $var\n";

no strict 'refs';
print "global is $ {'var'}\n";

如果你知道你所在的是哪一个包裹(package)的话,你可以直接指名,就像写 $Some_Pack::var这样。注意 $::var这个写法 并非表示目前此包裹 (package) 内的动态变数 $var,而是指在 main包裹(package) 里的那个,就等价於 $main::var。直接指定包裹(package)的名称虽然需要你把名字敲进程式码 中,但是它执行起来比较快,也避免和 use strict "refs" 起冲突。


--------------------------------------------------------------------------------

所谓深连结与浅连结 (deep and shallow binding)间有何不同呢?
在深连结中,匿名函数中所用到的文字式变数值是以该函数产生时所在的范围为准。在浅连结中,这些变数值是以函数被呼叫时所在的范围为准,如果在这个范围中恰巧有同名的变数,便使用这些当地变数的值。Perl总是使用文字式变数(就是以 my()创造的)式的深连结。然而,动态变数(也称作全域 (global),区域(local),或包裹(package)变数)在功效上是浅连结。就把这当作是少用它们的另一个理由好 了。请参考 闭包 (closure)是啥? 一节。


--------------------------------------------------------------------------------

为何 "local($foo) = ;"无法正确地作用?
local()会把 =号右边以序列情境来对待。而 这个阅读的 动作,就像 Perl里许多的函数以及运算子一样,会自动分辨出自己被呼叫时所在的情境并且采取适当的作法。一般来说,scalar()函数可以帮点忙。这个函数实际上对资料本身不会有任何作用(与一般所认为的相反),但是会告诉它所作用的函数要以对待纯量值的方法来运算。如果那个函数没有预先定义好碰到纯量情境的行为,那麽它当然也帮不了你(例如 sort() 函数)。

然而,在以上这个例子 (local...)中,只要省略括号便可强制使用纯量情境:

local($foo) = ; #错误
local($foo) = scalar(); #可以
local $foo = ; #正确

其实在这个例子中,或许你该改用文字式变数 (lexical variables),不过会碰到 的问题跟上面一样:

my($foo) = ; #错误
my $foo = ; #正确


--------------------------------------------------------------------------------

我如何重新定义一个内建函数、运算子或是方法?
为什麽要这麽做? :-)

如果你要覆盖掉某个内建函数,例如说 open(),那你得将其定义从另一个模组载 入。参考 Overriding Builtin Functions。在 Class/Template里面也有个范例。

如果你要覆盖掉一个 Perl运算子,像是 +或 ** ,那你该使用 use overload这个编译器指挥模组(pragma),其文件在 overload 。

如果你要覆盖父类别 (parent class)里的方法呼叫 (method calls),请看 Overridden Methods 。


--------------------------------------------------------------------------------

用 &foo和 foo()的方式来呼叫一个函数有什麽不同?
当你用 &foo的方式呼叫一个函数时,你等於让这个函数撷取你目前 @_里面的值,同时也跳过原型定义 (prototypes)不用。这表式此函数抓到的是你当时的 @_, 而非一个空的 @_!虽然严格讲起来它也不能算是个 bug (但是在 perlsub里面是这麽说的)但在大部份情况下,这也算不上是个特别功能。

当你用 &foo()的方式呼叫你的函数时,你会得到一个新的 @_,但是原型定义 仍然会被避开不用。

在一般情况下,你该用 foo()的方式去呼叫函数。只有在编译器已事先知道这 个函数的定义时,括号才能省略,譬如当这个函数所在的模组或包裹被 use (但如果是被 require则不行)时,或是透过先前提及或 use subs宣告等 方法,让编译器先接触到这个函数的定义。用这种呼叫方式,即使是当括号省掉时, 你都会得到一个乾净的 @_,不会有任何不该出现的旧值残留在上面。


--------------------------------------------------------------------------------

我如何作一个 switch或 case叙述?
这个问题在 perlsyn 文件里有更详尽的解释。简单来说,因为 Perl本身已提供了多种不同的条件测试方法可供使用 (数值比较、字串比较、 glob比较、正规表示式 对应、覆盖比较,及其它),所以并没有正式的 case叙述语法。虽然自 perl1起这就一直是许多人期盼的一个项目,但因 Larry无法决定怎样才是呈现这功能的最好方法,因此还是将它略掉。

下面这个简单的 switch范例以模式对应为基础。我们将要做的是对储存在 $whatchamacallit里面的参考值 (reference)的类型进行多重条件的判断。【译注:$whatchamacallit 函意为 $what_you_might_call_it】

SWITCH:
for (ref $whatchamacallit) {

/^$/ && die '不是个参考值';

/SCALAR/ && do {
print_scalar($$ref);
last SWITCH;
};

/ARRAY/ && do {
print_array(@$ref);
last SWITCH;
};

/HASH/ && do {
print_hash(%$ref);
last SWITCH;
};

/CODE/ && do {
warn '无法印出函数的 ref';
last SWITCH;
};

# DEFAULT

warn '跳过使用者自定的类型';

}


--------------------------------------------------------------------------------

我如何抓到呼叫未定义变数/函数/方法的事件?
在 Autoloading 和 AUTOLOAD: Proxy Methods里 提到的AUTOLOAD 方法让你能捕捉对於未定义函数与方法的呼叫。

如果是要处理一些在 -w之下触发警告讯息的未定义变数,你可以使用一个处理元 (handler)来捕捉 __WARN__这个虚拟信号 (pseudo-signal),范例如下:

$SIG{__WARN__} = sub {

for ( $_[0] ) {

/Use of uninitialized value/ && do {
#将警讯提升为致命行动
die $_;
};

#其它要捕捉的状况可以写在此。

warn $_;
}

};


--------------------------------------------------------------------------------

为什麽我的程式会找不到放在同一档案中的方法 (method)呢?
一些可能的原因:你用的继承给搞混了、你拼错了该方法的名字,或是物件的类别 错误。这些事在 perltoot里都有更详尽的说明。同时你也可以用 print ref($object) 来找出 $object这个物件是被归到哪个类别底下。

另一个可能的原因是你在 Perl还不知道这个包裹 (package)存在之前便将某个 类别名称在间接式物件语法中使用 (例如 find Guru "Samy")。最好是在开始使用你的包裹前,先确定都已经先把它们定义好了,如果你用的是 use 而非 require的话,这件事便会自动处理好。不然的话,确定你使用箭头式语法 (例如,Guru-find(``Samy'')>;)。在perlobj 里面对於物件的记号有详尽解释。


--------------------------------------------------------------------------------

我如何找出目前所在的 package为何?
如果只是一个随意的程式的话,你可以用下面的方法找出目前正被编译的包裹为何:

my $packname = ref bless [];

但如果是一个方法的话,而且印出的错误讯息中要包含呼叫此方法的物件 (不见得 就是把这个方法编译进去的那个物件)则:

sub amethod {
my $self = shift;
my $class = ref($self) || $self;
warn "我是被 $class这个物件所召唤";
}


--------------------------------------------------------------------------------

我如何将一大块 perl程式码变成注解?
用内嵌 POD格式的方法把程式码变注解:

#这是程式

=for nobody
这段就变成了注解

#程式继续下去

=begin comment text

接下来此处所有

的文字都会被
所有人忽略

=end comment text

=cut
回复 支持 反对

使用道具 举报

321

主题

773

帖子

4768

金币

大家网大学四年级

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

积分
3139
 楼主| 发表于 2008-12-25 16:53 | 显示全部楼层
如何得知使用者正在哪个作业系统下执行我的 perl 程式?
$^O 这个变数(若使用 English 模组就是 $OSTYPE)会指出你的 perl 解译器执 行档是替哪个作业系统、平台所建的。


--------------------------------------------------------------------------------

为什麽 exec() 不会传值回来?
因为这正是它所做的:它用另一个不同的程式来取代你当时所执行的。如果你的程 式需要继续跑下去(这可能正是你问此问题的原因吧?),改用 system() 。


--------------------------------------------------------------------------------

如何对 键盘/萤幕/滑鼠 做些花样?
连接/控制 键盘、萤幕和指标装置(「滑鼠」)的方法因作业系统的不同而有不 同;不妨试试下列模组:

键盘
Term::Cap perl 标准内建模组
Term::ReadKey CPAN
Term::ReadLine::Gnu CPAN
Term::ReadLine::Perl CPAN
Term::Screen CPAN

萤幕
Term::Cap perl 标准内建模组
Curses CPAN
Term::ANSIColor CPAN

滑鼠
Tk CPAN


--------------------------------------------------------------------------------

如何向使用者询问密码?
(这个问题跟全球资讯网一点关系也没有。如果你要找的是跟 WWW 有关的,那就 看另一份常见问题集吧。)

【译注:中文版的 Perl CGI 程式设计常见问题集可以在下列网址中找到: http://www.math.ncu.edu.tw/~chenym/FAQ/Perl/perl-cgi-faq/
http://2tigers.net/perl/perl-cgi-faq-chi/

在 crypt 里面有个范例。首先,将你的终端机设为「无回应」[no echo] 模式,然後就用平常的方法将密码读入。你可以用老式的 ioctl() 函数、 POSIX 终端机控制函数(参看 POSIX ,和 Camel 书第七章),或是呼叫 stty 程式,这些方法的可携性/移植性程度都不一样。

你也可以在大部份系统上使用 CPAN 里的 Term::ReadKey 模组,这个模组较易使 用而且理论上也较据可携性/移植性。


--------------------------------------------------------------------------------

如何对序列埠做读写动作?
这端看你在什麽作业系统上执行你的程式。以 Unix 来说,序列埠可以透过 /dev 目录下的档案来撷取; 而在其他系统上,设备的名称无疑地会不一样。以下是一些 在设备互动时可能遭遇的共同问题:

锁档 (lockfiles)
你的系统可能会使用锁档来控制多重读写的情况。确定你用的是正确的协定。因为 当多个程序同时对一个装置做读取时可能会发生意想不到的情况。
开档模式
如果你打算对一个装置同时做读与写的动作,你得将它开到更新的模式( 在 open 里有更详细的解说)。如果你不希望冒着阻挡其他程序读取 这个装置的风险,那就得用 sysopen() 和 Fcntl 模组(标准 perl 的一部分)内 的 O_RDWR|O_NDELAY|O_NOCTTY。在 sysopen 里有对此方法更 详尽的解说。
档案尾
有些装置会等着在每行结尾处看到一个 ``\r'',而非 ``\n''。在某些平台上的 perl, ``\r''和 ``\n'' 与它们平常(在 Unix 上)所指的 ASCII 值 ``\015'' 和 ``\012'' 有 所不同。你也许得直接给定数值,例如用八进位 (`` \015'')、十六进位 (``0x0D''), 或指定控制字元 (``\cM'')。
print DEV "atv1\012"; # 对某些装置来说是错误的
print DEV "atv1\015"; # 对某些装置来说是对的

尽管对普通的文字档案,一个 ``\n'' 便可解决断行的问题,但目前在不同作业系统 间(Unix、 DOS/Win 和 Macintosh),对於断行记号仍无统一标准,而只有用 ``\015\012'' 来当成 每行的结尾,然後再视需要去掉输出中不想要的部份。这 个做法尤其常用於 socket输出/输入 与自动洗清 (autoflushing),也是接下来 要讨论的主题。

洗清输出
如果你希望 print() 的时候每个字元都要送到你指定的装置去,那你应自动清洗 你的档案把手,旧方法是:
use FileHandle;
DEV->;autoflush(1);

比较新的方法是:

use IO::Handle;
DEV->;autoflush(1);

你可以用 select() 和 $| 变数来控制自动清洗的动作(参考 $| 和select ):

$oldh = select(DEV);
$| = 1;
select($oldh);

你也可能看到不使用额外的暂存变数的写法,例如:

select((select(DEV), $| = 1)[0]);

如同前一个项目所说的,这方法对 Unix 和 Macintosh 间的 socket 输出/入 没 用。在这种情况下,你得把你的行末字元写死在程式码内。

不挡式输入 (non-blocking input)
如果你正在做一个具阻挡性的 read() 或 sysread() 动作,则你需要安排一个闹 铃把手或提供一个逾时设定(参看 alarm)。如果你是用非阻挡式的 开档,那麽就要配合非阻挡性的读取,也就是说得用到4 个参数的 select() 来确 定此装置的 输出/入 是否已准备好了(参考 select )。

--------------------------------------------------------------------------------

如何逆解加密後的密码档案?
花大把大把的钱去买破解专用的硬体,这会让你成为焦点话题。

说正经的,如果是碰到 Unix 密码档的话就不行 - Unix 密码系统用的是单向的加 密函数。像 Crack 之类的程式可以暴力地(并聪明地)试着猜出密码,但无法 (也不能)保证速战速决。

如果你耽心的是使用者选取不良的密码,你应该在使用者换密码时主动审核(例如 说修改 passwd(1) 程式加入这个功能)。


--------------------------------------------------------------------------------

如何启动一个背景执行的程序?
你可以使用:

system("cmd &")

或是用 fork,像 fork 里写的(在 perlipc 里有更进一步的 范例)。如果你在 Unix 类的系统上的话,请注意以下几件事情:

STDIN, STDOUT 和 STDERR 是共享的
主程序和背景程序(即「子」程序)共用同一个 STDIN、STDOUT 和 STDERR 档案 把手。如果两个程序想同时去读、写同一个档案把手,就可能有怪事会发生。你也 许应该替子程序关闭或重新开启这些把手。你可以用开启一个管道 (pipe) 的方法 避免这些问题(参看 open)但是在某些系统上这样做会强迫子程序 必须比父程序早死。
讯号
SIGCHLD、可能还有 SIGPIPE 这两个讯号要抓到。当背景程序执行完成後就会送出 SIGCHLD 讯号。而当你写入一个子程序已经关闭的档案把手时就会收到 SIGPIPE 讯号(一个未抓住的 SIGPIPE 可能导致你的程式无声无息地死去)。用 system("cmd&") 的话不会有这样的问题。
僵 程序
你得做准备,在子程序结束时「收成」它:
$SIG{CHLD} = sub { wait };

在 Signals 有范例程式教你怎麽做。用 system("prog &") 的 话不会有僵 程序的问题。


--------------------------------------------------------------------------------

如何捕捉 控制字元/讯号?
你并不能真的 ``捕捉'' 一个控制字元。而是控制字元产生一个讯号让你捕捉。关於 讯号的资料可以在 Signals 以及 Camel 书第六章里找到。

要小心的是,大多 C 程式库无法重新进入 [re-entrant]。因此当你要尝试着在一 个处理器里做 print() 动作,而这个处理器是由另一个stdio 的动作所叫出来的 话,你的内部结构可能会处於失调状态,而程式可能会丢出记忆核心 (dump core)。 有的时候你可以用 syswrite() 取代 print() 以避免这个状况。

除非你极为小心,否则在一个讯号处理器中,唯一安全可做的是:设定一个变数後 离开。而在第一个情况下,你在设定变数的时候应确定 malloc() 不会被叫出来 (譬如,设定一个已经有值的变数)。

例如:

$Interrupted = 0; # 确定它有个值
$SIG{INT} = sub {
$Interrupted++;
syswrite(STDERR, "哇\n", 5);
}

然而,因为系统呼叫会自己重新启动,你将会发现如果你用的是「慢的」呼叫,像 、read()、connect() 或 wait(),那麽将它们停下的唯一办法是使 用 「跳远」的方式跳出来;也就是产生一个例外讯号。参看在 Signals 里对阻挡性 flock() 的逾时处理器的说明,或骆驼书第六 章。


--------------------------------------------------------------------------------

如何更动 Unix 系统上隐式密码档 (shadow password) 的内容?
如果你的 perl 安装正确的话,在 perlfunc 里描述的 getpw*() 函数应该就 能够读取隐式密码档了(只有读取权)。要更动该档案内容,做一个新的密码档 (这个档案的格式因系统而异,请看 passwd(5) )然後用 pwd_mkdb(8)(参考 pwd_mkdb(5))来安装新的密码档。


--------------------------------------------------------------------------------

如何设定时间和日期?
假设你有足够的权限,你应该可以用 date(1) 程式来设定系统的时间与日期。 (但没有针对个别程序修改时间日期的方法)这机制在 Unix、MS-DOS、Windows 和 NT 下都能用;VMS 下则要用 set time 。

然而,如果你只是要更动你的时区,只消设定一个环境变数即可:

$ENV{TZ} = "MST7MDT"; # unix 下
$ENV{'SYS$TIMEZONE_DIFFERENTIAL'}="-5" # vms
system "trn comp.lang.perl";


--------------------------------------------------------------------------------

如何能够针对小於一秒的时间做 sleep() 或 alarm() 的动作呢?
如果你要比 sleep() 所提供的最小单位一秒更精细的话,最简单的方法就是用 select 里面写的 select() 函数。如果你的系统有 itimers 并支 援syscall(),你可以试试下面这个老范例 http://www.perl.com/CPAN/doc/misc /ancient/tutorial/eg/itimers.pl .


--------------------------------------------------------------------------------

如何测量小於一秒的时间?
一般来说,你可能做不到。 Time::HiRes 模组(CPAN 有)在某些系统上能达到此 功能。

总之,你可能做不到。但是如果你的 Perl 支援 syscall() 函数并支援类似 gettimeofday(2) 的系统呼叫,你也许可以这麽做:

require 'sys/syscall.ph';

$TIMEVAL_T = "LL";

$done = $start = pack($TIMEVAL_T, ());

syscall( &SYS_gettimeofday, $start, 0)) != -1
or die "gettimeofday: $!";

##########################
# 在这做你要做的事 #
##########################

syscall( &SYS_gettimeofday, $done, 0) != -1
or die "gettimeofday: $!";

@start = unpack($TIMEVAL_T, $start);
@done = unpack($TIMEVAL_T, $done);

# fix microseconds
for ($done[1], $start[1]) { $_ /= 1_000_000 }

$delta_time = sprintf "%.4f", ($done[0] + $done[1] )
-
($start[0] + $start[1] );


--------------------------------------------------------------------------------

如何做 atexit() 或 setjmp()/longjmp() 的动作?(例外处理)
第五版的 Perl 增加了 END 区块,可以用来模拟 atexit()的效果。当程式或执行 绪(thread) 终了时就会去呼叫该包装的 END 区块(参考 perlmod 文件)。但 是如果当程式被没有抓到的讯号终结了,END 区块就不会被呼叫到,所以当你用 END 时应再加上

use sigtrap qw(die normal-signals);

Perl 的例外处理机制就是它的 eval() 运算子。你可以把 eval() 当做 setjmp 而die()当做 longjmp 来使用。更详细的说明请参考 Signals 和 Camel书第六章里关於讯号的那段,尤其是描述有关 flock() 的逾时处理器那段。

如果你只对例外处理的部分有兴趣,试试 exceptions.pl 程式库(包含在标准 perl里)。

如果你要的是 atexit() 语法(以及 rmexit()),试试 CPAN 里的 AtExit 模组。


--------------------------------------------------------------------------------

为何我的 sockets 程式在 System V (Solaris) 系统下不能用?「不支援本协定」这个错误讯息又是什麽意思?
有些 Sys-V 根底的系统,特别像 Solaris 2.X,已重新将一些标准的 socket常数 定义过了。由於这些常数在各种架构下都是定值,所以在 perl程式码中常被人写 死在里面。处理此问题的适当方式 是用 ``use Socket'' 来取得正确的值。

须注意尽管 SunOS 和 Solaris 在二进位执行档上相容,这些值是相异的。自己去 想为什麽吧。


--------------------------------------------------------------------------------

如何从 Perl 里呼叫系统中独特的 C 函数?
通常是写个外部的模组来处理 - 参看「我要如何学到将 C 与 Perl 连结在一起? [h2xs, xsubpp]」 这问题的答案。然而,如果此函数是个系统呼叫,而你的系统 有支援 syscall(),那麽可以用 syscall 函数(说明在 perlfunc 里)。

切记先查查看你的 perl 版本中所附的模组以及 CPAN 里的模组,因为也许某人已 经写了个这样的模组。


--------------------------------------------------------------------------------

在哪里可以找引入档来做 ioctl() 或 syscall()?
以前这些档案会由标准 perl 发行中所附的 h2ph 工具来产生。这个程式将 C 标 头档案里的 cpp(1)指令转换成内含副程式定义的档案,像 &SYS_getitimer,你可 以把它当做函数的参数。这样做并不怎麽完美,但通常可达成任务。简单的像 errno.h 、 syscall.h 和socket.h 这些档案都没问题,但像 ioctl.h 这种较难的档案总是需要人工编辑。以下是安装 *.ph 档案的步骤:

1. 进入最高使用者帐户
2. cd /usr/include
3. h2ph *.h */*.h

如果你的系统支援动态载入,那麽为了可携性、而且合理的做法是使用 h2xs(也 是 perl的标准配备)。这个工具将 C 标头档案转换成 Perl 的衍伸档案 (extensions)。 h2xs 的入门要看 perlxstut 。

如果你的系统不支援动态载入,你可能仍应使用 h2xs。参看 perlxstut 和 MakeMaker (简单来说,就是用 make perl 、而非 make来重 建一份使用新的静态连结的 perl)。


--------------------------------------------------------------------------------

为何 setuid perl 程式会抱怨关於系统核心的问题?
有些作业系统的核心有臭虫使得 setuid 程式在先天上就不安全。Perl提供你一些 方法(在 perlsec 里有写)可跳过这些系统的缺陷。


--------------------------------------------------------------------------------

如何打开对某程式既输入又输出的管道 (pipe)?
IPC::Open2 模组(perl 的标准配件)是个好用的方法,它在内部是藉着pipe()、 fork() 和 exec() 来完成此工作。不过切记要读它文件里关於锁死的警告 ( Open2 )。


--------------------------------------------------------------------------------

为何用 system() 却得不到一个指令的输出呢?
你把 system() 和反向引号 (``) 的用法搞混了。 system() 会执行一个指令然後 传回指令结束时的状况资讯(以一个 16 进位值表示:低位元是程序中止所收到的 讯号,高位元才是真正离开时的传回值)。反向引号 (``) 执行一个指令并且把它 所送出的东西送到 STDOUT。

$exit_status = system("mail-users");
$output_string = `ls`;


--------------------------------------------------------------------------------

如何补捉外部指令的 STDERR?
有叁种基本方式执行外部指令:

system $cmd; # 使用 system()
$output = `$cmd`; # 使用 反向引号 (``)
open (PIPE, "cmd |"); # 使用 open()

在 system() 下,STDOUT 和 STDERR 都会输出到和 script 本身的 STDOUT, STDERR相同的出处,除非指令本身将它们导向它处。反向引号和 open() 则 只 读取指令的 STDOUT 部份。

在上述方法中,你可以在呼叫前更改档案描述元 (file descriptor) 名称:

open(STDOUT, ">;logfile");
system("ls");

或者使用 Bourne shell 的档案描述元重导功能:

$output = `$cmd 2>;some_file`;
open (PIPE, "cmd 2>;some_file |");

也可以用档案描述元重导功能将 STDERR 导向到 STDOUT:

$output = `$cmd 2>;&1`;
open (PIPE, "cmd 2>;&1 |");

注意你 不能 光是将 STDERR 开成 STDOUT 的复制,而不呼叫 shell来做这个 重导的工作。这样是不行的:

open(STDERR, ">;&STDOUT");
$alloutput = `cmd args`; # stderr 仍然会跑掉

失败的原因是,open() 让 STDERR 在呼叫 open() 时往 STDOUT的方向走。然後反 向引号让 STDOUT的内容跑到一个字串变数里,但是没有改变 STDERR 的去向(它 仍然往旧的 STDOUT那里跑)。

注意,在反向引号里你 必须 使用 Bourne shell (sh(1)) 重导的语法而非 csh(1)的!至於为何 Perl 的 system()、反向引号和开管道都用 Bourne shell语 法的原因,可在下址找到: http://www.perl.com/CPAN/doc/FMTEYEWTK/versus/csh.whynot

你也可以使用 IPC::Open3 模组(perl 标准配备),但注意它的参数顺序和 IPC::Open2不一样(参看 Open3 )。


--------------------------------------------------------------------------------

为何当管道开启失败时 open() 不会传回错误讯息?
其实会,只是或许并非以你期望的方式。在遵循标准 fork()/exec() 机制的系统 上(例如,Unix),运作原理是这样的:open() 导致一个 fork()。在父程序里, open()传回子程序的ID。然後子程序 exec() 从管道传来/出 的指令。父程序无 法得知 exec() 的动作成功与否-它能传回的只有 fork() 动作成功与否的消息。 要找出这指令是否顺利执行,你得补捉 SIGCHLD 讯号并 wait() 以得到子程序离开 时的状态。如果你要写资料到子程序,则 SIGPIPE也该一并捕捉-否则在你写入之 前可能无法察觉 exec() 动作已失败了。这些在 perlipc 文件里都有说明。

在使用 spawn() 机制的系统里,open() 也许 能达到你所期望的-除非 perl 使用一个 shell 来起始你的指令。在这情况下以上对 fork()/exec() 的描述仍适 用。


--------------------------------------------------------------------------------

在输出值是空的情境里使用反向引号有何不对?
严格说起来,没啥不对。但从程式写作严谨与否来说,这样无法写出较易维护的程 式码,因为反向引号有一个(可能很巨大的)传回值,而你却忽略它。同时这也是 缺乏效率的方法,因为你得把每行所有的输出读进来、留一块记忆体给它们,然後 再把它们丢开。人们常常做下列这种事:

`cp file file.bak`;

然後它们就会想:「嘿,乾脆以後都用反向引号来执行程式好了。」这是馊主意, 因为反向引号的目的在补捉程式的输出;system() 函数才是用来执行程式的。

再看看下列这一行:

`cat /etc/termcap`;

你还没有指定输出,所以它会浪费记忆体(就那麽一下子)。另外你也忘了检查 $? 看看程式是否正确的执行。即使你写成

print `cat /etc/termcap`;

但在大部份情况下,这本来可以、而且也应该写成

system("cat /etc/termcap") == 0
or die "cat program failed!";

这样可快速地得到输出(一产生出来就会得到,不用等到最後),并且检查传回值。

system() 同时具有直接决定是否先做 shell 万用字元 (wildcard)处理的功能, 反向引号就不行。


--------------------------------------------------------------------------------

如何不经过 shell 处理来呼叫反向引号?
这需要些技巧。本来是写成

@ok = `grep @opts '$search_string' @filenames`;

你得改成:

my @ok = ();
if (open(GREP, "-|")) {
while () {
chomp;
push(@ok, $_);
}
close GREP;
} else {
exec 'grep', @opts, $search_string, @filenames;
}

一如 system(),当你 exec() 一个序列时不会有 shell 解译的情况发生。


--------------------------------------------------------------------------------

为何给了 EOF(Unix 上是 ^D,MS-DOS 上是 ^Z)後我的程式就不能从 STDIN 读取东西了呢?
因为某些 stdio 的 set error 和 eof 旗标需要清除。你可以用 POSIX 模组里定 义的clearerr()。这是在技术上正确的解决之道。还有一些较不保险的方法:

试着保存搜寻指标然後去找它,例如:
$where = tell(LOG);
seek(LOG, $where, 0);

如果那样行不通,试着去 seek() 档案的另一部份然後再找回来。
如果还是行不通,试着 seek() 档案另一个相异的的部份,读点东西,再回去找。
如果依然不行,放弃使用 stdio 改用 sysread。

--------------------------------------------------------------------------------

如何把 shell 程式转成 perl?
学习 Perl 然後重写。说真的,没有简单的转换方式。用 shell 做起来很笨的工 作可以用 Perl 很轻松的做到,而就是这些麻烦之处使得 shell->;perl 转换程式 非常不可能写得出来。在重新撰写程式的过程里,你会认清自己真正要做的工作为 何,也希望能够跳脱 shell 的管线资料流机制 [pipeline datastream paradigm], 这东西虽对某些事情很方便,但也常造成低效率。


--------------------------------------------------------------------------------

perl 能处理 telnet 或 ftp 这种双向互动吗?
试试 Net::FTP、TCP::Client 和 NET::Telnet 模组(CPAN 有)。 http://www.perl.com/CPAN/scripts/netstuff/telnet.emul.shar也有助於模拟 telnet 协定,但是 Net::Telnet 可能较容易使用。

如果你所要做的只是假装 telnet 但又不要起始 telnet 时的沟通程序,那麽以下 这个标准的双程序方式就可以满足你的需要了:

use IO::Socket; # 5.004 才有的新模组
$handle = IO::Socket::INET->;new('www.perl.com:80')
|| die "无法接上 www.perl.com 的 port 80: $!";
$handle->;autoflush(1);
if (fork()) { # XXX: undef 表示失败
select($handle);
print while ; # 将所有从 stdin 来的丢到 socket
} else {
print while ; # 将所有 socket 来的丢到 stdout
}
close $handle;
exit;


--------------------------------------------------------------------------------

如何在 Perl 里达到 Expect 的功能?
很久很久以前,有个叫做 chat2.pl 的程式库(perl 标准配备之一),但一直没 真正完工。现在,你的最佳选择就是从 CPAN 来的 Comm.pl 程式库。


--------------------------------------------------------------------------------

有没有可能将 perl 的指令列隐藏起来,以躲避像 "ps" 之类的程式?
首先要注意的是,如果你的目的是为了安全(例如避免人们偷看到密码),那你应 该重写你的程式,把重要的资讯从参数中剔除。光是隐藏起来不会让你的程式变得 完全安全。

如要真的把看得见的指令列改掉,你可以设定 $0 这个变数值,如同 perlvar 里写的。但这方法并非各种作业系统都适用。像 sendmail之类的背景程式 (daemons) 就将它们的状态放在那儿:

$0 = "orcus [accepting connections]";


--------------------------------------------------------------------------------

我在 perl script 里 {更动目录,更改我的使用环境}。为何这些改变在程式执行完後就消失了呢?如何让我做的修改显露出来?
Unix
严格的说起来,这是做不到的-一个 script 的执行是从启动它的 shell 生出一 个不同的程序来执行。这个程序的任何变动不会反映到它的父程序,只会反映到更 改之後它自己创造出来的子程序。有个 shell 魔术可以让你藉着在 shell 里 eval()你 script 的输出来装出这种效果,在 comp.unix.questions FAQ 里有详 细内容。
VMS
对 %ENV 的更改会持续到 Perl 离开,但是目录更动则不会。

--------------------------------------------------------------------------------

如何关闭一个程序的档案把手而不用等它完成呢?
假设你的系统支援这种功能,那就只要送个适当的讯号给此程序(参看 kill)。通常是先送一个 TERM 讯号,等一下下,然後再送个 KILL 讯号去终结它。


--------------------------------------------------------------------------------

如何 fork 出一个背景执行 (daemon) 程序?
如果你所指的是离线的程序(未与 tty 连线者),那下列的程序据说在大部份的 Unix系统都能用。非 Unix 系统的使用者应该检查 Your_OS::Process 模组看看有 没有其他的解决方案。

打开 /dev/tty 然後对它用 TIOCNOTTY ioctl。请参考 tty(4) 。
把目录换到 /
重开 STDIN、STDOUT 和 STDERR 使它们不会与旧的 tty 连接。
用下列方法把程式丢到背景:
fork && exit;


--------------------------------------------------------------------------------

如何使我的程式和 sh 及 csh 一起执行?
参看 eg/nih 这 script(perl 原始码发行的一部分)。


--------------------------------------------------------------------------------

如何得知我是否正在互动模式下执行?
问得好。有的时候 -t STDIN 和 -t STDOUT 可以提供线索,有时不行。

if (-t STDIN && -t STDOUT) {
print "Now what? ";
}

在 POSIX 系统中,你可以用以下方法测试你自己的程序群组与现在控制你终端机 的是否相同:

use POSIX qw/getpgrp tcgetpgrp/;
open(TTY, "/dev/tty") or die $!;
$tpgrp = tcgetpgrp(TTY);
$pgrp = getpgrp();
if ($tpgrp == $pgrp) {
print "前景\n";
} else {
print "背景\n";
}


--------------------------------------------------------------------------------

如何让一个缓慢的事件过时?
如同 Signals 和 Camel 书第六章里所描述的,用 alarm() 函数, 或许再配合上一个讯号处理器。你也可以改用 CPAN 里更具弹性的 Sys::AlarmCall 模组来做。


--------------------------------------------------------------------------------

如何设定 CPU 使用限制?
使用 CPAN 里的 BSD::Resource 模组。


--------------------------------------------------------------------------------

在 Unix 系统上如何避免产生僵 程序 (zombies)?
使用 Signals 里面叫 reaper 的程式码,在接到 SIGCHLD 时会呼 叫wait(),或是用 fork 里面写的双 fork 技巧。


--------------------------------------------------------------------------------

如何使用一个 SQL 资料库?
有几个连接 SQL 资料库非常好的界面。参看 http://www.perl.com/CPAN/modules/dbperl/DBD 里的 DBD::* 模组。


--------------------------------------------------------------------------------

如何让 system() 在收到 control-C 後就离开?
做不到。你需要摹仿 system() 呼叫(参看 perlipc 里的范例程式),然後设计一个讯号处理器,让它把 INT 讯号传给子程序。


--------------------------------------------------------------------------------

如何开启一个档案但不阻挡其他程序的阅读?
如果你有幸使用到支援非阻挡性读取的系统(大部份 Unix 般的系统都有支援), 你只需要用 Fcntl 模组里的 O_NDELAY 或 O_NONBLOCK 旗标,配合 sysopen():

use Fcntl;
sysopen(FH, "/tmp/somefile", O_WRONLY|O_NDELAY|O_CREAT, 0644)
or die "can't open /tmp/somefile: $!";


--------------------------------------------------------------------------------

如何安装一个 CPAN 模组?
最简单的方法就是让 CPAN 这个模组替你代劳。这个模组包含在 5.004及以後的版 本中。如要手动安装 CPAN 模组,或是任何按规矩发展的 CPAN模组,遵循以下步 骤:

把原始码解开放到一个暂存区域
perl Makefile.PL

make

make test

make install

如果你用的 perl 版本在编译时没有建入动态连结的功能,那你只消把第叁步 (make)换成 make perl 然後你就会得到一个新的 perl 执行档,里头连 有你新加入的延伸。

在 MakeMaker里面有更多关於建构模组的细节,并参考「如何保有 一份自己的 模组/程式库目录?」这个问题。


--------------------------------------------------------------------------------

如何保有一份自己的 模组/程式库 目录?
当你建构模组时,在产生 Makefiles 时使用 PREFIX 选项:

perl Makefile.PL PREFIX=/u/mydir/perl

然後在执行用到此 模组/程式库 的程式前先设好 PERL5LIB 环境变数(参考 perlrun ),或是用

use lib '/u/mydir/perl';

进一步的资料可在 Perl 的 lib 手册中找到。


--------------------------------------------------------------------------------

如何把我的程式所在位置加入 模组/程式库 搜寻路径?
use FindBin;
use lib "$FindBin:Bin";
use your_own_modules;


--------------------------------------------------------------------------------

如何在执行时添加目录到自己的引入路径中?
以下是我们建议更动引入路径的方法:

PERLLIB 环境变数
PERL5LIB 环境变数
perl -Idir 指令列参数
use lib pragma, as in
use lib "$ENV{HOME}/myown_perllib";

後者特别有用,因为它知道与机器相关的架构。lib.pm 机制模组是从 5.002 版开 始包含在 Perl 里面的。


--------------------------------------------------------------------------------

如何从终端机一次抓进一个按键?如果用 POSIX 模组时又该怎麽做?
#!/usr/bin/perl -w
use strict;
$| = 1;
for (1
exit;

BEGIN {
use POSIX qw(:termios_h);

my ($term, $oterm, $echo, $noecho, $fd_stdin);

$fd_stdin = fileno(STDIN);

$term = POSIX::Termios->;new();
$term->;getattr($fd_stdin);
$oterm = $term->;getlflag();

$echo = ECHO | ECHOK | ICANON;
$noecho = $oterm & ~$echo;

sub cbreak {
$term->;setlflag($noecho);
$term->;setcc(VTIME, 1);
$term->;setattr($fd_stdin, TCSANOW);
}

sub cooked {
$term->;setlflag($oterm);
$term->;setcc(VTIME, 0);
$term->;setattr($fd_stdin, TCSANOW);
}

sub getone {
my $key = '';
cbreak();
sysread(STDIN, $key, 1);
cooked();
return $key;
}

}
END { cooked() }
回复 支持 反对

使用道具 举报

321

主题

773

帖子

4768

金币

大家网大学四年级

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

积分
3139
 楼主| 发表于 2008-12-25 16:53 | 显示全部楼层
我的 CGI script可在指令列下执行但无法从浏览器执行。您能不能帮我修修看?
当然,但您恐怕付不起雇我们的签约金 :-)

说真的,如果您能够先证明您已读过下列这几个 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/ 处取得。最後一份(WWW FAQ)的中译版可自 http://www.acer.net/document /cwwwfaq/ 取得。


--------------------------------------------------------------------------------

如何去除文章中的 HTML标签?
最正确(尽管不是最快)的方法是使用 HTML::Parse模组(可由 CPAN取得,是所有写 Web程式者必备的 libwww-perl 套件的一部分)。

许多人尝试用简陋的正规表示式来解决这个问题,譬如说像 s///g,但这个式子在很多情况下会失败,因为要处理的字串可能会跨越断行字元,也可能含有被 quote【跳脱】的箭头号,或有 HTML comment出现;再加上一些疏忽,譬如,人们常忘了转换如 '"]*| (['"]).*?\1)*>;//gs

如果您想要更完整的解法,请看叁部曲的 striphtml 程式, http://www.perl.com/CPAN/authors ... cripts/striphtml.gz


--------------------------------------------------------------------------------

如何萃取 URLs?
一个快速但不完美的做法是

#!/usr/bin/perl -n00
# qxurl - tchrist@perl.com
print "$2\n" while m{

}gsix;

这个版本并不替相对式写法的 URLs 作调整,也不懂代换 bases【】,或如何处理 HTML comments、同时处理同一个标签里的 HREF和 NAME 属性,或接受 URL形式的参数。同时,它要比一个较「完整」、利用 LWP [libwww-perl]模组套件的解法,例如 http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/xurl.gz这个程 式,快上一百倍。


--------------------------------------------------------------------------------

如何从 user端上传资料?如何在另一台机器上开一个档案?
如果是 HTML表格的话,您可以使用 multipart/form-data的编码格式。 CGI.pm(可自 CPAN取得)中的 start_multipart_form()这个 method 就是为此设计的,它和 startform()这个 method 是两回事。


--------------------------------------------------------------------------------

如何在 HTML中做 pop-up menu(跳出式选单)?
用 和 这两个标签。 CGI.pm模组(可由 CPAN取得)对这个 widget【此指跳出式选单这个介面成分】还有许多其他的介面成分都有支援【即有制作动态标签的函式】,其中有些是以巧妙模拟的方 式达成。


--------------------------------------------------------------------------------

如何抓 HTML档案?
有一个方法是,如果您的系统上装有 lynx一类的文字模式的 HTML浏览器的话,那麽可以这麽做:

$html_code = `lynx -source $url`;
$text_data = `lynx -dump $url`;

收录在 CPAN里的 libwww-perl (LWP)模组则提供了更强的方法来做这件事。它不但可钻过 proxies,而且也不需要 lynx:

# print HTML from a URL
use LWP::Simple;
getprint "http://www.sn.no/libwww-perl/";;

# print ASCII from HTML from a URL
use LWP::Simple;
use HTML::Parse;
use HTML::FormatText;
my ($html, $ascii);
$html = get("http://www.perl.com/";);
defined $html
or die "Can't fetch HTML from http://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=%2Bcgi-bin+%2Bperl.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、Berkeley DB或任何 DBI相容的资料库驱动程式 (drivers)。 HTTPD::UserAdmin支援`Basic' 和 `Digest'这两个认证模式所用的档案。以下是 一例:

use HTTPD::UserAdmin ();
HTTPD::UserAdmin
->;new(DB =>; "/foo/.htpasswd")
->;add($username =>; $password);


--------------------------------------------------------------------------------

如何防范使用者藉由填我的 CGI表格来做坏事?
阅读 CGI security FAQ,(可在 http://www-genome.wi.mit.edu/WWW/faqs/www- security-faq.html取得),还有 Perl CGI FAQ,在 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标头资料?
如果您只需要一个「快而脏」的解法的话,您可以试试这个从再版的 ``Programming Perl''第 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}的程式码。没错,这麽 写是可以行得通,但事实上也有很多在网路上出没的这类程式根本不能用!

请不要忍不住去重新发明轮子【译者:这是英文的说法 (reinventing the wheels),也就是浪费时间做人家做过的事的意思】。请改用 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 ... n/scripts/ckaddr.gz。这个 script真的彻底地依据所有的 RFC规定来做检验(除了内嵌式 comments外),同时会排除一些您可能不会想送信去的位址(如 Bill Clinton【美国柯林顿总统】或您的 postmaster),然後它会确定位址中的主机名称可在 DNS中找得到。这个 script 跑起来不是很快,但至少有效。

不少 CGI scripts的作者使用另一个替代的方案:用一个简单的正规表示式,(如上头的那个)。如果一个位址能让这个式子对得上的话,那麽就接受这个位址。如果这个位址对不上这个式子的话,便再向使用者讯问,以确定她们填入的这个位 址正确无误。


--------------------------------------------------------------------------------

如何解 MIME/BASE64字串?
MIME-tools套件(可自 CPAN取得)不但可处理这个问题而且有许多其他的功能。有了这个套件,解 BASE64码就变得像这麽容易:

use MIME::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); #计算长度字元
print unpack("u", $len . $_); # uu解码後 print


--------------------------------------------------------------------------------

如何根据使用者帐户名称自动合成 email位址?
在支援 getpwuid【UNIX系统呼叫】、 $add('From', 'gnat@frii.com');
$header->;add('Subject', 'Testing');
$header->;add('To', 'gnat@frii.com');
#制作本文
$body = 'This is a test, ignore';
#产生 mail物件
$mail = new Mail::Internet(undef, Header =>; $header, Body =>; \[$body]);
#送出
$mail->;smtpsend or die;


--------------------------------------------------------------------------------

如何找出我的主机名/网域名/IP位址?
长久以来许多 code都很草率地直接呼叫 `hostname`这个程式来取得主机名。 虽然这麽做很方便,但也同时增加了移植到其他平台上的困难。这是一个很典型的 例子,在方便和可移植性之间作抉择,不论选哪一边,必须付出一些牺牲和代价。

Sys::Hostname这个模组(标准 perl发行的一部分)可用来取得机器的名字,然後您便可利用 gethostbyname()这个系统呼叫来找出该机的 IP位址了(假定您的 DNS 运作正常)。

use Socket;
use Sys::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
-e 'print News::NNTPClient->;;new->;list("newsgroups")'


--------------------------------------------------------------------------------

如何抓/丢 FTP档案?
LWP::Simple模组(可自 CPAN下载)可以抓,但不能丢档案。 Net::FTP模组(也可自 CPAN下载)虽比较复杂,但可用来丢、也能抓档案。


--------------------------------------------------------------------------------

如何用 Perl做 RPC?
有一个 DCE::RPC模组正在发展阶段(但尚未完成)。一旦完成後它会随着 DCE-Perl这个套件发行(可由 CPAN 下载)。至於 ONC::RPC这样的模组则还没听说有人在发展。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则



诚聘英才|移动端|Archiver|版权声明|大家论坛 ( 京ICP备06071611号,京公网安备11010802018363号 )

GMT+8, 2019-11-20 08:08 , Processed in 0.821778 second(s), 8 queries , Redis On.

Powered by Discuz!

© Comsenz Inc.

快速回复 返回顶部 返回列表