About eText Wizard

eTextWizard

目录：

一、关于eText Wizard（电子文档处理器）:

  1. 软件开发目的及软件用途
  2. 软件的主要功能
  3. 软件的安装及卸载
  4. 软件的注册
  5. 软件的下载地址

二、“国华软件工作室”的其他软件：

三、我的主页及联系方式：
一、关于eText Wizard: Top
1.软件开发目的及软件用途 Top
相信各位在上网一段时间后，硬盘里一定积攒了大量的电子文本，如：
技巧集锦、教程、技术文章、编程指南、小说、散文、传记等等，这些
精彩文件平时大量占用你的硬盘空间，一到用的时候，怎么也找不到；
读起来也很费力，一部《三国》你至少要打开一百二十个文件；而且
第二天也找不到昨天读到那里了，你说急人不急人！
   终于有一天想把他们作成帮助文件，但是，面对某个目录下的几千个
文本文件，才发现所谓的“所见即所得”的帮助文件制作软件竟然要
一个又一个地“手工重复劳作”........
  我曾经用Help&Manual手工作了一个包含两千多条Delphi技巧的帮助文件，
历时两三周终于完成，可是...手抽筋，眼发晕，食欲不振.......
  有时，真的希望有人把我想要的文本全处理好再放到网上，可是...
  面对网上成兆亿的文本难道只有手工处理吗？
  由此，萌发了我的软件开发方向：电子文档处理。
  “电子文档处理器”同时也是一个强大的HELP和CHM格式电子书批量制作工具。

“把繁重而又重复乏味的工作全部交给电脑去做吧！
    人的智慧在于告诉它应该怎么做！”
                                —xxx如是说。

2.软件的主要功能 Top

                功能
                 | 简介

                Txt->Help
                 | 文本文件转帮助文件，支持UNIX格式的TXT，自动添加标题、目录、索引。

                Txt->Html
                 | 文本文件转网页，自动处理URL&EMAIL

                Txt->Chm
                 | 文本文件转网页帮助文件，自动添加目录、索引、
                前页、后页等

                Html->Txt
                 | 网页文件转文本，转换完成比较干净彻底
                （可处理&nbsp;等），支持部分ASP文件。

                Html->Chm
                 | 网页文件转网页帮助文件，自动添加目录、索引。

                VBcode->Html
                 | VB源程序转为网页文件便于浏览。

                FileRenamer
                 | 批量文件改名，可预览结果，众多实用选项。

                Index Maker
                 | 对一个或多个目录下的所有或部分文件作索引，
                自动提取网页的<Title>作为标题，自动提取
                TXT第一行作为标题，多种输出格式。

                Dir Maker
                 | 批量目录建造。 *

                Title Editor
                 | 文本文件的标题编辑器及文件预览器。*

                Find&Replace
                 | 批量文件字符串查找替换器
                *

                Big5->GB

       注解：带*的项目尚未最终完成

3.软件的安装及卸载 Top

运行环境： MS win95;win98;win2000. win98以前的版本运行需要VB5.0
          的链接库文件MSVBVM50.DLL。

安装：

直接运行完全安装版即可（建议初次安装使用完全安装版）；
如果你以前安装过eTextWizard，直接下载新版本的主执行文件
eTextWizard.exe覆盖原来的即可。

卸载：

运行“控制面版”的“添加和删除程序”，找到“电子文档处理器”，
  选“卸载”，再依屏幕操作即可。

4.软件的注册 Top

本软件为“共享软件”，即“先试后买”的软件。

由于目前正处于测试阶段，故软件价格待定。

5.软件的下载地址:

本软件将会不定期更新版本，请留意！

http://www.ebook007.com/softdown.htm

二、“国华软件工作室”的其他软件： Top

TextVampire(网络即时贴) v1.0：Top
浏览文本或网页时，随时把你喜欢的文字很方便地存成.Txt文件。
你要做的只是选好文本，再按一下CTRL-C，这时，点击“网络即时贴”的
粘贴并生成文本文件”钮，TextVampire会自动截取所选文本的第一行（非空）
作为文件名保存起来，方便你将来收集整理、查找；同时也简化了你的击键次数。

http://www.ebook007.com/softdown.htm

三、我的主页及联系方式： Top

我的主要站点：

http://www.ebook007.com/
 (北极星书库)

http://go.163.com/~guohua99/(电子文档处理器)

镜像站点：

http://chmbook.363.net/

eMail:

fengguohua@hotmail.com

警告：Top

对重要文件请先作好备份，以免造成不必要的损失；
用户试用此软件的同时由用户个人承担试用此软件的一切风险，如用户不接受
此项要求请马上把本程序从你的硬盘删除； TXT转HELP结束后，程序会自动删除
已处理过的目录下的所有RTF文件，请在运行前
检查该目录下是否有重要的RTF文件，请作好备份。
请不要使用本软件制作非法电子出版物，否则一切后果由制作人自行承担，与本软件作者无关。
请不要修改本软件的版权信息（包括随软件发布的一切文档），否则作者将保留追究其法律责任的权利。
在不修改作者版权信息及保证软件完整性的前提下，任何个人或组织可随意发布或试用本软件的免费共享版，
但不得对本软件做任何逆向工程（指反汇编、反编译，公布注册码、散发正式版等）。
本文档中所指“程序”或“本程序”均指eTextWizard(中文名：电子文档处理器)。

注：MS,Windows Help(hcw.exe,hcrtf.exe),Html Help(hhw.exe,hhc.exe)的版权均归美国微软公司所有。

All Rights Reserved, Fengguohua of North Star, 2000-05-03

Top


Perl常见问题集

perlfaq - perl常问问题集

目录

perlfaq

  • 篇名
  • 概述
    • 如何取得这份文件？
    • 如何替这份文件尽点力？
    • 如果把写 Perl程式时所遇到的问题寄去问作者会有什麽後果？
  • 功劳
  • 作者及版权事宜
    • 非商业性之翻印及转载
    • 商业性之翻印及转载
    • 声明
  • 原文版更动记录

perlfaq1

  • 篇名
  • 概述
    • Perl是什麽？
    • 谁对 perl提供支援？由谁负责发展？它为什麽是免费的？
    • 我该用哪一个版本的 Perl？
    • perl4和 perl5各代表什麽？
    • Perl的发展已稳定了吗？
    • Perl难学吗？
    • Perl和其他的程式语言比起来如何？例如 Java, Python, REXX, Scheme,或 Tcl？
    • 我可以用 Perl来做【某种差事】吗？
    • 哪些场合下不适合用 Perl？
    • 「perl」和「Perl」有什麽不同？
    • Perl程式应算是 program还是 script？
    • JAPH是什麽？
    • 到哪儿可拿到 Larry Wall的智慧讽语 (witticisms)？
    • 我要如何取信、说服我的系统管理者／上司／属下使用第 5/5.004版的 Perl，而不去用其他的语言？
  • 作者及版权事宜

perlfaq2

  • 篇名
  • 概述
    • 哪些平台上有 Perl？要到哪里去找？
    • 要如合取得以执行档形式发行的 Perl？
    • 我的系统里没有 C编译器。要如何编译 perl？
    • 我直接将 Perl的执行档从一台机器上复制到另一台机器上，但是程式跑不起来。
    • 我抓回了原始码，试着编译 perl，但是 gdbm/dynamic loading/malloc/linking/...部分失败。要如何将它搞定？
    • Perl有哪些模组和延伸？ CPAN是什麽？ CPAN/src/...又代表什麽？
    • 是不是有一个经 ISO【国际标准局】或 ANSI【美国国家标准局】认可的 Perl版本？
    • Perl的相关资料要上哪儿找？
    • USENET上有哪些专门讨论 Perl的新闻讨论群？问题该投到哪里？
    • 如果我想投程式原始码，该投到哪个板子上？
    • Perl书籍
    • 和 Perl有关的杂志
    • 网路上的 Perl：透过 FTP和 WWW
    • 有哪些讨论 Perl的邮递论坛（mailing lists）？
    • 存有 comp.lang.perl.misc posts的档案库
    • Perl训练课程
    • 如何购买商业版本的 Perl？
    • 如果发现 bugs要向何处报告？
    • 什麽是 perl.com、 perl.org，和 Perl Institute？
    • 如何学着用 Perl来写物件导向程式？
  • 作者及版权事宜

perlfaq3

  • 篇名
  • 概述
    • 我如何作 (任何事)?
    • 如何以互动的方式使用 Perl?
    • 有 Perl shell吗?
    • 如何替我的 Perl程式除虫?
    • 如何检测 (profile)我的 perl程式?
    • 如何替我的 Perl程式作交叉参考 (cross-reference)?
    • 有 Perl专用的美化列印程式 (pretty-printer)吗?
    • 有 Perl的 ctags吗?
    • 哪里有 vi用的 Perl巨集?
    • 给 emacs用的 perl模式又要去哪抓呢?
    • 如何在 Perl里使用 curses?
    • X或 Tk如何与 Perl配合呢?
    • 如何不靠 CGI或 Tk之助作出简单的目录（选单）?
    • 我可以动态地将 C常式载入 Perl吗?
    • 什麽是 undump?
    • 如何让我的 Perl程式跑得更快些?
    • 如何让我的 Perl程式吃少一点的记忆体?
    • 把指标传回到区域资料是不安全的做法吗?
    • 我如何释放一个阵列或杂凑阵列以缩小我的程式尺寸?
    • 如何让我的 CGI脚本 (script)执行起来更有效率?
    • 如何隐藏 Perl程式的原始码?
    • 如何把我的 Perl程式码编译成 byte code或 C?
    • 如何才能让 '#!perl'在 [MS-DOS,NT,...]下作用?
    • 我能利用命令列写出有用的程式吗?
    • 为何一行的 perl程式无法在我的 DOS/Mac/VMS系统上运作?
    • 我得去哪里学 Perl的 CGI或是 Web程式设计呢?
    • 在哪可以学到用 Perl作物件导向程式设计?
    • 哪里可以学到将 C与 Perl相连结? [h2xs, xsubpp]
    • 我已经读了 perlembed, perlguts,等等，但我仍然无法将 perl嵌入我的
    • 当我试着执行我的程式时，我收到某项讯息。它代表什麽意思?
    • 什麽是 MakeMaker?
  • 作者与版权事宜

perlfaq4

  • NAME
  • DESCRIPTION
  • Data: Numbers
    • Why am I getting long decimals (eg, 19.9499999999999) instead of the numbers I should be getting (eg, 19.95)?
    • Why isn't my octal data interpreted correctly?
    • Does perl have a round function? What about ceil() and floor()?
    • How do I convert bits into ints?
    • How do I multiply matrices?
    • How do I perform an operation on a series of integers?
    • How can I output Roman numerals?
    • Why aren't my random numbers random?
  • Data: Dates
    • How do I find the week-of-the-year/day-of-the-year?
    • How can I compare two date strings?
    • How can I take a string and turn it into epoch seconds?
    • How can I find the Julian Day?
    • Does Perl have a year 2000 problem?
  • Data: Strings
    • How do I validate input?
    • How do I unescape a string?
    • How do I remove consecutive pairs of characters?
    • How do I expand function calls in a string?
    • How do I find matching/nesting anything?
    • How do I reverse a string?
    • How do I expand tabs in a string?
    • How do I reformat a paragraph?
    • How can I access/change the first N letters of a string?
    • How do I change the Nth occurrence of something?
    • How can I count the number of occurrences of a substring within a string?
    • How do I capitalize all the words on one line?
    • How can I split a [character] delimited string except when inside [character]? (Comma-separated files)
    • How do I strip blank space from the beginning/end of a string?
    • How do I extract selected columns from a string?
    • How do I find the soundex value of a string?
    • How can I expand variables in text strings?
    • What's wrong with always quoting "$vars"?
    • Why don't my <
  • Data: Arrays
    • What is the difference between $array[1] and @array[1]?
    • How can I extract just the unique elements of an array?
    • How can I tell whether an array contains a certain element?
    • How do I compute the difference of two arrays? How do I compute the intersection of two arrays?
    • How do I find the first array element for which a condition is true?
    • How do I handle linked lists?
    • How do I handle circular lists?
    • How do I shuffle an array randomly?
    • How do I process/modify each element of an array?
    • How do I select a random element from an array?
    • How do I permute N elements of a list?
    • How do I sort an array by (anything)?
    • How do I manipulate arrays of bits?
    • Why does defined() return true on empty arrays and hashes?
  • Data: Hashes (Associative Arrays)
    • How do I process an entire hash?
    • What happens if I add or remove keys from a hash while iterating over it?
    • How do I look up a hash element by value?
    • How can I know how many entries are in a hash?
    • How do I sort a hash (optionally by value instead of key)?
    • How can I always keep my hash sorted?
    • What's the difference between "delete" and "undef" with hashes?
    • Why don't my tied hashes make the defined/exists distinction?
    • How do I reset an each() operation part-way through?
    • How can I get the unique keys from two hashes?
    • How can I store a multidimensional array in a DBM file?
    • How can I make my hash remember the order I put elements into it?
    • Why does passing a subroutine an undefined element in a hash create it?
    • How can I make the Perl equivalent of a C structure/C++ class/hash or array of hashes or arrays?
    • How can I use a reference as a hash key?
  • Data: Misc
    • How do I handle binary data correctly?
    • How do I determine whether a scalar is a number/whole/integer/float?
    • How do I keep persistent data across program calls?
    • How do I print out or copy a recursive data structure?
    • How do I define methods for every class/object?
    • How do I verify a credit card checksum?
  • AUTHOR AND COPYRIGHT

perlfaq5

  • NAME
  • DESCRIPTION
    • How do I flush/unbuffer a filehandle? Why must I do this?
    • How do I change one line in a file/delete a line in a file/insert a line in the middle of a file/append to the beginning of a file?
    • How do I count the number of lines in a file?
    • How do I make a temporary file name?
    • How can I manipulate fixed-record-length files?
    • How can I make a filehandle local to a subroutine? How do I pass filehandles between subroutines? How do I make an array of filehandles?
    • How can I set up a footer format to be used with write()?
    • How can I write() into a string?
    • How can I output my numbers with commas added?
    • How can I translate tildes (~) in a filename?
    • How come when I open the file read-write it wipes it out?
    • Why do I sometimes get an "Argument list too long" when I use <*>?
    • Is there a leak/bug in glob()?
    • How can I open a file with a leading ">" or trailing blanks?
    • How can I reliably rename a file?
    • How can I lock a file?
    • What can't I just open(FH, ">file.lock")?
    • I still don't get locking. I just want to increment the number in the file. How can I do this?
    • How do I randomly update a binary file?
    • How do I get a file's timestamp in perl?
    • How do I set a file's timestamp in perl?
    • How do I print to more than one file at once?
    • How can I read in a file by paragraphs?
    • How can I read a single character from a file? From the keyboard?
    • How can I tell if there's a character waiting on a filehandle?
    • How do I open a file without blocking?
    • How do I create a file only if it doesn't exist?
    • How do I do a tail -f in perl?
    • How do I dup() a filehandle in Perl?
    • How do I close a file descriptor by number?
    • Why can't I use "C:\temp\foo" in DOS paths? What doesn't `C:\temp\foo.exe` work?
    • Why doesn't glob("*.*") get all the files?
    • Why does Perl let me delete read-only files? Why does -i clobber protected files? Isn't this a bug in Perl?
    • How do I select a random line from a file?
  • AUTHOR AND COPYRIGHT

perlfaq6

  • 篇名
  • 概述
    • 我该如何使用正规表示式才不至於写出不合语法且难以维护的程式码?
    • 我无法对应到超过一行的内容，哪里出了问题?
    • 我如何取出位於不同行的两个模式间之内容?
    • 我把一个正规表示式放入 $/但却没有用。错在哪里?
    • 如何在 LHS端【译注：式子中运算元左端部份】作不区别大小写式的替换，但在 RHS端【右端】保留大小写区别?
    • 如何使 \w对应到附重音记号 (accented)的字元?
    • 如何作一个适合不同 locale【译注：国家、地区在文字编码上各自的惯例】的 /[a-zA-Z]/对应?
    • 在一个正规表示式里如何引入 (quote)变数?
    • /o到底是干麽用的?
    • 如何使用正规表示式将档案中 C语言样式的注解删掉?
    • 我能用 Perl的正规表示式去对应成对的符号吗?
    • 有人说正规表示式很贪婪，那是什麽意思?该如何避免它所带来的问题?
    • 如何处理每一行的每个字?
    • 我如何印出文字出现频率或行出现频率的纲要?
    • 如何能作近似对应?
    • 我如何有效率地一次对应多个正规表示式?
    • 为何我用 \b作字界搜寻时会失败呢?
    • 为什麽每当我用 $&, $`,或 $'时程式的速度就慢下来了呢?
    • 正规表示式中的 \G能给我什麽好处?
    • Perl正规表示引擎是 DFAs或 NFAs?它们是 POSIX相容的吗?
    • 在无递回的场合下用 grep或 map有什麽不对?
    • 如何对应多位元组字母所构成的字串?
  • 作者与版权事宜

perlfaq7

  • 篇名
  • 概述
    • 我能拿到 Perl的 BNF/yacc/RE吗？
    • $@%*这些符号是什麽意思？我怎麽知道何时该使用他们呢？
    • 字串加引号或使用分号及逗号是否绝对必要/还是完全没必要？
    • 我如何跳过一些传回值？
    • 我如何暂时滤掉警告讯息？
    • 什麽是一个扩充 (extension)？
    • 为何 Perl运算子的优先顺序和 C的不一样？
    • 我如何宣告/生成一个资料结构 (structure)？
    • 我如何创造出一个模组 (module)？
    • 我如何创造一个类别 (class)？
    • 我如何辨别一个变数是否被污染了(tainted)？
    • 闭包 (closure)是啥？
    • 何谓变数自杀而我又该如何防止它？
    • 我如何传递/传回一个 {函数，档案把手，阵列，杂凑阵列，方法，和正规表现式}？
    • 我如何生成一个静态变数？
    • 动态与文字式(静态)范围界定 (scoping)有何不同？ Local()和 my()呢？
    • 当同一个范围中有一个文字式变数时，我该如何去撷取同名的动态变数？
    • 所谓深连结与浅连结 (deep and shallow binding)间有何不同呢？
    • 为何 "local($foo) = ;"无法正确地作用？
    • 我如何重新定义一个内建函数、运算子或是方法？
    • 用 &foo和 foo()的方式来呼叫一个函数有什麽不同？
    • 我如何作一个 switch或 case叙述？
    • 我如何抓到呼叫未定义变数/函数/方法的事件？
    • 为什麽我的程式会找不到放在同一档案中的方法 (method)呢？
    • 我如何找出目前所在的 package为何？
    • 我如何将一大块 perl程式码变成注解？
  • 作者与版权事宜

perlfaq8

  • 篇名
  • 概述
    • 如何得知使用者正在哪个作业系统下执行我的 perl程式？
    • 为什麽 exec()不会传值回来？
    • 如何对键盘／萤幕／滑鼠做些花样？
    • 如何向使用者询问密码？
    • 如何对序列埠做读写动作？
    • 如何逆解加密後的密码档案？
    • 如何启动一个背景执行的程序？
    • 如何捕捉控制字元／讯号？
    • 如何更动 Unix系统上隐式密码档 (shadow password)的内容？
    • 如何设定时间和日期？
    • 如何能够针对小於一秒的时间做 sleep()或 alarm()的动作呢？
    • 如何测量小於一秒的时间？
    • 如何做 atexit()或 setjmp()/longjmp()的动作？（例外处理）
    • 为何我的 sockets程式在 System V (Solaris)系统下不能用？「不支援本协定」这个错误讯息又是什麽意思？
    • 如何从 Perl里呼叫系统中独特的 C函数？
    • 在哪里可以找引入档来做 ioctl()或 syscall()？
    • 为何 setuid perl程式会抱怨关於系统核心的问题？
    • 如何打开对某程式既输入又输出的管道 (pipe)？
    • 为何用 system()却得不到一个指令的输出呢？
    • 如何补捉外部指令的 STDERR？
    • 为何当管道开启失败时 open()不会传回错误讯息？
    • 在输出值是空的情境里使用反向引号有何不对？
    • 如何不经过 shell处理来呼叫反向引号？
    • 为何给了 EOF（Unix上是 ^D，MS-DOS上是 ^Z）後我的程式就不能从 STDIN读取东西了呢？
    • 如何把 shell程式转成 perl？
    • perl能处理 telnet或 ftp这种双向互动吗？
    • 如何在 Perl里达到 Expect的功能？
    • 有没有可能将 perl的指令列隐藏起来，以躲避像 "ps"之类的程式？
    • 我在 perl script里 {更动目录，更改我的使用环境}。为何这些改变在程式执行完後就消失了呢？如何让我做的修改显露出来？
    • 如何关闭一个程序的档案把手而不用等它完成呢？
    • 如何 fork出一个背景执行 (daemon)程序？
    • 如何使我的程式和 sh及 csh一起执行？
    • 如何得知我是否正在互动模式下执行？
    • 如何让一个缓慢的事件过时？
    • 如何设定 CPU使用限制？
    • 在 Unix系统上如何避免产生僵 程序 (zombies)？
    • 如何使用一个 SQL资料库？
    • 如何让 system()在收到 control-C後就离开？
    • 如何开启一个档案但不阻挡其他程序的阅读？
    • 如何安装一个 CPAN模组？
    • 如何保有一份自己的模组／程式库目录？
    • 如何把我的程式所在位置加入模组／程式库搜寻路径？
    • 如何在执行时添加目录到自己的引入路径中？
  • 如何从终端机一次抓进一个按键？如果用 POSIX模组时又该怎麽做？
  • 作者、译者与版权

perlfaq9

  • 篇名
  • 概述
    • 我的 CGI script可在指令列下执行但无法从浏览器执行。您能不能帮我修修看？
    • 如何去除文章中的 HTML标签？
    • 如何萃取 URLs？
    • 如何从 user端上传资料？如何在另一台机器上开一个档案？
    • 如何在 HTML中做 pop-up menu（跳出式选单）？
    • 如何抓 HTML档案？
    • 如何解开或产生 Web上那些冠 %的码？
    • 如何【将 requests】转向到另一页去？
    • 如何替网页加上密码？
    • 要怎麽用 Perl来编辑 .htpasswd和 .htgroup这两个档案？
    • 如何防范使用者藉由填我的 CGI表格来做坏事？
    • 如何解读、萃取 email标头资料？
    • 如何解译 CGI表格？
    • 如何验证 email位址？
    • 如何解 MIME/BASE64字串？
    • 如何根据使用者帐户名称自动合成 email位址？
    • 我的程式如何送／读 email？
    • 如何找出我的主机名／网域名／IP位址？
    • 如何抓新闻讨论群的文章或群组名录？
    • 如何抓／丢 FTP档案？
    • 如何用 Perl做 RPC？
  • 作者及版权事宜


Get Update News

Update Link:
http://www.ebook007.com/softdown.htm

“电子文档处理器”会经常更新，请登陆“北极星书库”以得到最新版本。如有任何意见和建议也请给我发Email。

我的Email地址： fengguohua@hotmail.com。


Goto eTextWizard's Homepage

My Homepage:
http://www.ebook007.com/

“北极星书库”有海量的已经经过我的“电子文档处理器”处理过的Windows帮助文件或Windows HtmlHelp格式的小说、传记等作为电子书范本供大家参考。欢迎大家试用“电子文档处理器”--他不仅是一个强大的批量文本转换工具、文档制作工具，同时也是一款少见的具有强大功能的电子书批量制作工具，“北极星书库”的所有HELP和CHM格式电子书全部用“电子文档处理器”批量制作完成！！


perlfaq

perlfaq - perl常问问题集，总篇

  • 篇名
  • 概述
    • 如何取得这份文件？
    • 如何替这份文件尽点力？
    • 如果把写 Perl程式时所遇到的问题寄去问作者会有什麽後果？
  • 功劳
  • 作者及版权事宜
    • 非商业性之翻印及转载
    • 商业性之翻印及转载
    • 声明
  • 原文版更动记录

篇名

perlfaq -中译 Perl 常问问题集 (FAQ)（原文版 Date: 1997/04/24 22:46:06.中译版 $Revision: 1.9 $, $Date: 1997/06/19 03:42:55 $）

概述

本文件分为下列各章节：

perlfaq:本常问问题集结构综览。
本章。

perlfaq1:概括性的 Perl问题。
非常概括性、高层次的 Perl 问题

perlfaq2: Perl的取得和学习。
有关哪里可找到 Perl的原始码和使用说明、支援、训练课程，及相关事宜等。

perlfaq3:程式设计工具
程式设计工具及支援。

perlfaq4:资料变换和处理
处理数字、日期、字串、阵列、hashes（关连阵列），及各种资料有关的事项。

perlfaq5:档案与格式
I/O（输入／输出）及各 ``f''事项：filehandles（档案控制把手）、flushing （洗清）、 formats （报表格式化），及 footers（注脚制作）。

perlfaq6:正规表示法 (Regexps)
模式对应及正规表示法。

perlfaq7:综合性 Perl语言问题
不太适合编入其他章节的综合性 Perl语言问题。

perlfaq8:系统连动
process之间相互沟通 (IPC)，使用者介面控制（键盘、萤幕，及指向工具【如滑 鼠】等）。

perlfaq9:网路连线
网路连线、Internet，加上几个 WWW 的问题。

如何取得这份文件？

这份文件会定期投到 comp.lang.perl.announce和其他几个相关的讨论群。同时，在 CPAN的 /CPAN/doc/FAQs/FAQ/这个目录底下，或在全球网上的 http://www.perl.com/perl/faq/都可以找到这份文件，并且以多种不同的格式存 放。

（译者注：每当中译版【尚未完成】有修改或有新的篇章翻译完成时，译者会不定 期把它投到 tw.bbs.comp.lang.perl这个板子，并透过「2T新鲜情报」这个 mailing list来做通知；此外，第一手新版资料今後皆会以 HTML格式放在两只老 虎工作室： http://2Ti.com/cgi-bin/2T/perl/） 本 FAQ台湾的 mirror在： ftp://www.math.ncu.edu.tw/pub/staff/chenym/FAQ/Perl/ (anonymous FTP)和 如何替这份文件尽点力？ 您可以将您发现的错误、或内容补充，建议等寄到 perlfaq-suggestions@perl.com 。寄信到旧的 perlfaq位址是错误的做法，这只会让系统自动寄一份 FAQ给您。

（译者：请将对中文版的意见寄到 pailing@2Ti.com）

如果把写 Perl程式时所遇到的问题寄去问作者会有什麽後果？

作者大概不会理会您所提的问题，除非您是写信建议把一些新问题加进 FAQ去，但如果是这样的话这些问题当初就该寄到 perlfaq-suggestions@perl.com 处才对。

您当初如果读了这份 FAQ的第二部，您就会知道 comp.lang.perl.misc 才是寻求免费建议的适当地方。如果您的问题真的很重要而且您急着要得到正确解答，那麽您该请一个顾问。

功劳

当我在 80年代末刚着手写 Perl FAQ的时候，我从没想到它在将来会增加到超过 一百页，也没想到 Perl会变得这麽热门而广为流行。如果没有 Larry Wall 和其 他的 Perl Porters的鼎力襄助，这份文件是无法出炉的。

作者及版权事宜

Copyright (c) 1997 Tom Christiansen and Nathan Torkington.着作权所有， All rights reserved。有关使用、（转）发行事宜，详见 perlfaq 。

中译版着作权所有：萧百龄及两只老虎工作室。本中译版遵守并使用与原文版相同 的使用条款发行。

非商业性之翻印及转载

在(1)所有着作权告示保持完整、(2)不涉及收费营利，以及(3)尽可能地使用最新 版的情况下，作者允许任何人透过电子形式（电子邮件、讨论群布告，或存放）， 或印表方式对本文件作完整或部份发行。

不仅如此，您还可将这份文件附加於任何以完整原始码或执行档形式发行的 Perl、或其使用手册中，或附在一份完整复制的 CPAN档案库中，只要和以上的叁条规定不相抵触即可。

商业性之翻印及转载

如欲将本文件作其他方式发行，包括将本文件或文中的范例程式码整份或部份附加 於商业产品，举凡，但不局限於书籍，杂志文章，或光碟之中，必须事先写信至 perlfaq-legal@perl.com ，以徵得许可授权。如有任何未经本文作者授权、涉及本 文件中任何一部分之商业性使用行为，作者将采取法律行动。

声明

作者提供这份资料，目的在於希望它能对大家有帮助，但本资料在任何情况下绝不 保证绝对正确、最新，或适用於任何特定目的。作者对这份资料或其应用完全不负 任何责任。

原文版更动记录

  1. 年 4月 23日 由 Chip所作的文体和空白字元的修正，Tom 添加一个有关使用 POSIX模组从终端机一次读进一个字元的问题。

  2. /April/97 把 http://www.oasis.leo.org/perl/ 加到 perlfaq2 里。perlfaq3的文体修整。增加浮点运算精确度、【以下未翻】 fixed complex number arithmetic, cross-references, caveat for Text::Wrap, alternative answer for initial capitalizing, fixed incorrect regexp, added example of Tie::IxHash to perlfaq4. Added example of passing and storing filehandles, added commify to perlfaq5. Restored variable suicide, and added mass commenting to perlfaq7. Added Net::Telnet, fixed backticks, added reader/writer pair to telnet question, added FindBin, grouped module questions together in perlfaq8. Expanded caveats for the simple URL extractor, gave LWP example, added CGI security question, expanded on the email address answer in perlfaq9.

  3. 年 3月 25日 补充 perlfaq2中执行档发行一节的资讯。在 perlfaq6 中增加 Net::Telnet 。修改 perlfaq8中的错字。修改 perlfaq9中送信的例子。在 perlfaq2中提到 Merlyn 的专栏。

  4. 年 3月 18日 在篇名处增加日期 (DATE)，显示各部分变动情形。

    在 perlfaq8中的 forking open的回答中提到 SIGPIPE 和 perlipc 。

    修正对 perlfaq4中一条正规表示式的叙述。

  5. 年 3月 17日版 整份文件错字修正。

    在 perlfaq7 增加一个有关 Perl BNF的新问题。

    首次发行： 1997年 3月 11日
    这是第叁版 FAQ的首次发行；因此尚无任何更动。


perlfaq1

perlfaq1 - perl常问问题集，第一篇

  • 篇名
  • 概述
    • Perl是什麽？
    • 谁对 perl提供支援？由谁负责发展？它为什麽是免费的？
    • 我该用哪一个版本的 Perl？
    • perl4和 perl5各代表什麽？
    • Perl的发展已稳定了吗？
    • Perl难学吗？
    • Perl和其他的程式语言比起来如何？例如 Java, Python, REXX, Scheme,或 Tcl？
    • 我可以用 Perl来做【某种差事】吗？
    • 哪些场合下不适合用 Perl？
    • 「perl」和「Perl」有什麽不同？
    • Perl程式应算是 program还是 script？
    • JAPH是什麽？
    • 到哪儿可拿到 Larry Wall的智慧讽语 (witticisms)？
    • 我要如何取信、说服我的系统管理者／上司／属下使用第 5/5.004版的 Perl，而不去用其他的语言？
  • 作者及版权事宜

篇名

perlfaq1 - 概括性的 Perl 问题（原文版 Revision: 1.12, Date: 1997/04/24 22:43:34. 17:23:09.中译版 $Revision: 1.12 $ $Date: 1997/07/12 20:41:48 $）

概述

本篇涵盖非常概括性、高层次的 Perl 问题。

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版之间被消除，所以您至少应升级到比这几个版本高才是。

作者及版权事宜

Copyright (c) 1997 Tom Christiansen and Nathan Torkington.着作权所有， All rights reserved。有关使用、（转）发行事宜，详见 perlfaq 。

中译版着作权所有：萧百龄及两只老虎工作室。本中译版遵守并使用与原文版相同 的使用条款发行。


perlfaq2

perlfaq2 - perl常问问题集，第二篇

  • 篇名
  • 概述
    • 哪些平台上有 Perl？要到哪里去找？
    • 要如合取得以执行档形式发行的 Perl？
    • 我的系统里没有 C编译器。要如何编译 perl？
    • 我直接将 Perl的执行档从一台机器上复制到另一台机器上，但是程式跑不起来。
    • 我抓回了原始码，试着编译 perl，但是 gdbm/dynamic loading/malloc/linking/...部分失败。要如何将它搞定？
    • Perl有哪些模组和延伸？ CPAN是什麽？ CPAN/src/...又代表什麽？
    • 是不是有一个经 ISO【国际标准局】或 ANSI【美国国家标准局】认可的 Perl版本？
    • Perl的相关资料要上哪儿找？
    • USENET上有哪些专门讨论 Perl的新闻讨论群？问题该投到哪里？
    • 如果我想投程式原始码，该投到哪个板子上？
    • Perl书籍
    • 和 Perl有关的杂志
    • 网路上的 Perl：透过 FTP和 WWW
    • 有哪些讨论 Perl的邮递论坛（mailing lists）？
    • 存有 comp.lang.perl.misc posts的档案库
    • Perl训练课程
    • 如何购买商业版本的 Perl？
    • 如果发现 bugs要向何处报告？
    • 什麽是 perl.com、 perl.org，和 Perl Institute？
    • 如何学着用 Perl来写物件导向程式？
  • 作者及版权事宜

篇名

perlfaq2 - Perl的取得和学习（原文版 Revision: 1.16, Date: 1997/04/23 18:04:09. 18:15:48.中译版 $Revision: 1.13 $ $Date: 1997/07/12 20:41:48 $）

概述

本篇涵盖如何获得 Perl原始码、使用说明、支援，训练等问题及相关事宜。

哪些平台上有 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 <perl-classes@perl.com> 和 Randal Schwartz <perl-training-info@stonehenge.com >，加上二人手下的众爪牙，联手 提供各项有关 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则还提供了一些非常好的技巧和心得。

作者及版权事宜

Copyright (c) 1997 Tom Christiansen and Nathan Torkington.着作权所有， All rights reserved。有关使用、（转）发行事宜，详见 perlfaq 。

中译版着作权所有：萧百龄及两只老虎工作室。本中译版遵守并使用与原文版相同 的使用条款发行。


perlfaq3

perlfaq3 - perl常问问题集，第叁篇

  • 篇名
  • 概述
    • 我如何作 (任何事)?
    • 如何以互动的方式使用 Perl?
    • 有 Perl shell吗?
    • 如何替我的 Perl程式除虫?
    • 如何检测 (profile)我的 perl程式?
    • 如何替我的 Perl程式作交叉参考 (cross-reference)?
    • 有 Perl专用的美化列印程式 (pretty-printer)吗?
    • 有 Perl的 ctags吗?
    • 哪里有 vi用的 Perl巨集?
    • 给 emacs用的 perl模式又要去哪抓呢?
    • 如何在 Perl里使用 curses?
    • X或 Tk如何与 Perl配合呢?
    • 如何不靠 CGI或 Tk之助作出简单的目录（选单）?
    • 我可以动态地将 C常式载入 Perl吗?
    • 什麽是 undump?
    • 如何让我的 Perl程式跑得更快些?
    • 如何让我的 Perl程式吃少一点的记忆体?
    • 把指标传回到区域资料是不安全的做法吗?
    • 我如何释放一个阵列或杂凑阵列以缩小我的程式尺寸?
    • 如何让我的 CGI脚本 (script)执行起来更有效率?
    • 如何隐藏 Perl程式的原始码?
    • 如何把我的 Perl程式码编译成 byte code或 C?
    • 如何才能让 '#!perl'在 [MS-DOS,NT,...]下作用?
    • 我能利用命令列写出有用的程式吗?
    • 为何一行的 perl程式无法在我的 DOS/Mac/VMS系统上运作?
    • 我得去哪里学 Perl的 CGI或是 Web程式设计呢?
    • 在哪可以学到用 Perl作物件导向程式设计?
    • 哪里可以学到将 C与 Perl相连结? [h2xs, xsubpp]
    • 我已经读了 perlembed, perlguts,等等，但我仍然无法将 perl嵌入我的
    • 当我试着执行我的程式时，我收到某项讯息。它代表什麽意思?
    • 什麽是 MakeMaker?
  • 作者与版权事宜

篇名

perlfaq3 -程式设计工具（原文版 Revision: 1.22, Date: 1997/04/24 22:43:42. 中译版 $Revision: 1.4 $, $Date: 1997/07/12 20:03:10 $）

概述

这个部份回答了有关程式设计师的工具与程式设计方面的协助等相关问题。

我如何作 (任何事)?

你到 CPAN（见 perlfaq2）找过了吗?也许别人已经写了某个模组可以解决你的 问题。你查过相关的说明文件了吗 (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 .. 10 ) {
        push @many, makeone();
    }

    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 <Ctrl-x>"Hello world\n<Ctrl-x>""

在 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 。

作者与版权事宜

Copyright (c) 1997 Tom Christiansen and Nathan Torkington. All rights reserved.有关使用、（转）发行事宜，详见 perlfaq 。

译者：陈彦铭

中译版着作权所有：陈彦铭、萧百龄及两只老虎工作室。本中译版遵守并使用与 原文版相同的使用条款发行。


perlfaq4

perlfaq4 - perl常问问题集，第四篇

  • NAME
  • DESCRIPTION
  • Data: Numbers
    • Why am I getting long decimals (eg, 19.9499999999999) instead of the numbers I should be getting (eg, 19.95)?
    • Why isn't my octal data interpreted correctly?
    • Does perl have a round function? What about ceil() and floor()?
    • How do I convert bits into ints?
    • How do I multiply matrices?
    • How do I perform an operation on a series of integers?
    • How can I output Roman numerals?
    • Why aren't my random numbers random?
  • Data: Dates
    • How do I find the week-of-the-year/day-of-the-year?
    • How can I compare two date strings?
    • How can I take a string and turn it into epoch seconds?
    • How can I find the Julian Day?
    • Does Perl have a year 2000 problem?
  • Data: Strings
    • How do I validate input?
    • How do I unescape a string?
    • How do I remove consecutive pairs of characters?
    • How do I expand function calls in a string?
    • How do I find matching/nesting anything?
    • How do I reverse a string?
    • How do I expand tabs in a string?
    • How do I reformat a paragraph?
    • How can I access/change the first N letters of a string?
    • How do I change the Nth occurrence of something?
    • How can I count the number of occurrences of a substring within a string?
    • How do I capitalize all the words on one line?
    • How can I split a [character] delimited string except when inside [character]? (Comma-separated files)
    • How do I strip blank space from the beginning/end of a string?
    • How do I extract selected columns from a string?
    • How do I find the soundex value of a string?
    • How can I expand variables in text strings?
    • What's wrong with always quoting "$vars"?
    • Why don't my <
  • Data: Arrays
    • What is the difference between $array[1] and @array[1]?
    • How can I extract just the unique elements of an array?
    • How can I tell whether an array contains a certain element?
    • How do I compute the difference of two arrays? How do I compute the intersection of two arrays?
    • How do I find the first array element for which a condition is true?
    • How do I handle linked lists?
    • How do I handle circular lists?
    • How do I shuffle an array randomly?
    • How do I process/modify each element of an array?
    • How do I select a random element from an array?
    • How do I permute N elements of a list?
    • How do I sort an array by (anything)?
    • How do I manipulate arrays of bits?
    • Why does defined() return true on empty arrays and hashes?
  • Data: Hashes (Associative Arrays)
    • How do I process an entire hash?
    • What happens if I add or remove keys from a hash while iterating over it?
    • How do I look up a hash element by value?
    • How can I know how many entries are in a hash?
    • How do I sort a hash (optionally by value instead of key)?
    • How can I always keep my hash sorted?
    • What's the difference between "delete" and "undef" with hashes?
    • Why don't my tied hashes make the defined/exists distinction?
    • How do I reset an each() operation part-way through?
    • How can I get the unique keys from two hashes?
    • How can I store a multidimensional array in a DBM file?
    • How can I make my hash remember the order I put elements into it?
    • Why does passing a subroutine an undefined element in a hash create it?
    • How can I make the Perl equivalent of a C structure/C++ class/hash or array of hashes or arrays?
    • How can I use a reference as a hash key?
  • Data: Misc
    • How do I handle binary data correctly?
    • How do I determine whether a scalar is a number/whole/integer/float?
    • How do I keep persistent data across program calls?
    • How do I print out or copy a recursive data structure?
    • How do I define methods for every class/object?
    • How do I verify a credit card checksum?
  • AUTHOR AND COPYRIGHT

NAME

perlfaq4 - Data Manipulation ($Revision: 1.19 $, $Date: 1997/04/24 22:43:57 $)

DESCRIPTION

The section of the FAQ answers question related to the manipulation of data as numbers, dates, strings, arrays, hashes, and miscellaneous data issues.

Data: Numbers

Why am I getting long decimals (eg, 19.9499999999999) instead of the numbers I should be getting (eg, 19.95)?

Internally, your computer represents floating-point numbers in binary. Floating-point numbers read in from a file, or appearing as literals in your program, are converted from their decimal floating-point representation (eg, 19.95) to the internal binary representation.

However, 19.95 can't be precisely represented as a binary floating-point number, just like 1/3 can't be exactly represented as a decimal floating-point number. The computer's binary representation of 19.95, therefore, isn't exactly 19.95.

When a floating-point number gets printed, the binary floating-point representation is converted back to decimal. These decimal numbers are displayed in either the format you specify with printf(), or the current output format for numbers (see $# if you use print. $# has a different default value in Perl5 than it did in Perl4. Changing $# yourself is deprecated.

This affects all computer languages that represent decimal floating-point numbers in binary, not just Perl. Perl provides arbitrary-precision decimal numbers with the Math::BigFloat module (part of the standard Perl distribution), but mathematical operations are consequently slower.

To get rid of the superfluous digits, just use a format (eg, printf("%.2f", 19.95)) to get the required precision.

Why isn't my octal data interpreted correctly?

Perl only understands octal and hex numbers as such when they occur as literals in your program. If they are read in from somewhere and assigned, no automatic conversion takes place. You must explicitly use oct() or hex() if you want the values converted. oct() interprets both hex (``0x350'') numbers and octal ones (``0350'' or even without the leading ``0'', like ``377''), while hex() only converts hexadecimal ones, with or without a leading ``0x'', like ``0x255'', ``3A'', ``ff'', or ``deadbeef''.

This problem shows up most often when people try using chmod(), mkdir(), umask(), or sysopen(), which all want permissions in octal.

    chmod(644,  $file); # WRONG -- perl -w catches this
    chmod(0644, $file); # right

Does perl have a round function? What about ceil() and floor()? Trig functions?

For rounding to a certain number of digits, sprintf() or printf() is usually the easiest route.

The POSIX module (part of the standard perl distribution) implements ceil(), floor(), and a number of other mathematical and trigonometric functions.

In 5.000 to 5.003 Perls, trigonometry was done in the Math::Complex module. With 5.004, the Math::Trig module (part of the standard perl distribution) implements the trigonometric functions. Internally it uses the Math::Complex module and some functions can break out from the real axis into the complex plane, for example the inverse sine of 2.

Rounding in financial applications can have serious implications, and the rounding method used should be specified precisely. In these cases, it probably pays not to trust whichever system rounding is being used by Perl, but to instead implement the rounding function you need yourself.

How do I convert bits into ints?

To turn a string of 1s and 0s like '10110110' into a scalar containing its binary value, use the pack() function (documented in pack):

    $decimal = pack('B8', '10110110');

Here's an example of going the other way:

    $binary_string = join('', unpack('B*', "\x29"));

How do I multiply matrices?

Use the Math::Matrix or Math::MatrixReal modules (available from CPAN) or the PDL extension (also available from CPAN).

How do I perform an operation on a series of integers?

To call a function on each element in an array, and collect the results, use:

    @results = map { my_func($_) } @array;

For example:

    @triple = map { 3 * $_ } @single;

To call a function on each element of an array, but ignore the results:

    foreach $iterator (@array) {
        &my_func($iterator);
    }

To call a function on each integer in a (small) range, you can use:

    @results = map { &my_func($_) } (5 .. 25);

but you should be aware that the .. operator creates an array of all integers in the range. This can take a lot of memory for large ranges. Instead use:

    @results = ();
    for ($i=5; $i < 500_005; $i++) {
        push(@results, &my_func($i));
    }

How can I output Roman numerals?

Get the http://www.perl.com/CPAN/modules/by-module/Roman module.

Why aren't my random numbers random?

The short explanation is that you're getting pseudorandom numbers, not random ones, because that's how these things work. A longer explanation is available on http://www.perl.com/CPAN/doc/FMTEYEWTK/random, courtesy of Tom Phoenix.

You should also check out the Math::TrulyRandom module from CPAN.

Data: Dates

How do I find the week-of-the-year/day-of-the-year?

The day of the year is in the array returned by localtime() (see localtime):

    $day_of_year = (localtime(time()))[7];

or more legibly (in 5.004 or higher):

    use Time::localtime;
    $day_of_year = localtime(time())->yday;

You can find the week of the year by dividing this by 7:

    $week_of_year = int($day_of_year / 7);

Of course, this believes that weeks start at zero.

How can I compare two date strings?

Use the Date::Manip or Date::DateCalc modules from CPAN.

How can I take a string and turn it into epoch seconds?

If it's a regular enough string that it always has the same format, you can split it up and pass the parts to timelocal in the standard Time::Local module. Otherwise, you should look into one of the Date modules from CPAN.

How can I find the Julian Day?

Neither Date::Manip nor Date::DateCalc deal with Julian days. Instead, there is an example of Julian date calculation in http://www.perl.com/CPAN/authors/David_Muir_Sharnoff/modules/Time/JulianDay.pm.gz, which should help.

Does Perl have a year 2000 problem?

Not unless you use Perl to create one. The date and time functions supplied with perl (gmtime and localtime) supply adequate information to determine the year well beyond 2000 (2038 is when trouble strikes). The year returned by these functions when used in an array context is the year minus 1900. For years between 1910 and 1999 this happens to be a 2-digit decimal number. To avoid the year 2000 problem simply do not treat the year as a 2-digit number. It isn't.

When gmtime() and localtime() are used in a scalar context they return a timestamp string that contains a fully-expanded year. For example, $timestamp = gmtime(1005613200) sets $timestamp to ``Tue Nov 13 01:00:00 2001''. There's no year 2000 problem here.

Data: Strings

How do I validate input?

The answer to this question is usually a regular expression, perhaps with auxiliary logic. See the more specific questions (numbers, email addresses, etc.) for details.

How do I unescape a string?

It depends just what you mean by ``escape''. URL escapes are dealt with in the perlfaq9 manpage. Shell escapes with the backslash (\) character are removed with:

    s/\\(.)/$1/g;

Note that this won't expand \n or \t or any other special escapes.

How do I remove consecutive pairs of characters?

To turn ``abbcccd'' into ``abccd'':

    s/(.)\1/$1/g;

How do I expand function calls in a string?

This is documented in the perlref manpage. In general, this is fraught with quoting and readability problems, but it is possible. To interpolate a subroutine call (in a list context) into a string:

    print "My sub returned @{[mysub(1,2,3)]} that time.\n";

If you prefer scalar context, similar chicanery is also useful for arbitrary expressions:

    print "That yields ${\($n + 5)} widgets\n";

See also ``How can I expand variables in text strings?'' in this section of the FAQ.

How do I find matching/nesting anything?

This isn't something that can be tackled in one regular expression, no matter how complicated. To find something between two single characters, a pattern like /x([^x]*)x/ will get the intervening bits in $1. For multiple ones, then something more like /alpha(.*?)omega/ would be needed. But none of these deals with nested patterns, nor can they. For that you'll have to write a parser.

How do I reverse a string?

Use reverse() in a scalar context, as documented in reverse.

    $reversed = reverse $string;

How do I expand tabs in a string?

You can do it the old-fashioned way:

    1 while $string =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;

Or you can just use the Text::Tabs module (part of the standard perl distribution).

    use Text::Tabs;
    @expanded_lines = expand(@lines_with_tabs);

How do I reformat a paragraph?

Use Text::Wrap (part of the standard perl distribution):

    use Text::Wrap;
    print wrap("\t", '  ', @paragraphs);

The paragraphs you give to Text::Wrap may not contain embedded newlines. Text::Wrap doesn't justify the lines (flush-right).

How can I access/change the first N letters of a string?

There are many ways. If you just want to grab a copy, use substr:

    $first_byte = substr($a, 0, 1);

If you want to modify part of a string, the simplest way is often to use substr() as an lvalue:

    substr($a, 0, 3) = "Tom";

Although those with a regexp kind of thought process will likely prefer

    $a =~ s/^.../Tom/;

How do I change the Nth occurrence of something?

You have to keep track. For example, let's say you want to change the fifth occurrence of ``whoever'' or ``whomever'' into ``whosoever'' or ``whomsoever'', case insensitively.

    $count = 0;
    s{((whom?)ever)}{
        ++$count == 5           # is it the 5th?
            ? "${2}soever"      # yes, swap
            : $1                # renege and leave it there
    }igex;

How can I count the number of occurrences of a substring within a string?

There are a number of ways, with varying efficiency: If you want a count of a certain single character (X) within a string, you can use the tr/// function like so:

    $string = "ThisXlineXhasXsomeXx'sXinXit":
    $count = ($string =~ tr/X//);
    print "There are $count X charcters in the string";

This is fine if you are just looking for a single character. However, if you are trying to count multiple character substrings within a larger string, tr/// won't work. What you can do is wrap a while() loop around a global pattern match. For example, let's count negative integers:

    $string = "-9 55 48 -2 23 -76 4 14 -44";
    while ($string =~ /-\d+/g) { $count++ }
    print "There are $count negative numbers in the string";

How do I capitalize all the words on one line?

To make the first letter of each word upper case:

        $line =~ s/\b(\w)/\U$1/g;

This has the strange effect of turning ``don't do it'' into ``Don'T Do It''. Sometimes you might want this, instead (Suggested by Brian Foy <comdog@computerdog.com>):

    $string =~ s/ (
                 (^\w)    #at the beginning of the line
                   |      # or
                 (\s\w)   #preceded by whitespace
                   )
                /\U$1/xg;
    $string =~ /([\w']+)/\u\L$1/g;

To make the whole line upper case:

        $line = uc($line);

To force each word to be lower case, with the first letter upper case:

        $line =~ s/(\w+)/\u\L$1/g;

How can I split a [character] delimited string except when inside [character]? (Comma-separated files)

Take the example case of trying to split a string that is comma-separated into its different fields. (We'll pretend you said comma-separated, not comma-delimited, which is different and almost never what you mean.) You can't use split(/,/) because you shouldn't split if the comma is inside quotes. For example, take a data line like this:

    SAR001,"","Cimetrix, Inc","Bob Smith","CAM",N,8,1,0,7,"Error, Core Dumped"

Due to the restriction of the quotes, this is a fairly complex problem. Thankfully, we have Jeffrey Friedl, author of a highly recommended book on regular expressions, to handle these for us. He suggests (assuming your string is contained in $text):

     @new = ();
     push(@new, $+) while $text =~ m{
         "([^\"\\]*(?:\\.[^\"\\]*)*)",?  # groups the phrase inside the quotes
       | ([^,]+),?
       | ,
     }gx;
     push(@new, undef) if substr($text,-1,1) eq ',';

If you want to represent quotation marks inside a quotation-mark-delimited field, escape them with backslashes (eg, C<``like \''this\``''). Unescaping them is a task addressed earlier in this section.

Alternatively, the Text::ParseWords module (part of the standard perl distribution) lets you say:

    use Text::ParseWords;
    @new = quotewords(",", 0, $text);

How do I strip blank space from the beginning/end of a string?

The simplest approach, albeit not the fastest, is probably like this:

    $string =~ s/^\s*(.*?)\s*$/$1/;

It would be faster to do this in two steps:

    $string =~ s/^\s+//;
    $string =~ s/\s+$//;

Or more nicely written as:

    for ($string) {
        s/^\s+//;
        s/\s+$//;
    }

How do I extract selected columns from a string?

Use substr() or unpack(), both documented in the perlfunc manpage.

How do I find the soundex value of a string?

Use the standard Text::Soundex module distributed with perl.

How can I expand variables in text strings?

Let's assume that you have a string like:

    $text = 'this has a $foo in it and a $bar';
    $text =~ s/\$(\w+)/${$1}/g;

Before version 5 of perl, this had to be done with a double-eval substitution:

    $text =~ s/(\$\w+)/$1/eeg;

Which is bizarre enough that you'll probably actually need an EEG afterwards. :-)

See also ``How do I expand function calls in a string?'' in this section of the FAQ.

What's wrong with always quoting "$vars"?

The problem is that those double-quotes force stringification, coercing numbers and references into strings, even when you don't want them to be.

If you get used to writing odd things like these:

    print "$var";       # BAD
    $new = "$old";      # BAD
    somefunc("$var");   # BAD

You'll be in trouble. Those should (in 99.8% of the cases) be the simpler and more direct:

    print $var;
    $new = $old;
    somefunc($var);

Otherwise, besides slowing you down, you're going to break code when the thing in the scalar is actually neither a string nor a number, but a reference:

    func(\@array);
    sub func {
        my $aref = shift;
        my $oref = "$aref";  # WRONG
    }

You can also get into subtle problems on those few operations in Perl that actually do care about the difference between a string and a number, such as the magical ++ autoincrement operator or the syscall() function.

Why don't my <

Check for these three things:

  1. There must be no space after the << part.
  2. There (probably) should be a semicolon at the end.
  3. You can't (easily) have any space in front of the tag.

Data: Arrays

What is the difference between $array[1] and @array[1]?

The former is a scalar value, the latter an array slice, which makes it a list with one (scalar) value. You should use $ when you want a scalar value (most of the time) and @ when you want a list with one scalar value in it (very, very rarely; nearly never, in fact).

Sometimes it doesn't make a difference, but sometimes it does. For example, compare:

    $good[0] = `some program that outputs several lines`;

with

    @bad[0]  = `same program that outputs several lines`;

The -w flag will warn you about these matters.

How can I extract just the unique elements of an array?

There are several possible ways, depending on whether the array is ordered and whether you wish to preserve the ordering.

a) If @in is sorted, and you want @out to be sorted:
    $prev = 'nonesuch';
    @out = grep($_ ne $prev && ($prev = $_), @in);

This is nice in that it doesn't use much extra memory, simulating uniq(1)'s behavior of removing only adjacent duplicates.

b) If you don't know whether @in is sorted:
    undef %saw;
    @out = grep(!$saw{$_}++, @in);

c) Like (b), but @in contains only small integers:
    @out = grep(!$saw[$_]++, @in);

d) A way to do (b) without any loops or greps:
    undef %saw;
    @saw{@in} = ();
    @out = sort keys %saw;  # remove sort if undesired

e) Like (d), but @in contains only small positive integers:
    undef @ary;
    @ary[@in] = @in;
    @out = @ary;

How can I tell whether an array contains a certain element?

There are several ways to approach this. If you are going to make this query many times and the values are arbitrary strings, the fastest way is probably to invert the original array and keep an associative array lying about whose keys are the first array's values.

    @blues = qw/azure cerulean teal turquoise lapis-lazuli/;
    undef %is_blue;
    for (@blues) { $is_blue{$_} = 1 }

Now you can check whether $is_blue{$some_color}. It might have been a good idea to keep the blues all in a hash in the first place.

If the values are all small integers, you could use a simple indexed array. This kind of an array will take up less space:

    @primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
    undef @is_tiny_prime;
    for (@primes) { $is_tiny_prime[$_] = 1; }

Now you check whether $is_tiny_prime[$some_number].

If the values in question are integers instead of strings, you can save quite a lot of space by using bit strings instead:

    @articles = ( 1..10, 150..2000, 2017 );
    undef $read;
    grep (vec($read,$_,1) = 1, @articles);

Now check whether vec($read,$n,1) is true for some $n.

Please do not use

    $is_there = grep $_ eq $whatever, @array;

or worse yet

    $is_there = grep /$whatever/, @array;

These are slow (checks every element even if the first matches), inefficient (same reason), and potentially buggy (what if there are regexp characters in $whatever?).

How do I compute the difference of two arrays? How do I compute the intersection of two arrays?

Use a hash. Here's code to do both and more. It assumes that each element is unique in a given array:

    @union = @intersection = @difference = ();
    %count = ();
    foreach $element (@array1, @array2) { $count{$element}++ }
    foreach $element (keys %count) {
        push @union, $element;
        push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
    }

How do I find the first array element for which a condition is true?

You can use this if you care about the index:

    for ($i=0; $i < @array; $i++) {
        if ($array[$i] eq "Waldo") {
            $found_index = $i;
            last;
        }
    }

Now $found_index has what you want.

How do I handle linked lists?

In general, you usually don't need a linked list in Perl, since with regular arrays, you can push and pop or shift and unshift at either end, or you can use splice to add and/or remove arbitrary number of elements at arbitrary points.

If you really, really wanted, you could use structures as described in the perldsc manpage or the perltoot manpage and do just what the algorithm book tells you to do.

How do I handle circular lists?

Circular lists could be handled in the traditional fashion with linked lists, or you could just do something like this with an array:

    unshift(@array, pop(@array));  # the last shall be first
    push(@array, shift(@array));   # and vice versa

How do I shuffle an array randomly?

Here's a shuffling algorithm which works its way through the list, randomly picking another element to swap the current element with:

    srand;
    @new = ();
    @old = 1 .. 10;  # just a demo
    while (@old) {
        push(@new, splice(@old, rand @old, 1));
    }

For large arrays, this avoids a lot of the reshuffling:

    srand;
    @new = ();
    @old = 1 .. 10000;  # just a demo
    for( @old ){
        my $r = rand @new+1;
        push(@new,$new[$r]);
        $new[$r] = $_;
    }

How do I process/modify each element of an array?

Use for/foreach:

    for (@lines) {
        s/foo/bar/;
        tr[a-z][A-Z];
    }

Here's another; let's compute spherical volumes:

    for (@radii) {
        $_ **= 3;
        $_ *= (4/3) * 3.14159;  # this will be constant folded
    }

How do I select a random element from an array?

Use the rand() function (see rand):

    srand;                      # not needed for 5.004 and later
    $index   = rand @array;
    $element = $array[$index];

How do I permute N elements of a list?

Here's a little program that generates all permutations of all the words on each line of input. The algorithm embodied in the permut() function should work on any list:

    #!/usr/bin/perl -n
    # permute - tchrist@perl.com
    permut([split], []);
    sub permut {
        my @head = @{ $_[0] };
        my @tail = @{ $_[1] };
        unless (@head) {
            # stop recursing when there are no elements in the head
            print "@tail\n";
        } else {
            # for all elements in @head, move one from @head to @tail
            # and call permut() on the new @head and @tail
            my(@newhead,@newtail,$i);
            foreach $i (0 .. $#head) {
                @newhead = @head;
                @newtail = @tail;
                unshift(@newtail, splice(@newhead, $i, 1));
                permut([@newhead], [@newtail]);
            }
        }
    }

How do I sort an array by (anything)?

Supply a comparison function to sort() (described in sort):

    @list = sort { $a <=> $b } @list;

The default sort function is cmp, string comparison, which would sort (1, 2, 10) into (1, 10, 2). <=>, used above, is the numerical comparison operator.

If you have a complicated function needed to pull out the part you want to sort on, then don't do it inside the sort function. Pull it out first, because the sort BLOCK can be called many times for the same element. Here's an example of how to pull out the first word after the first number on each item, and then sort those words case-insensitively.

    @idx = ();
    for (@data) {
        ($item) = /\d+\s*(\S+)/;
        push @idx, uc($item);
    }
    @sorted = @data[ sort { $idx[$a] cmp $idx[$b] } 0 .. $#idx ];

Which could also be written this way, using a trick that's come to be known as the Schwartzian Transform:

    @sorted = map  { $_->[0] }
              sort { $a->[1] cmp $b->[1] }
              map  { [ $_, uc((/\d+\s*(\S+)/ )[0] ] } @data;

If you need to sort on several fields, the following paradigm is useful.

    @sorted = sort { field1($a) <=> field1($b) ||
                     field2($a) cmp field2($b) ||
                     field3($a) cmp field3($b)
                   }     @data;

This can be conveniently combined with precalculation of keys as given above.

See http://www.perl.com/CPAN/doc/FMTEYEWTK/sort.html for more about this approach.

See also the question below on sorting hashes.

How do I manipulate arrays of bits?

Use pack() and unpack(), or else vec() and the bitwise operations.

For example, this sets $vec to have bit N set if $ints[N] was set:

    $vec = '';
    foreach(@ints) { vec($vec,$_,1) = 1 }

And here's how, given a vector in $vec, you can get those bits into your @ints array:

    sub bitvec_to_list {
        my $vec = shift;
        my @ints;
        # Find null-byte density then select best algorithm
        if ($vec =~ tr/\0// / length $vec > 0.95) {
            use integer;
            my $i;
            # This method is faster with mostly null-bytes
            while($vec =~ /[^\0]/g ) {
                $i = -9 + 8 * pos $vec;
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
            }
        } else {
            # This method is a fast general algorithm
            use integer;
            my $bits = unpack "b*", $vec;
            push @ints, 0 if $bits =~ s/^(\d)// && $1;
            push @ints, pos $bits while($bits =~ /1/g);
        }
        return \@ints;
    }

This method gets faster the more sparse the bit vector is. (Courtesy of Tim Bunce and Winfried Koenig.)

Why does defined() return true on empty arrays and hashes?

See defined in the 5.004 release or later of Perl.

Data: Hashes (Associative Arrays)

How do I process an entire hash?

Use the each() function (see each) if you don't care whether it's sorted:

    while (($key,$value) = each %hash) {
        print "$key = $value\n";
    }

If you want it sorted, you'll have to use foreach() on the result of sorting the keys as shown in an earlier question.

What happens if I add or remove keys from a hash while iterating over it?

Don't do that.

How do I look up a hash element by value?

Create a reverse hash:

    %by_value = reverse %by_key;
    $key = $by_value{$value};

That's not particularly efficient. It would be more space-efficient to use:

    while (($key, $value) = each %by_key) {
        $by_value{$value} = $key;
    }

If your hash could have repeated values, the methods above will only find one of the associated keys. This may or may not worry you.

How can I know how many entries are in a hash?

If you mean how many keys, then all you have to do is take the scalar sense of the keys() function:

    $num_keys = scalar keys %hash;

In void context it just resets the iterator, which is faster for tied hashes.

How do I sort a hash (optionally by value instead of key)?

Internally, hashes are stored in a way that prevents you from imposing an order on key-value pairs. Instead, you have to sort a list of the keys or values:

    @keys = sort keys %hash;    # sorted by key
    @keys = sort {
                    $hash{$a} cmp $hash{$b}
            } keys %hash;       # and by value

Here we'll do a reverse numeric sort by value, and if two keys are identical, sort by length of key, and if that fails, by straight ASCII comparison of the keys (well, possibly modified by your locale -- see the perllocale manpage).

    @keys = sort {
                $hash{$b} <=> $hash{$a}
                          ||
                length($b) <=> length($a)
                          ||
                      $a cmp $b
    } keys %hash;

How can I always keep my hash sorted?

You can look into using the DB_File module and tie() using the $DB_BTREE hash bindings as documented in In Memory Databases.

What's the difference between "delete" and "undef" with hashes?

Hashes are pairs of scalars: the first is the key, the second is the value. The key will be coerced to a string, although the value can be any kind of scalar: string, number, or reference. If a key $key is present in the array, exists($key) will return true. The value for a given key can be undef, in which case $array{$key} will be undef while $exists{$key} will return true. This corresponds to ($key, undef) being in the hash.

Pictures help... here's the %ary table:

          keys  values
        +------+------+
        |  a   |  3   |
        |  x   |  7   |
        |  d   |  0   |
        |  e   |  2   |
        +------+------+

And these conditions hold

        $ary{'a'}                       is true
        $ary{'d'}                       is false
        defined $ary{'d'}               is true
        defined $ary{'a'}               is true
        exists $ary{'a'}                is true (perl5 only)
        grep ($_ eq 'a', keys %ary)     is true

If you now say

        undef $ary{'a'}

your table now reads:

          keys  values
        +------+------+
        |  a   | undef|
        |  x   |  7   |
        |  d   |  0   |
        |  e   |  2   |
        +------+------+

and these conditions now hold; changes in caps:

        $ary{'a'}                       is FALSE
        $ary{'d'}                       is false
        defined $ary{'d'}               is true
        defined $ary{'a'}               is FALSE
        exists $ary{'a'}                is true (perl5 only)
        grep ($_ eq 'a', keys %ary)     is true

Notice the last two: you have an undef value, but a defined key!

Now, consider this:

        delete $ary{'a'}

your table now reads:

          keys  values
        +------+------+
        |  x   |  7   |
        |  d   |  0   |
        |  e   |  2   |
        +------+------+

and these conditions now hold; changes in caps:

        $ary{'a'}                       is false
        $ary{'d'}                       is false
        defined $ary{'d'}               is true
        defined $ary{'a'}               is false
        exists $ary{'a'}                is FALSE (perl5 only)
        grep ($_ eq 'a', keys %ary)     is FALSE

See, the whole entry is gone!

Why don't my tied hashes make the defined/exists distinction?

They may or may not implement the EXISTS() and DEFINED() methods differently. For example, there isn't the concept of undef with hashes that are tied to DBM* files. This means the true/false tables above will give different results when used on such a hash. It also means that exists and defined do the same thing with a DBM* file, and what they end up doing is not what they do with ordinary hashes.

How do I reset an each() operation part-way through?

Using keys %hash in a scalar context returns the number of keys in the hash and resets the iterator associated with the hash. You may need to do this if you use last to exit a loop early so that when you re-enter it, the hash iterator has been reset.

How can I get the unique keys from two hashes?

First you extract the keys from the hashes into arrays, and then solve the uniquifying the array problem described above. For example:

    %seen = ();
    for $element (keys(%foo), keys(%bar)) {
        $seen{$element}++;
    }
    @uniq = keys %seen;

Or more succinctly:

    @uniq = keys %{{%foo,%bar}};

Or if you really want to save space:

    %seen = ();
    while (defined ($key = each %foo)) {
        $seen{$key}++;
    }
    while (defined ($key = each %bar)) {
        $seen{$key}++;
    }
    @uniq = keys %seen;

How can I store a multidimensional array in a DBM file?

Either stringify the structure yourself (no fun), or else get the MLDBM (which uses Data::Dumper) module from CPAN and layer it on top of either DB_File or GDBM_File.

How can I make my hash remember the order I put elements into it?

Use the Tie::IxHash from CPAN.

    use Tie::IxHash;
    tie(%myhash, Tie::IxHash);
    for ($i=0; $i<20; $i++) {
        $myhash{$i} = 2*$i;
    }
    @keys = keys %myhash;
    # @keys = (0,1,2,3,...)

Why does passing a subroutine an undefined element in a hash create it?

If you say something like:

    somefunc($hash{"nonesuch key here"});

Then that element ``autovivifies''; that is, it springs into existence whether you store something there or not. That's because functions get scalars passed in by reference. If somefunc() modifies $_[0], it has to be ready to write it back into the caller's version.

This has been fixed as of perl5.004.

Normally, merely accessing a key's value for a nonexistent key does not cause that key to be forever there. This is different than awk's behavior.

How can I make the Perl equivalent of a C structure/C++ class/hash or array of hashes or arrays?

Use references (documented in the perlref manpage). Examples of complex data structures are given in the perldsc manpage and the perllol manpage. Examples of structures and object-oriented classes are in the perltoot manpage.

How can I use a reference as a hash key?

You can't do this directly, but you could use the standard Tie::Refhash module distributed with perl.

Data: Misc

How do I handle binary data correctly?

Perl is binary clean, so this shouldn't be a problem. For example, this works fine (assuming the files are found):

    if (`cat /vmunix` =~ /gzip/) {
        print "Your kernel is GNU-zip enabled!\n";
    }

On some systems, however, you have to play tedious games with ``text'' versus ``binary'' files. See binmode.

If you're concerned about 8-bit ASCII data, then see the perllocale manpage.

If you want to deal with multibyte characters, however, there are some gotchas. See the section on Regular Expressions.

How do I determine whether a scalar is a number/whole/integer/float?

Assuming that you don't care about IEEE notations like ``NaN'' or ``Infinity'', you probably just want to use a regular expression.

   warn "has nondigits"        if     /\D/;
   warn "not a whole number"   unless /^\d+$/;
   warn "not an integer"       unless /^-?\d+$/;  # reject +3
   warn "not an integer"       unless /^[+-]?\d+$/;
   warn "not a decimal number" unless /^-?\d+\.?\d*$/;  # rejects .2
   warn "not a decimal number" unless /^-?(?:\d+(?:\.\d*)?|\.\d+)$/;
   warn "not a C float"
       unless /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;

Or you could check out http://www.perl.com/CPAN/modules/by-module/String/String-Scanf-1.1.tar.gz instead. The POSIX module (part of the standard Perl distribution) provides the strtol and strtod for converting strings to double and longs, respectively.

How do I keep persistent data across program calls?

For some specific applications, you can use one of the DBM modules. See the AnyDBM_File manpage. More generically, you should consult the FreezeThaw, Storable, or Class::Eroot modules from CPAN.

How do I print out or copy a recursive data structure?

The Data::Dumper module on CPAN is nice for printing out data structures, and FreezeThaw for copying them. For example:

    use FreezeThaw qw(freeze thaw);
    $new = thaw freeze $old;

Where $old can be (a reference to) any kind of data structure you'd like. It will be deeply copied.

How do I define methods for every class/object?

Use the UNIVERSAL class (see the UNIVERSAL manpage).

How do I verify a credit card checksum?

Get the Business::CreditCard module from CPAN.

AUTHOR AND COPYRIGHT

Copyright (c) 1997 Tom Christiansen and Nathan Torkington. All rights reserved. See the perlfaq manpage for distribution information.


perlfaq5

perlfaq5 - perl常问问题集，第五篇

  • NAME
  • DESCRIPTION
    • How do I flush/unbuffer a filehandle? Why must I do this?
    • How do I change one line in a file/delete a line in a file/insert a line in the middle of a file/append to the beginning of a file?
    • How do I count the number of lines in a file?
    • How do I make a temporary file name?
    • How can I manipulate fixed-record-length files?
    • How can I make a filehandle local to a subroutine? How do I pass filehandles between subroutines? How do I make an array of filehandles?
    • How can I set up a footer format to be used with write()?
    • How can I write() into a string?
    • How can I output my numbers with commas added?
    • How can I translate tildes (~) in a filename?
    • How come when I open the file read-write it wipes it out?
    • Why do I sometimes get an "Argument list too long" when I use <*>?
    • Is there a leak/bug in glob()?
    • How can I open a file with a leading ">" or trailing blanks?
    • How can I reliably rename a file?
    • How can I lock a file?
    • What can't I just open(FH, ">file.lock")?
    • I still don't get locking. I just want to increment the number in the file. How can I do this?
    • How do I randomly update a binary file?
    • How do I get a file's timestamp in perl?
    • How do I set a file's timestamp in perl?
    • How do I print to more than one file at once?
    • How can I read in a file by paragraphs?
    • How can I read a single character from a file? From the keyboard?
    • How can I tell if there's a character waiting on a filehandle?
    • How do I open a file without blocking?
    • How do I create a file only if it doesn't exist?
    • How do I do a tail -f in perl?
    • How do I dup() a filehandle in Perl?
    • How do I close a file descriptor by number?
    • Why can't I use "C:\temp\foo" in DOS paths? What doesn't `C:\temp\foo.exe` work?
    • Why doesn't glob("*.*") get all the files?
    • Why does Perl let me delete read-only files? Why does -i clobber protected files? Isn't this a bug in Perl?
    • How do I select a random line from a file?
  • AUTHOR AND COPYRIGHT

NAME

perlfaq5 - Files and Formats ($Revision: 1.22 $, $Date: 1997/04/24 22:44:02 $)

DESCRIPTION

This section deals with I/O and the ``f'' issues: filehandles, flushing, formats, and footers.

How do I flush/unbuffer a filehandle? Why must I do this?

The C standard I/O library (stdio) normally buffers characters sent to devices. This is done for efficiency reasons, so that there isn't a system call for each byte. Any time you use print() or write() in Perl, you go though this buffering. syswrite() circumvents stdio and buffering.

In most stdio implementations, the type of buffering and the size of the buffer varies according to the type of device. Disk files are block buffered, often with a buffer size of more than 2k. Pipes and sockets are often buffered with a buffer size between 1/2 and 2k. Serial devices (e.g. modems, terminals) are normally line-buffered, and stdio sends the entire line when it gets the newline.

Perl does not support truly unbuffered output (except insofar as you can syswrite(OUT, $char, 1)). What it does instead support is ``command buffering'', in which a physical write is performed after every output command. This isn't as hard on your system as unbuffering, but does get the output where you want it when you want it.

If you expect characters to get to your device when you print them there, you'll want to autoflush its handle, as in the older:

    use FileHandle;
    open(DEV, "<+/dev/tty");      # ceci n'est pas une pipe
    DEV->autoflush(1);

or the newer IO::* modules:

    use IO::Handle;
    open(DEV, ">/dev/printer");   # but is this?
    DEV->autoflush(1);

or even this:

    use IO::Socket;               # this one is kinda a pipe?
    $sock = IO::Socket::INET->new(PeerAddr => 'www.perl.com',
                                  PeerPort => 'http(80)',
                                  Proto    => 'tcp');
    die "$!" unless $sock;

    $sock->autoflush();
    $sock->print("GET /\015\012");
    $document = join('', $sock->getlines());
    print "DOC IS: $document\n";

Note the hardcoded carriage return and newline in their octal equivalents. This is the ONLY way (currently) to assure a proper flush on all platforms, including Macintosh.

You can use select() and the $| variable to control autoflushing (see $| and select):

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

You'll also see code that does this without a temporary variable, as in

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

How do I change one line in a file/delete a line in a file/insert a line in the middle of a file/append to the beginning of a file?

Although humans have an easy time thinking of a text file as being a sequence of lines that operates much like a stack of playing cards -- or punch cards -- computers usually see the text file as a sequence of bytes. In general, there's no direct way for Perl to seek to a particular line of a file, insert text into a file, or remove text from a file.

(There are exceptions in special circumstances. Replacing a sequence of bytes with another sequence of the same length is one. Another is using the $DB_RECNO array bindings as documented in the DB_File manpage. Yet another is manipulating files with all lines the same length.)

The general solution is to create a temporary copy of the text file with the changes you want, then copy that over the original.

    $old = $file;
    $new = "$file.tmp.$$";
    $bak = "$file.bak";

    open(OLD, "< $old")         or die "can't open $old: $!";
    open(NEW, "> $new")         or die "can't open $new: $!";

    # Correct typos, preserving case
    while (<OLD>) {
        s/\b(p)earl\b/${1}erl/i;
        (print NEW $_)          or die "can't write to $new: $!";
    }

    close(OLD)                  or die "can't close $old: $!";
    close(NEW)                  or die "can't close $new: $!";

    rename($old, $bak)          or die "can't rename $old to $bak: $!";
    rename($new, $old)          or die "can't rename $new to $old: $!";

Perl can do this sort of thing for you automatically with the -i command-line switch or the closely-related $^I variable (see the perlrun manpage for more details). Note that -i may require a suffix on some non-Unix systems; see the platform-specific documentation that came with your port.

    # Renumber a series of tests from the command line
    perl -pi -e 's/(^\s+test\s+)\d+/ $1 . ++$count /e' t/op/taint.t

    # form a script
    local($^I, @ARGV) = ('.bak', glob("*.c"));
    while (<>) {
        if ($. == 1) {
            print "This line should appear at the top of each file\n";
        }
        s/\b(p)earl\b/${1}erl/i;        # Correct typos, preserving case
        print;
        close ARGV if eof;              # Reset $.
    }

If you need to seek to an arbitrary line of a file that changes infrequently, you could build up an index of byte positions of where the line ends are in the file. If the file is large, an index of every tenth or hundredth line end would allow you to seek and read fairly efficiently. If the file is sorted, try the look.pl library (part of the standard perl distribution).

In the unique case of deleting lines at the end of a file, you can use tell() and truncate(). The following code snippet deletes the last line of a file without making a copy or reading the whole file into memory:

        open (FH, "+< $file");
        while ( <FH> ) { $addr = tell(FH) unless eof(FH) }
        truncate(FH, $addr);

Error checking is left as an exercise for the reader.

How do I count the number of lines in a file?

One fairly efficient way is to count newlines in the file. The following program uses a feature of tr///, as documented in the perlop manpage. If your text file doesn't end with a newline, then it's not really a proper text file, so this may report one fewer line than you expect.

    $lines = 0;
    open(FILE, $filename) or die "Can't open `$filename': $!";
    while (sysread FILE, $buffer, 4096) {
        $lines += ($buffer =~ tr/\n//);
    }
    close FILE;

How do I make a temporary file name?

Use the process ID and/or the current time-value. If you need to have many temporary files in one process, use a counter:

    BEGIN {
        use IO::File;
        use Fcntl;
        my $temp_dir = -d '/tmp' ? '/tmp' : $ENV{TMP} || $ENV{TEMP};
        my $base_name = sprintf("%s/%d-%d-0000", $temp_dir, $$, time());
        sub temp_file {
            my $fh = undef;
            my $count = 0;
            until (defined($fh) || $count > 100) {
                $base_name =~ s/-(\d+)$/"-" . (1 + $1)/e;
                $fh = IO::File->new($base_name, O_WRONLY|O_EXCL|O_CREAT, 0644)
            }
            if (defined($fh)) {
                return ($fh, $base_name);
            } else {
                return ();
            }
        }
    }

Or you could simply use IO::Handle::new_tmpfile.

How can I manipulate fixed-record-length files?

The most efficient way is using pack() and unpack(). This is faster than using substr(). Here is a sample chunk of code to break up and put back together again some fixed-format input lines, in this case from the output of a normal, Berkeley-style ps:

    # sample input line:
    #   15158 p5  T      0:00 perl /home/tchrist/scripts/now-what
    $PS_T = 'A6 A4 A7 A5 A*';
    open(PS, "ps|");
    $_ = <PS>; print;
    while (<PS>) {
        ($pid, $tt, $stat, $time, $command) = unpack($PS_T, $_);
        for $var (qw!pid tt stat time command!) {
            print "$var: <$$var>\n";
        }
        print 'line=', pack($PS_T, $pid, $tt, $stat, $time, $command),
                "\n";
    }

How can I make a filehandle local to a subroutine? How do I pass filehandles between subroutines? How do I make an array of filehandles?

You may have some success with typeglobs, as we always had to use in days of old:

    local(*FH);

But while still supported, that isn't the best to go about getting local filehandles. Typeglobs have their drawbacks. You may well want to use the FileHandle module, which creates new filehandles for you (see the FileHandle manpage):

    use FileHandle;
    sub findme {
        my $fh = FileHandle->new();
        open($fh, "</etc/hosts") or die "no /etc/hosts: $!";
        while (<$fh>) {
            print if /\b127\.(0\.0\.)?1\b/;
        }
        # $fh automatically closes/disappears here
    }

Internally, Perl believes filehandles to be of class IO::Handle. You may use that module directly if you'd like (see Handle), or one of its more specific derived classes.

Once you have IO::File or FileHandle objects, you can pass them between subroutines or store them in hashes as you would any other scalar values:

    use FileHandle;

    # Storing filehandles in a hash and array
    foreach $filename (@names) {
        my $fh = new FileHandle($filename)              or die;
        $file{$filename} = $fh;
        push(@files, $fh);
    }

    # Using the filehandles in the array
    foreach $file (@files) {
        print $file "Testing\n";
    }

    # You have to do the { } ugliness when you're specifying the
    # filehandle by anything other than a simple scalar variable.
    print { $files[2] } "Testing\n";

    # Passing filehandles to subroutines
    sub debug {
        my $filehandle = shift;
        printf $filehandle "DEBUG: ", @_;
    }

    debug($fh, "Testing\n");

How can I set up a footer format to be used with write()?

There's no builtin way to do this, but the perlform manpage has a couple of techniques to make it possible for the intrepid hacker.

How can I write() into a string?

See the perlform manpage for an swrite() function.

How can I output my numbers with commas added?

This one will do it for you:

    sub commify {
        local $_  = shift;
        1 while s/^(-?\d+)(\d{3})/$1,$2/;
        return $_;
    }

    $n = 23659019423.2331;
    print "GOT: ", commify($n), "\n";

    GOT: 23,659,019,423.2331

You can't just:

    s/^(-?\d+)(\d{3})/$1,$2/g;

because you have to put the comma in and then recalculate your position.

Alternatively, this commifies all numbers in a line regardless of whether they have decimal portions, are preceded by + or -, or whatever:

    # from Andrew Johnson <ajohnson@gpu.srv.ualberta.ca>
    sub commify {
       my $input = shift;
        $input = reverse $input;
        $input =~ s<(\d\d\d)(?=\d)(?!\d*\.)><$1,>g;
        return reverse $input;
    }

How can I translate tildes (~) in a filename?

Use the <> (glob()) operator, documented in the perlfunc manpage. This requires that you have a shell installed that groks tildes, meaning csh or tcsh or (some versions of) ksh, and thus may have portability problems. The Glob::KGlob module (available from CPAN) gives more portable glob functionality.

Within Perl, you may use this directly:

        $filename =~ s{
          ^ ~             # find a leading tilde
          (               # save this in $1
              [^/]        # a non-slash character
                    *     # repeated 0 or more times (0 means me)
          )
        }{
          $1
              ? (getpwnam($1))[7]
              : ( $ENV{HOME} || $ENV{LOGDIR} )
        }ex;

How come when I open the file read-write it wipes it out?

Because you're using something like this, which truncates the file and then gives you read-write access:

    open(FH, "+> /path/name");  # WRONG

Whoops. You should instead use this, which will fail if the file doesn't exist.

    open(FH, "+< /path/name");  # open for update

If this is an issue, try:

    sysopen(FH, "/path/name", O_RDWR|O_CREAT, 0644);

Error checking is left as an exercise for the reader.

Why do I sometimes get an "Argument list too long" when I use <*>?

The <> operator performs a globbing operation (see above). By default glob() forks csh(1) to do the actual glob expansion, but csh can't handle more than 127 items and so gives the error message Argument list too long. People who installed tcsh as csh won't have this problem, but their users may be surprised by it.

To get around this, either do the glob yourself with Dirhandles and patterns, or use a module like Glob::KGlob, one that doesn't use the shell to do globbing.

Is there a leak/bug in glob()?

Due to the current implementation on some operating systems, when you use the glob() function or its angle-bracket alias in a scalar context, you may cause a leak and/or unpredictable behavior. It's best therefore to use glob() only in list context.

How can I open a file with a leading ">" or trailing blanks?

Normally perl ignores trailing blanks in filenames, and interprets certain leading characters (or a trailing ``|'') to mean something special. To avoid this, you might want to use a routine like this. It makes incomplete pathnames into explicit relative ones, and tacks a trailing null byte on the name to make perl leave it alone:

    sub safe_filename {
        local $_  = shift;
        return m#^/#
                ? "$_\0"
                : "./$_\0";
    }

    $fn = safe_filename("<<<something really wicked   ");
    open(FH, "> $fn") or "couldn't open $fn: $!";

You could also use the sysopen() function (see sysopen).

How can I reliably rename a file?

Well, usually you just use Perl's rename() function. But that may not work everywhere, in particular, renaming files across file systems. If your operating system supports a mv(1) program or its moral equivalent, this works:

    rename($old, $new) or system("mv", $old, $new);

It may be more compelling to use the File::Copy module instead. You just copy to the new file to the new name (checking return values), then delete the old one. This isn't really the same semantics as a real rename(), though, which preserves metainformation like permissions, timestamps, inode info, etc.

How can I lock a file?

Perl's builtin flock() function (see the perlfunc manpage for details) will call flock(2) if that exists, fcntl(2) if it doesn't (on perl version 5.004 and later), and lockf(3) if neither of the two previous system calls exists. On some systems, it may even use a different form of native locking. Here are some gotchas with Perl's flock():

  1. Produces a fatal error if none of the three system calls (or their close equivalent) exists.

  2. lockf(3) does not provide shared locking, and requires that the filehandle be open for writing (or appending, or read/writing).

  3. Some versions of flock() can't lock files over a network (e.g. on NFS file systems), so you'd need to force the use of fcntl(2) when you build Perl. See the flock entry of the perlfunc manpage, and the INSTALL file in the source distribution for information on building Perl to do this.

The CPAN module File::Lock offers similar functionality and (if you have dynamic loading) won't require you to rebuild perl if your flock() can't lock network files.

What can't I just open(FH, ">file.lock")?

A common bit of code NOT TO USE is this:

    sleep(3) while -e "file.lock";      # PLEASE DO NOT USE
    open(LCK, "> file.lock");           # THIS BROKEN CODE

This is a classic race condition: you take two steps to do something which must be done in one. That's why computer hardware provides an atomic test-and-set instruction. In theory, this ``ought'' to work:

    sysopen(FH, "file.lock", O_WRONLY|O_EXCL|O_CREAT, 0644)
                or die "can't open  file.lock: $!":

except that lamentably, file creation (and deletion) is not atomic over NFS, so this won't work (at least, not every time) over the net. Various schemes involving involving link() have been suggested, but these tend to involve busy-wait, which is also subdesirable.

I still don't get locking. I just want to increment the number in the file. How can I do this?

Didn't anyone ever tell you web-page hit counters were useless?

Anyway, this is what to do:

    use Fcntl;
    sysopen(FH, "numfile", O_RDWR|O_CREAT, 0644) or die "can't open numfile: $!";
    flock(FH, 2)                                 or die "can't flock numfile: $!";
    $num = <FH> || 0;
    seek(FH, 0, 0)                               or die "can't rewind numfile: $!";
    truncate(FH, 0)                              or die "can't truncate numfile: $!";
    (print FH $num+1, "\n")                      or die "can't write numfile: $!";
    # DO NOT UNLOCK THIS UNTIL YOU CLOSE
    close FH                                     or die "can't close numfile: $!";

Here's a much better web-page hit counter:

    $hits = int( (time() - 850_000_000) / rand(1_000) );

If the count doesn't impress your friends, then the code might. :-)

How do I randomly update a binary file?

If you're just trying to patch a binary, in many cases something as simple as this works:

    perl -i -pe 's{window manager}{window mangler}g' /usr/bin/emacs

However, if you have fixed sized records, then you might do something more like this:

    $RECSIZE = 220; # size of record, in bytes
    $recno   = 37;  # which record to update
    open(FH, "+<somewhere") || die "can't update somewhere: $!";
    seek(FH, $recno * $RECSIZE, 0);
    read(FH, $record, $RECSIZE) == $RECSIZE || die "can't read record $recno: $!";
    # munge the record
    seek(FH, $recno * $RECSIZE, 0);
    print FH $record;
    close FH;

Locking and error checking are left as an exercise for the reader. Don't forget them, or you'll be quite sorry.

Don't forget to set binmode() under DOS-like platforms when operating on files that have anything other than straight text in them. See the docs on open() and on binmode() for more details.

How do I get a file's timestamp in perl?

If you want to retrieve the time at which the file was last read, written, or had its meta-data (owner, etc) changed, you use the -M, -A, or -C filetest operations as documented in the perlfunc manpage. These retrieve the age of the file (measured against the start-time of your program) in days as a floating point number. To retrieve the ``raw'' time in seconds since the epoch, you would call the stat function, then use localtime(), gmtime(), or POSIX::strftime() to convert this into human-readable form.

Here's an example:

    $write_secs = (stat($file))[9];
    print "file $file updated at ", scalar(localtime($file)), "\n";

If you prefer something more legible, use the File::stat module (part of the standard distribution in version 5.004 and later):

    use File::stat;
    use Time::localtime;
    $date_string = ctime(stat($file)->mtime);
    print "file $file updated at $date_string\n";

Error checking is left as an exercise for the reader.

How do I set a file's timestamp in perl?

You use the utime() function documented in utime. By way of example, here's a little program that copies the read and write times from its first argument to all the rest of them.

    if (@ARGV < 2) {
        die "usage: cptimes timestamp_file other_files ...\n";
    }
    $timestamp = shift;
    ($atime, $mtime) = (stat($timestamp))[8,9];
    utime $atime, $mtime, @ARGV;

Error checking is left as an exercise for the reader.

Note that utime() currently doesn't work correctly with Win95/NT ports. A bug has been reported. Check it carefully before using it on those platforms.

How do I print to more than one file at once?

If you only have to do this once, you can do this:

    for $fh (FH1, FH2, FH3) { print $fh "whatever\n" }

To connect up to one filehandle to several output filehandles, it's easiest to use the tee(1) program if you have it, and let it take care of the multiplexing:

    open (FH, "| tee file1 file2 file3");

Otherwise you'll have to write your own multiplexing print function -- or your own tee program -- or use Tom Christiansen's, at http://www.perl.com/CPAN/authors/id/TOMC/scripts/tct.gz, which is written in Perl.

In theory a IO::Tee class could be written, but to date we haven't seen such.

How can I read in a file by paragraphs?

Use the $\ variable (see the perlvar manpage for details). You can either set it to "" to eliminate empty paragraphs ("abc\n\n\n\ndef", for instance, gets treated as two paragraphs and not three), or "\n\n" to accept empty paragraphs.

How can I read a single character from a file? From the keyboard?

You can use the builtin getc() function for most filehandles, but it won't (easily) work on a terminal device. For STDIN, either use the Term::ReadKey module from CPAN, or use the sample code in getc.

If your system supports POSIX, you can use the following code, which you'll note turns off echo processing as well.

    #!/usr/bin/perl -w
    use strict;
    $| = 1;
    for (1..4) {
        my $got;
        print "gimme: ";
        $got = getone();
        print "--> $got\n";
    }
    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() }

The Term::ReadKey module from CPAN may be easier to use:

    use Term::ReadKey;
    open(TTY, "</dev/tty");
    print "Gimme a char: ";
    ReadMode "raw";
    $key = ReadKey 0, *TTY;
    ReadMode "normal";
    printf "\nYou said %s, char number %03d\n",
        $key, ord $key;

For DOS systems, Dan Carson <dbc@tc.fluke.COM> reports the following:

To put the PC in ``raw'' mode, use ioctl with some magic numbers gleaned from msdos.c (Perl source file) and Ralf Brown's interrupt list (comes across the net every so often):

    $old_ioctl = ioctl(STDIN,0,0);     # Gets device info
    $old_ioctl &= 0xff;
    ioctl(STDIN,1,$old_ioctl | 32);    # Writes it back, setting bit 5

Then to read a single character:

    sysread(STDIN,$c,1);               # Read a single character

And to put the PC back to ``cooked'' mode:

    ioctl(STDIN,1,$old_ioctl);         # Sets it back to cooked mode.

So now you have $c. If ord($c) == 0, you have a two byte code, which means you hit a special key. Read another byte with sysread(STDIN,$c,1), and that value tells you what combination it was according to this table:

    # PC 2-byte keycodes = ^@ + the following:

    # HEX     KEYS
    # ---     ----
    # 0F      SHF TAB
    # 10-19   ALT QWERTYUIOP
    # 1E-26   ALT ASDFGHJKL
    # 2C-32   ALT ZXCVBNM
    # 3B-44   F1-F10
    # 47-49   HOME,UP,PgUp
    # 4B      LEFT
    # 4D      RIGHT
    # 4F-53   END,DOWN,PgDn,Ins,Del
    # 54-5D   SHF F1-F10
    # 5E-67   CTR F1-F10
    # 68-71   ALT F1-F10
    # 73-77   CTR LEFT,RIGHT,END,PgDn,HOME
    # 78-83   ALT 1234567890-=
    # 84      CTR PgUp

This is all trial and error I did a long time ago, I hope I'm reading the file that worked.

How can I tell if there's a character waiting on a filehandle?

You should check out the Frequently Asked Questions list in comp.unix.* for things like this: the answer is essentially the same. It's very system dependent. Here's one solution that works on BSD systems:

    sub key_ready {
        my($rin, $nfd);
        vec($rin, fileno(STDIN), 1) = 1;
        return $nfd = select($rin,undef,undef,0);
    }

You should look into getting the Term::ReadKey extension from CPAN.

How do I open a file without blocking?

You need to use the O_NDELAY or O_NONBLOCK flag from the Fcntl module in conjunction with sysopen():

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

How do I create a file only if it doesn't exist?

You need to use the O_CREAT and O_EXCL flags from the Fcntl module in conjunction with sysopen():

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

Be warned that neither creation nor deletion of files is guaranteed to be an atomic operation over NFS. That is, two processes might both successful create or unlink the same file!

How do I do a tail -f in perl?

First try

    seek(GWFILE, 0, 1);

The statement seek(GWFILE, 0, 1) doesn't change the current position, but it does clear the end-of-file condition on the handle, so that the next <GWFILE> makes Perl try again to read something.

If that doesn't work (it relies on features of your stdio implementation), then you need something more like this:

        for (;;) {
          for ($curpos = tell(GWFILE); <GWFILE>; $curpos = tell(GWFILE)) {
            # search for some stuff and put it into files
          }
          # sleep for a while
          seek(GWFILE, $curpos, 0);  # seek to where we had been
        }

If this still doesn't work, look into the POSIX module. POSIX defines the clearerr() method, which can remove the end of file condition on a filehandle. The method: read until end of file, clearerr(), read some more. Lather, rinse, repeat.

How do I dup() a filehandle in Perl?

If you check open, you'll see that several of the ways to call open() should do the trick. For example:

    open(LOG, ">>/tmp/logfile");
    open(STDERR, ">&LOG");

Or even with a literal numeric descriptor:

   $fd = $ENV{MHCONTEXTFD};
   open(MHCONTEXT, "<&=$fd");   # like fdopen(3S)

Error checking has been left as an exercise for the reader.

How do I close a file descriptor by number?

This should rarely be necessary, as the Perl close() function is to be used for things that Perl opened itself, even if it was a dup of a numeric descriptor, as with MHCONTEXT above. But if you really have to, you may be able to do this:

    require 'sys/syscall.ph';
    $rc = syscall(&SYS_close, $fd + 0);  # must force numeric
    die "can't sysclose $fd: $!" unless $rc == -1;

Why can't I use "C:\temp\foo" in DOS paths? What doesn't `C:\temp\foo.exe` work?

Whoops! You just put a tab and a formfeed into that filename! Remember that within double quoted strings (``like\this''), the backslash is an escape character. The full list of these is in Quote and Quote-like Operators. Unsurprisingly, you don't have a file called ``c:(tab)emp(formfeed)oo'' or ``c:(tab)emp(formfeed)oo.exe'' on your DOS filesystem.

Either single-quote your strings, or (preferably) use forward slashes. Since all DOS and Windows versions since something like MS-DOS 2.0 or so have treated / and \ the same in a path, you might as well use the one that doesn't clash with Perl -- or the POSIX shell, ANSI C and C++, awk, Tcl, Java, or Python, just to mention a few.

Why doesn't glob("*.*") get all the files?

Because even on non-Unix ports, Perl's glob function follows standard Unix globbing semantics. You'll need glob("*") to get all (non-hidden) files.

Why does Perl let me delete read-only files? Why does -i clobber protected files? Isn't this a bug in Perl?

This is elaborately and painstakingly described in the ``Far More Than You Every Wanted To Know'' in http://www.perl.com/CPAN/doc/FMTEYEWTK/file-dir-perms .

The executive summary: learn how your filesystem works. The permissions on a file say what can happen to the data in that file. The permissions on a directory say what can happen to the list of files in that directory. If you delete a file, you're removing its name from the directory (so the operation depends on the permissions of the directory, not of the file). If you try to write to the file, the permissions of the file govern whether you're allowed to.

How do I select a random line from a file?

Here's an algorithm from the Camel Book:

    srand;
    rand($.) < 1 && ($line = $_) while <>;

This has a significant advantage in space over reading the whole file in.

AUTHOR AND COPYRIGHT

Copyright (c) 1997 Tom Christiansen and Nathan Torkington. All rights reserved. See the perlfaq manpage for distribution information.


perlfaq6

perlfaq6 - perl常问问题集，第六篇

  • 篇名
  • 概述
    • 我该如何使用正规表示式才不至於写出不合语法且难以维护的程式码?
    • 我无法对应到超过一行的内容，哪里出了问题?
    • 我如何取出位於不同行的两个模式间之内容?
    • 我把一个正规表示式放入 $/但却没有用。错在哪里?
    • 如何在 LHS端【译注：式子中运算元左端部份】作不区别大小写式的替换，但在 RHS端【右端】保留大小写区别?
    • 如何使 \w对应到附重音记号 (accented)的字元?
    • 如何作一个适合不同 locale【译注：国家、地区在文字编码上各自的惯例】的 /[a-zA-Z]/对应?
    • 在一个正规表示式里如何引入 (quote)变数?
    • /o到底是干麽用的?
    • 如何使用正规表示式将档案中 C语言样式的注解删掉?
    • 我能用 Perl的正规表示式去对应成对的符号吗?
    • 有人说正规表示式很贪婪，那是什麽意思?该如何避免它所带来的问题?
    • 如何处理每一行的每个字?
    • 我如何印出文字出现频率或行出现频率的纲要?
    • 如何能作近似对应?
    • 我如何有效率地一次对应多个正规表示式?
    • 为何我用 \b作字界搜寻时会失败呢?
    • 为什麽每当我用 $&, $`,或 $'时程式的速度就慢下来了呢?
    • 正规表示式中的 \G能给我什麽好处?
    • Perl正规表示引擎是 DFAs或 NFAs?它们是 POSIX相容的吗?
    • 在无递回的场合下用 grep或 map有什麽不对?
    • 如何对应多位元组字母所构成的字串?
  • 作者与版权事宜

篇名

perlfaq6 -正规表示式原文版 Revision: 1.17, Date: 1997/04/24 22:44:10 中译版 $Revision: 1.4 $, $Date: 1997/08/03 17:22:55 $)

概述

本节之所以出人意料地小是因为在这份 FAQ 的其它部份已散布着与正规表示式有关的答案了。例如说，从一串文字中撷取 URL，以及检查字串是否含数字，这些都是以正 规表示式来处理的，但是这些问题的答案得到本 FAQ的其它部份去找 (更精确地说，是资料和网路那两部份)。

我该如何使用正规表示式才不至於写出不合语法且难以维护的程式码?

以下提供叁个技巧使得你的正规表示式易懂又好维护。

在正规表示式外围作注解。
用 Perl的注解方式描述你所作的事以及你如何作到它。

    #把每行变成「第一个字、冒号，和剩馀的字元数」这样的格式。
    s/^(\w+)(.*)/ lc($1) . ":" . length($2) /ge;

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

/x让你把下面这行：

    s{<(?:[^>'"]*|".*?"|'.*?')+>}{}gs;

变成：

    s{ <                    #箭头括弧区起始
        (?:                 #划分「勿追溯前段」(non-backreferencing)的括弧
             [^>'"] *       #有零个以上、不是 >、 '，或 "的字元
                |           #或者是
             ".*?"          #一段双引号圈起来的区域 (吝啬式对应)
                |           #或者是
             '.*?'          #一段单引号圈起来的区域 (吝啬式对应)
        ) +                 #以上区域出现一次或多次
       >                    #箭头括弧区结束
    }{}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(.*?)END/sm ) { # /s使得 .能跨越行界
            print "$1\n";
        }
    }

我如何取出位於不同行的两个模式间之内容?

你可以使用看起来有点怪的 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/, <FH>;

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 "+<file";

    ##把它附着成一个 "stream"物件
    use Net::Telnet;
    $file = new Net::Telnet (-fhopen => $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 < $newlen ? $oldlen : $newlen;

        for ($i = 0; $i < $len; $i++) {
            if ($c = substr($old, $i, 1), $c =~ /[\W\d_]/) {
                $state = 0;
            } elsif (lc $c eq $c) {
                substr($new, $i, 1) = lc(substr($new, $i, 1));
                $state = 1;
            } else {
                substr($new, $i, 1) = uc(substr($new, $i, 1));
                $state = 2;
            }
        }
        #把剩下的 new部份作处理 (当 new比 old长时)
        if ($newlen > $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.perl.com/CPAN/authors/id/TOMC/scripts/pull_quotes.gz 找到一个精细复杂的副常式(给 7-bit ASCII专用)，它可以抓出成对甚至於巢状分布 的单一字元，像 `和 '，{和 }，或 (和 )。

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 (<FH>) {
        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出版) ，获得所有你想知道关於这些事的所有细节(在 perlfaq2里面有该书的详细资料)。

在无递回的场合下用 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]|.)/gs) {  #也许不需要 \G
       print "找到 GX了!\n", last if $1 eq 'GX';
   }

不然乾脆这样：

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

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

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

作者与版权事宜

Copyright (c) 1997 Tom Christiansen and Nathan Torkington. All rights reserved.有关使用、（转）发行事宜，详见 perlfaq

译者：陈彦铭

中译版着作权所有：陈彦铭、萧百龄及两只老虎工作室 。本中译版遵守并使用与原文版相同的使用条款发行。


perlfaq7

perlfaq7 - perl常问问题集，第七篇

  • 篇名
  • 概述
    • 我能拿到 Perl的 BNF/yacc/RE吗？
    • $@%*这些符号是什麽意思？我怎麽知道何时该使用他们呢？
    • 字串加引号或使用分号及逗号是否绝对必要/还是完全没必要？
    • 我如何跳过一些传回值？
    • 我如何暂时滤掉警告讯息？
    • 什麽是一个扩充 (extension)？
    • 为何 Perl运算子的优先顺序和 C的不一样？
    • 我如何宣告/生成一个资料结构 (structure)？
    • 我如何创造出一个模组 (module)？
    • 我如何创造一个类别 (class)？
    • 我如何辨别一个变数是否被污染了(tainted)？
    • 闭包 (closure)是啥？
    • 何谓变数自杀而我又该如何防止它？
    • 我如何传递/传回一个 {函数，档案把手，阵列，杂凑阵列，方法，和正规表现式}？
    • 我如何生成一个静态变数？
    • 动态与文字式(静态)范围界定 (scoping)有何不同？ Local()和 my()呢？
    • 当同一个范围中有一个文字式变数时，我该如何去撷取同名的动态变数？
    • 所谓深连结与浅连结 (deep and shallow binding)间有何不同呢？
    • 为何 "local($foo) = ;"无法正确地作用？
    • 我如何重新定义一个内建函数、运算子或是方法？
    • 用 &foo和 foo()的方式来呼叫一个函数有什麽不同？
    • 我如何作一个 switch或 case叙述？
    • 我如何抓到呼叫未定义变数/函数/方法的事件？
    • 为什麽我的程式会找不到放在同一档案中的方法 (method)呢？
    • 我如何找出目前所在的 package为何？
    • 我如何将一大块 perl程式码变成注解？
  • 作者与版权事宜

篇名

perlfaq7 - Perl语言相关问题 ($（原文版 Revision: 1.18, Date: 1997/04/24 22:44:14 中译版 $Revision: 1.1 $ $Date: 1998/01/16 23:57:56 $）

概述

本篇内的问题主要是不适合纳入其他篇章的一般性 Perl语言问题。

我能拿到 Perl的 BNF/yacc/RE吗？

不行，引用 Chaim Frenkel的话：“Perl的语法无法被简化到可以用 BNF 表示。解析Perl的工作是分散於 yacc、lexer、烟雾和镜子之间。”

$@%*这些符号是什麽意思？我怎麽知道何时该使用他们呢？

他们都是指定形态 (type)用的符号，如同 perldata里所详述的：

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

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

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

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

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

字串加引号或使用分号及逗号是否绝对必要/还是完全没必要？

通常一个没有冠上形态符号的字 (bareword)是不需被纳入引号里的，但在大多数 的情况下或许该这麽做 (在use strict下则是必须的)。但由一个简单的字(不 能是一个已定义的副函数之名称)所构成的索引值，和 =>左端的运算子，都会被视为已纳入引号了：

这些是和这些一样的
-------------------
  北极星书库||http://www.ebook007.com

    $foo{line}              $foo{"line"}
    bar => stuff            "bar" => stuff

一个区块末端的分号可有可无，一个序列的最後一个逗号亦同。良好的写作风格

    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);

我如何暂时滤掉警告讯息？

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

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

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

什麽是一个扩充 (extension)？

一种从 Perl呼叫编译好的

为何 Perl运算子的优先顺序和 C的不一样？

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

一个常犯的错误像是：

    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)即可。参看

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

。

我如何创造出一个模组 (module)？

一个模组就是一个放在同名档案里的包裹(package)。例如，Hello::There模组会 也会很有帮助。如果你正在写一个 C 或是混合了 C及 Perl 的模组，那麽你就该读 。

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

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

    use strict;

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

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

#但是小心两位数版本编号可能造成的影响。 $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)？

也有。

我如何辨别一个变数是否被污染了(tainted)？

没有将任何程序交给讯号)：

    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)是啥？

。

闭包 (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) 将

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

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

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

何谓变数自杀而我又该如何防止它？

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

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

    my $f = "foo";
    sub T {
      while ($i++ < 3) { my $f = $f; $f .= "bar"; print $f, "\n" }
    }
    T;
    print "Finally $f\n";

有叁个 ``bar''加进去的 $f 变数应该是一个新的 $f (因为 my $f在每个回圈都应该创造一个新的区域变数)。然而，实际上并非如此。这个臭虫在未来的 perl 版本中将会被修正。

我如何传递/传回一个 {函数，档案把手，阵列，杂凑阵列，方法，和正规表现式}？

除了正规表现式这个特例外，你需要以传参考值的方式传资料给这些物件。参看 里面有参考值的资讯。

传递变数和函数
一般的变数和函数是相当简单的：只要传一个指向现存的匿名变数或函数的参考值 即可：

    func( \$some_scalar );

    func( \$some_array );
    func( [ 1 .. 10 ]   );

    func( \%some_hash   );
    func( { this => 10, that => 20 }   );

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

传递档案把手
)，或是使用旧名 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页的 的资料。

如何传递方法 (methods)
你可以用下面的方法传递一个物件方法给一个函式：

    call_a_lot(10, $some_obj, "methname")
    sub call_a_lot {
        my ($count, $widget, $trick) = @_;
        for (my $i = 0; $i < $count; $i++) {
            $widget->$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() 才是你要找的。

，里面有更详尽的解说。

当同一个范围中有一个文字式变数时，我该如何去撷取同名的动态变数？

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

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

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

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

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

我如何重新定义一个内建函数、运算子或是方法？

为什麽要这麽做？ :-)

如果你要覆盖掉某个内建函数，例如说 open()，那你得将其定义从另一个模组载 入。参考

如果你要覆盖掉一个 Perl运算子，像是 +或 ** ，那你该使用 use 。

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

用 &foo和 foo()的方式来呼叫一个函数有什麽不同？

当你用 &foo的方式呼叫一个函数时，你等於让这个函数撷取你目前 @_里面的值，同时也跳过原型定义 (prototypes)不用。这表式此函数抓到的是你当时的 @_， 而非一个空的 @_！虽然严格讲起来它也不能算是个 bug (但是在

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

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

我如何作一个 switch或 case叙述？

文件里有更详尽的解释。简单来说，因为 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 '跳过使用者自定的类型';

    }

我如何抓到呼叫未定义变数/函数/方法的事件？

提到的AUTOLOAD 方法让你能捕捉对於未定义函数与方法的呼叫。

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

    $SIG{__WARN__} = sub {

        for ( $_[0] ) {

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

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

            warn $_;
        }

    };

为什麽我的程式会找不到放在同一档案中的方法 (method)呢？

一些可能的原因：你用的继承给搞混了、你拼错了该方法的名字，或是物件的类别 ref($object) 来找出 $object这个物件是被归到哪个类别底下。

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

我如何找出目前所在的 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

作者与版权事宜

Copyright (c) 1997 Tom Christiansen and Nathan Torkington. All rights reserved.有关使用 、（转）发行事宜 ，详见

译者：陈彦铭、萧百龄

中译版着作权所有：陈彦铭、萧百龄及两只老虎工作室。 本中译版遵守并使用与原文版相同的使用条款发行。


perlfaq8

perlfaq8 - perl 常问问题集，第八篇

  • 篇名
  • 概述
    • 如何得知使用者正在哪个作业系统下执行我的 perl 程式？
    • 为什麽 exec() 不会传值回来？
    • 如何对 键盘／萤幕／滑鼠 做些花样？
    • 如何向使用者询问密码？
    • 如何对序列埠做读写动作？
    • 如何逆解加密後的密码档案？
    • 如何启动一个背景执行的程序？
    • 如何捕捉 控制字元／讯号？
    • 如何更动 Unix 系统上隐式密码档 (shadow password) 的内容？
    • 如何设定时间和日期？
    • 如何能够针对小於一秒的时间做 sleep() 或 alarm() 的动作呢？
    • 如何测量小於一秒的时间？
    • 如何做 atexit() 或 setjmp()/longjmp() 的动作？（例外处理）
    • 为何我的 sockets 程式在 System V (Solaris) 系统下不能用？「不支援本协定」这个错误讯息又是什麽意思？
    • 如何从 Perl 里呼叫系统中独特的 C 函数？
    • 在哪里可以找引入档来做 ioctl() 或 syscall()？
    • 为何 setuid perl 程式会抱怨关於系统核心的问题？
    • 如何打开对某程式既输入又输出的管道 (pipe)？
    • 为何用 system() 却得不到一个指令的输出呢？
    • 如何补捉外部指令的 STDERR？
    • 为何当管道开启失败时 open() 不会传回错误讯息？
    • 在输出值是空的情境里使用反向引号有何不对？
    • 如何不经过 shell 处理来呼叫反向引号？
    • 为何给了 EOF（Unix 上是 ^D，MS-DOS 上是 ^Z）後我的程式就不能从 STDIN 读取东西了呢？
    • 如何把 shell 程式转成 perl？
    • perl 能处理 telnet 或 ftp 这种双向互动吗？
    • 如何在 Perl 里达到 Expect 的功能？
    • 有没有可能将 perl 的指令列隐藏起来，以躲避像 "ps" 之类的程式？
    • 我在 perl script 里 {更动目录，更改我的使用环境}。为何这些改变在程式执行完後就消失了呢？如何让我做的修改显露出来？
    • 如何关闭一个程序的档案把手而不用等它完成呢？
    • 如何 fork 出一个背景执行 (daemon) 程序？
    • 如何使我的程式和 sh 及 csh 一起执行？
    • 如何得知我是否正在互动模式下执行？
    • 如何让一个缓慢的事件过时？
    • 如何设定 CPU 使用限制？
    • 在 Unix 系统上如何避免产生僵 程序 (zombies)？
    • 如何使用一个 SQL 资料库？
    • 如何让 system() 在收到 control-C 後就离开？
    • 如何开启一个档案但不阻挡其他程序的阅读？
    • 如何安装一个 CPAN 模组？
    • 如何保有一份自己的 模组／程式库 目录？
    • 如何把我的程式所在位置加入 模组／程式库 搜寻路径？
    • 如何在执行时添加目录到自己的引入路径中？
  • 如何从终端机一次抓进一个按键？如果用 POSIX 模组时又该怎麽做？
  • 作者、译者与版权

篇名

perlfaq8 - 系统互动（原文版 Revision: 1.21, Date: 1997/04/24 22:44:19. 中文版 $Revision: 1.1 $, $Date: 1998/03/25 03:19:06 $）

概述

这部份的 Perl 常见问题集涵盖关於与作业系统互动的问题。这包括了程序间通讯 [interprocess communication (IPC)]、使用者介面的控制（键盘、萤幕以及指标 装置），以及几乎所有和资料处理无关的事情。

请阅读特别针对你所使用的作业系统下的 perl 所写的常见问题集和文件（例如， perlvms 、perlplan9，...），以取得 perl 在个别差异方面更详尽的资料。

如何得知使用者正在哪个作业系统下执行我的 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);
    }

然而，因为系统呼叫会自己重新启动，你将会发现如果你用的是「慢的」呼叫，像 < FH>、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 (<GREP>) {
            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()。这是在技术上正确的解决之道。还有一些较不保险的方法：

  1. 试着保存搜寻指标然後去找它，例如：

        $where = tell(LOG);
    seek(LOG, $where, 0);

  2. 如果那样行不通，试着去 seek() 档案的另一部份然後再找回来。

  3. 如果还是行不通，试着 seek() 档案另一个相异的的部份，读点东西，再回去找。

  4. 如果依然不行，放弃使用 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>;    # 将所有从 stdin 来的丢到 socket
    } else {
        print while <$handle>;  # 将所有 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模组，遵循以下步 骤：

  1. 把原始码解开放到一个暂存区域

  2.     perl Makefile.PL

  3.     make

  4.     make test

  5.     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..4) {
        my $got;
        print '给我： ';
        $got = getone();
        print "--> $got\n";
    }
    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() }

作者、译者与版权

Copyright (c) 1997 Tom Christiansen and Nathan Torkington. All rights reserved. 有关使用、（ 转 ）发行事宜，详见 perlfaq

译者：陈彦铭、萧百龄

中译版着作权所有：陈彦铭、萧百龄及两只老虎工作室。本中译版遵守并使用与原 文版相同的使用条款发行。


perlfaq9

perlfaq9 - perl常问问题集，第九篇

  • 篇名
  • 概述
    • 我的 CGI script可在指令列下执行但无法从浏览器执行。您能不能帮我修修看？
    • 如何去除文章中的 HTML标签？
    • 如何萃取 URLs？
    • 如何从 user端上传资料？如何在另一台机器上开一个档案？
    • 如何在 HTML中做 pop-up menu（跳出式选单）？
    • 如何抓 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/23 18:12:06. 中文版 $Revision: 1.13 $, $Date: 1997/07/12 20:44:25 $）

概述

本篇涵盖网路连线、 Internet ，还有几个关於 WWW 的问题。

我的 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出现；再加上一些疏忽，譬如，人们常忘了转换如 <的 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" while m{
        < \s*
          A \s+ HREF \s* = \s* (["']) (.*?) \1
        \s* >
    }gsix;

这个版本并不替相对式写法的 URLs 作调整，也不懂代换 bases【< LINK BASE=``...''>】，或如何处理 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（跳出式选单）？

用 <SELECT> 和 <OPTION>这两个标签。 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 = <MSG>;
    $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/Tom_Christiansen/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系统呼叫】、 $<这个变数，和 Sys::Hostname模组（标准 perl 发行的一部分）的系统上，您可试试这样的做法：

    use Sys::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 套件的一部分）。

   #送信
    use Mail::Internet;
    use Mail::Header;
    #设定使用哪台主机
    $ENV{SMTPHOSTS} = 'mail.frii.com';
    #制作标头
    $header = new Mail::Header;
    $header->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这样的模组则还没听说有人在发展。

作者及版权事宜

Copyright (c) 1997 Tom Christiansen and Nathan Torkington.着作权所有， All rights reserved。有关使用、（转）发行事宜，详见 perlfaq 。

中译版着作权所有：萧百龄及两只老虎工作室。本中译版遵守并使用与原文版相同 的使用条款发行。