- Perl 基础
- Perl - 首页
- Perl - 简介
- Perl - 环境
- Perl - 语法概述
- Perl - 数据类型
- Perl - 变量
- Perl - 标量
- Perl - 数组
- Perl - 哈希表
- Perl - IF...ELSE
- Perl - 循环
- Perl - 运算符
- Perl - 日期与时间
- Perl - 子程序
- Perl - 引用
- Perl - 格式
- Perl - 文件I/O
- Perl - 目录
- Perl - 错误处理
- Perl - 特殊变量
- Perl - 编码规范
- Perl - 正则表达式
- Perl - 发送邮件
- Perl 高级
- Perl - 套接字编程
- Perl - 面向对象
- Perl - 数据库访问
- Perl - CGI 编程
- Perl - 包与模块
- Perl - 进程管理
- Perl - 内嵌文档
- Perl - 函数引用
- Perl 有用资源
- Perl - 问答
- Perl 快速指南
- Perl - 有用资源
- Perl - 讨论
Perl 快速指南
Perl - 简介
Perl 是一种通用的编程语言,最初开发用于文本处理,现在用于各种任务,包括系统管理、Web 开发、网络编程、GUI 开发等等。
什么是 Perl?
Perl 是一种稳定、跨平台的编程语言。
虽然 Perl 官方并非首字母缩写词,但一些人将其称为 **Practical Extraction and Report Language**(实用提取和报告语言)。
它被用于公共和私营部门的关键任务项目。
Perl 是一个 *开源* 软件,其许可证为 *Artistic License* 或 *GNU General Public License (GPL)*。
Perl 由 Larry Wall 创建。
Perl 1.0 于 1987 年发布到 usenet 的 alt.comp.sources。
在撰写本教程时,Perl 的最新版本为 5.16.2。
Perl 被列入 *牛津英语词典*。
《PC Magazine》宣布 Perl 为其 1998 年开发工具类别的技术卓越奖决赛入围者。
Perl 特性
Perl 汲取了其他语言(如 C、awk、sed、sh 和 BASIC 等)的最佳特性。
Perl 的数据库集成接口 DBI 支持第三方数据库,包括 Oracle、Sybase、Postgres、MySQL 等。
Perl 可与 HTML、XML 和其他标记语言一起使用。
Perl 支持 Unicode。
Perl 兼容 Y2K。
Perl 支持过程式编程和面向对象编程。
Perl 通过 XS 或 SWIG 与外部 C/C++ 库进行接口。
Perl 可扩展。从 Comprehensive Perl Archive Network (CPAN) 可获得超过 20,000 个第三方模块。
Perl 解释器可以嵌入到其他系统中。
Perl 和 Web
由于其文本处理能力和快速开发周期,Perl 曾是最流行的 Web 编程语言。
Perl 被广泛称为 "互联网的胶带"。
Perl 可以处理加密的 Web 数据,包括电子商务交易。
Perl 可以嵌入到 Web 服务器中,从而将处理速度提高多达 2000%。
Perl 的 mod_perl 允许 Apache Web 服务器嵌入 Perl 解释器。
Perl 的 DBI 包使 Web 数据库集成变得容易。
Perl 是解释型语言
Perl 是一种解释型语言,这意味着您的代码可以按原样运行,无需编译阶段来创建不可移植的可执行程序。
传统的编译器将程序转换为机器语言。当您运行 Perl 程序时,它首先被编译成字节码,然后在程序运行时转换为机器指令。因此,它与 shell 或 Tcl(它们是**严格**解释的,没有中间表示)并不完全相同。
它也不像大多数版本的 C 或 C++,它们直接编译成与机器相关的格式。它介于两者之间,与 *Python*、*awk* 和 Emacs .elc 文件类似。
Perl - 环境
在开始编写 Perl 程序之前,让我们了解如何设置 Perl 环境。Perl 可用于各种平台:-
- Unix(Solaris、Linux、FreeBSD、AIX、HP/UX、SunOS、IRIX 等)
- Win 9x/NT/2000/
- WinCE
- Macintosh(PPC,68K)
- Solaris(x86,SPARC)
- OpenVMS
- Alpha(7.2 及更高版本)
- Symbian
- Debian GNU/kFreeBSD
- MirOS BSD
- 等等…
您的系统很可能已经安装了 perl。只需在 $ 提示符下尝试以下命令:-
$perl -v
如果您的机器上安装了 perl,那么您将收到类似以下的消息:-
This is perl 5, version 16, subversion 2 (v5.16.2) built for i686-linux Copyright 1987-2012, Larry Wall Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5 source kit. Complete documentation for Perl, including FAQ lists, should be found on this system using "man perl" or "perldoc perl". If you have access to the Internet, point your browser at https://www.perl5.cn/, the Perl Home Page.
如果您的机器上尚未安装 perl,则继续下一节。
获取 Perl 安装程序
最新的源代码、二进制文件、文档、新闻等都可以在 Perl 的官方网站上找到。
**Perl 官方网站** - https://www.perl5.cn/
您可以从以下网站下载 Perl 文档。
**Perl 文档网站** - https://perldoc.perl5.cn
安装 Perl
Perl 发行版适用于各种平台。您只需下载适用于您平台的二进制代码并安装 Perl。
如果您的平台没有提供二进制代码,则需要使用 C 编译器手动编译源代码。编译源代码在您需要的安装功能的选择方面提供了更大的灵活性。
以下是关于在各种平台上安装 Perl 的快速概述。
Unix 和 Linux 安装
以下是关于在 Unix/Linux 机器上安装 Perl 的简单步骤。
打开 Web 浏览器并访问 https://www.perl5.cn/get.html。
点击链接下载适用于 Unix/Linux 的压缩源代码。
下载 **perl-5.x.y.tar.gz** 文件,并在 $ 提示符下执行以下命令。
$tar -xzf perl-5.x.y.tar.gz $cd perl-5.x.y $./Configure -de $make $make test $make install
**注意** - 此处的 $ 是 Unix 提示符,您可以在其中键入命令,因此请确保在键入上述命令时不要键入 $。
这将把 Perl 安装到标准位置 * /usr/local/bin*,其库安装在 * /usr/local/lib/perlXX* 中,其中 XX 是您正在使用的 Perl 版本。
在发出 **make** 命令后,编译源代码需要一段时间。安装完成后,您可以在 $ 提示符下发出 **perl -v** 命令来检查 perl 安装。如果一切正常,它将显示我们上面显示的消息。
Windows 安装
以下是关于在 Windows 机器上安装 Perl 的步骤。
按照在 Windows 上安装 Strawberry Perl 的链接操作 http://strawberryperl.com
下载 32 位或 64 位版本的安装程序。
双击 Windows 资源管理器中的下载文件运行它。这将调出 Perl 安装向导,它非常易于使用。只需接受默认设置,等待安装完成,您就可以开始使用了!
Macintosh 安装
为了构建您自己的 Perl 版本,您需要 'make',它是通常随 Mac OS 安装 DVD 提供的 Apple 开发者工具的一部分。您不需要最新版本的 Xcode(现在是收费的)来安装 make。
以下是关于在 Mac OS X 机器上安装 Perl 的简单步骤。
打开 Web 浏览器并访问 https://www.perl5.cn/get.html。
点击链接下载适用于 Mac OS X 的压缩源代码。
下载 **perl-5.x.y.tar.gz** 文件,并在 $ 提示符下执行以下命令。
$tar -xzf perl-5.x.y.tar.gz $cd perl-5.x.y $./Configure -de $make $make test $make install
这将把 Perl 安装到标准位置 * /usr/local/bin*,其库安装在 * /usr/local/lib/perlXX* 中,其中 XX 是您正在使用的 Perl 版本。
运行 Perl
以下是启动 Perl 的不同方法。
交互式解释器
您可以输入 **perl** 并从命令行启动它,立即在交互式解释器中开始编码。您可以从 Unix、DOS 或任何其他提供命令行解释器或 shell 窗口的系统执行此操作。
$perl -e <perl code> # Unix/Linux or C:>perl -e <perl code> # Windows/DOS
以下是所有可用的命令行选项列表:-
序号 | 选项和说明 |
---|---|
1 | -d[:debugger] 在调试器下运行程序 |
2 | -Idirectory 指定 @INC/#include 目录 |
3 | -T 启用污染检查 |
4 | -t 启用污染警告 |
5 | -U 允许不安全操作 |
6 | -w 启用许多有用的警告 |
7 | -W 启用所有警告 |
8 | -X 禁用所有警告 |
9 | -e program 运行作为程序发送的 Perl 脚本 |
10 | file 从给定文件运行 Perl 脚本 |
来自命令行的脚本
Perl 脚本是一个文本文件,其中包含 perl 代码,可以通过在命令行上调用解释器来执行您的应用程序,如下所示:-
$perl script.pl # Unix/Linux or C:>perl script.pl # Windows/DOS
集成开发环境
您也可以从图形用户界面 (GUI) 环境运行 Perl。您只需要系统上支持 Perl 的 GUI 应用程序即可。您可以下载 Padre,Perl IDE。如果您熟悉 Eclipse,也可以使用 Eclipse 插件 EPIC - Perl 编辑器和 Eclipse IDE。
在继续下一章之前,请确保您的环境已正确设置并运行良好。如果您无法正确设置环境,则可以寻求系统管理员的帮助。
后续章节中提供的所有示例都在 CentOS 版本的 Linux 上使用 v5.16.2 版本执行。
Perl - 语法概述
Perl 借鉴了许多语言的语法和概念:awk、sed、C、Bourne Shell、Smalltalk、Lisp 甚至英语。但是,这些语言之间存在一些明显的差异。本章旨在让您快速了解 Perl 中预期的语法。
Perl 程序由一系列声明和语句组成,这些声明和语句从上到下执行。循环、子程序和其他控制结构允许你在代码中跳转。每个简单语句都必须以分号 (;) 结尾。
Perl 是一种自由格式的语言:你可以根据自己的喜好来格式化和缩进代码。空格主要用于分隔标记,这与 Python 等语言(其中空格是语法的重要组成部分)或 Fortran 等语言(其中空格无关紧要)不同。
第一个 Perl 程序
交互模式编程
你可以在命令行中使用 Perl 解释器和 -e 选项,这允许你从命令行执行 Perl 语句。让我们在 $ 提示符下尝试如下操作:
$perl -e 'print "Hello World\n"'
执行结果如下:
Hello, world
脚本模式编程
假设你已经在 $ 提示符下,让我们使用 vi 或 vim 编辑器打开一个文本文件 hello.pl,并将以下几行代码放入你的文件中。
#!/usr/bin/perl # This will print "Hello, World" print "Hello, world\n";
这里 /usr/bin/perl 实际上是 perl 解释器的二进制文件。在执行脚本之前,务必更改脚本文件的模式并赋予执行权限,通常设置为 0755 就足够了,最后你可以按如下方式执行上述脚本:
$chmod 0755 hello.pl $./hello.pl
执行结果如下:
Hello, world
你可以根据个人喜好为函数参数使用括号或省略括号。只有在需要澄清优先级问题时才需要它们。以下两个语句产生相同的结果。
print("Hello, world\n"); print "Hello, world\n";
Perl 文件扩展名
Perl 脚本可以在任何普通的纯文本编辑器程序中创建。每种类型的平台都有多个可用的程序。网络上可以下载许多为程序员设计的程序。
按照 Perl 的约定,Perl 文件必须保存为 .pl 或 .PL 文件扩展名,才能被识别为有效的 Perl 脚本。文件名可以包含数字、符号和字母,但不能包含空格。用下划线 (_) 代替空格。
Perl 中的注释
任何编程语言中的注释都是开发人员的朋友。注释可以使程序更易于使用,并且它们会被解释器简单地跳过,而不会影响代码的功能。例如,在上例程序中,以井号 # 开头的行就是注释。
简单来说,Perl 中的注释以井号开头,一直延续到行尾:
# This is a comment in perl
以 = 开头的行被解释为嵌入式文档 (pod) 部分的开头,编译器会忽略所有后续行,直到下一个 =cut。以下是一个示例:
#!/usr/bin/perl # This is a single line comment print "Hello, world\n"; =begin comment This is all part of multiline comment. You can use as many lines as you like These comments will be ignored by the compiler until the next =cut is encountered. =cut
这将产生以下结果:
Hello, world
Perl 中的空格
Perl 程序不关心空格。以下程序可以完美运行:
#!/usr/bin/perl print "Hello, world\n";
但是,如果空格位于引号字符串内,则会按原样打印。例如:
#!/usr/bin/perl # This would print with a line break in the middle print "Hello world\n";
这将产生以下结果:
Hello world
所有类型的空格,例如空格、制表符、换行符等,当它们用在引号之外时,对于解释器来说是等效的。仅包含空格(可能带有注释)的行称为空行,Perl 会完全忽略它。
Perl 中的单引号和双引号
你可以使用双引号或单引号括起文字字符串,如下所示:
#!/usr/bin/perl print "Hello, world\n"; print 'Hello, world\n';
这将产生以下结果:
Hello, world Hello, world\n$
单引号和双引号之间有一个重要的区别。只有双引号内插变量和特殊字符(如换行符 \n),而单引号不内插任何变量或特殊字符。请查看下面的示例,我们使用 $a 作为变量来存储一个值,然后打印该值:
#!/usr/bin/perl $a = 10; print "Value of a = $a\n"; print 'Value of a = $a\n';
这将产生以下结果:
Value of a = 10 Value of a = $a\n$
“Here”文档
你可以非常方便地存储或打印多行文本。你甚至可以在“here”文档中使用变量。以下是一个简单的语法,请仔细检查,在 << 和标识符之间不能有空格。
标识符可以是裸词或一些引用的文本,就像我们在下面使用的 EOF 一样。如果标识符被引用,则你使用的引号类型决定了对“here”文档内文本的处理方式,就像在常规引用中一样。未引用的标识符的工作方式类似于双引号。
#!/usr/bin/perl $a = 10; $var = <<"EOF"; This is the syntax for here document and it will continue until it encounters a EOF in the first line. This is case of double quote so variable value will be interpolated. For example value of a = $a EOF print "$var\n"; $var = <<'EOF'; This is case of single quote so variable value will be interpolated. For example value of a = $a EOF print "$var\n";
这将产生以下结果:
This is the syntax for here document and it will continue until it encounters a EOF in the first line. This is case of double quote so variable value will be interpolated. For example value of a = 10 This is case of single quote so variable value will be interpolated. For example value of a = $a
转义字符
Perl 使用反斜杠 (\) 字符来转义任何可能干扰我们代码的字符。让我们来看一个示例,我们想要打印双引号和 $ 符号:
#!/usr/bin/perl $result = "This is \"number\""; print "$result\n"; print "\$result\n";
这将产生以下结果:
This is "number" $result
Perl 标识符
Perl 标识符是用于标识变量、函数、类、模块或其他对象的名称。Perl 变量名以 $、@ 或 % 开头,后面可以跟零个或多个字母、下划线和数字 (0 到 9)。
Perl 不允许在标识符中使用 @、$ 和 % 等标点符号。Perl 是一种区分大小写的编程语言。因此,$Manpower 和 $manpower 在 Perl 中是两个不同的标识符。
Perl - 数据类型
Perl 是一种弱类型语言,在程序中使用数据时不需要指定数据的类型。Perl 解释器将根据数据的上下文本身选择类型。
Perl 有三种基本数据类型:标量、标量数组和标量哈希(也称为关联数组)。以下是关于这些数据类型的简要说明。
序号 | 类型与说明 |
---|---|
1 | 标量 标量是简单的变量。它们前面带有美元符号 ($) 。标量可以是数字、字符串或引用。引用实际上是变量的地址,我们将在接下来的章节中看到。 |
2 | 数组 数组是有序的标量列表,你可以使用从 0 开始的数字索引访问它们。它们前面带有“at”符号 (@)。 |
3 | 哈希 哈希是无序的键/值对集合,你可以使用键作为下标来访问它们。它们前面带有百分号 (%)。 |
数值字面量
Perl 在内部将所有数字存储为带符号整数或双精度浮点值。数值字面量可以采用以下任何浮点或整数格式指定:
类型 | 值 |
---|---|
整数 | 1234 |
负整数 | -100 |
浮点数 | 2000 |
科学计数法 | 16.12E14 |
十六进制 | 0xffff |
八进制 | 0577 |
字符串字面量
字符串是字符序列。它们通常是字母数字值,由单引号 (') 或双引号 (") 括起来。它们的工作方式与 UNIX shell 引号非常相似,你可以在其中使用单引号字符串和双引号字符串。
双引号字符串字面量允许变量内插,而单引号字符串不允许。某些字符在前面带有反斜杠时具有特殊含义,它们用于表示换行符 (\n) 或制表符 (\t) 等。
你可以在双引号字符串中直接嵌入换行符或以下任何转义序列:
转义序列 | 含义 |
---|---|
\\ | 反斜杠 |
\' | 单引号 |
\" | 双引号 |
\a | 警告或铃声 |
\b | 退格 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\0nn | 创建八进制格式的数字 |
\xnn | 创建十六进制格式的数字 |
\cX | 控制字符,x 可以是任何字符 |
\u | 强制下一个字符为大写 |
\l | 强制下一个字符为小写 |
\U | 强制所有后续字符为大写 |
\L | 强制所有后续字符为小写 |
\Q | 对所有后续非字母数字字符进行反斜杠转义 |
\E | 结束 \U、\L 或 \Q |
示例
让我们再次看看字符串在单引号和双引号中的行为。在这里,我们将使用上表中提到的字符串转义符,并将使用标量变量来赋值字符串。
#!/usr/bin/perl # This is case of interpolation. $str = "Welcome to \ntutorialspoint.com!"; print "$str\n"; # This is case of non-interpolation. $str = 'Welcome to \ntutorialspoint.com!'; print "$str\n"; # Only W will become upper case. $str = "\uwelcome to tutorialspoint.com!"; print "$str\n"; # Whole line will become capital. $str = "\UWelcome to tutorialspoint.com!"; print "$str\n"; # A portion of line will become capital. $str = "Welcome to \Ututorialspoint\E.com!"; print "$str\n"; # Backsalash non alpha-numeric including spaces. $str = "\QWelcome to tutorialspoint's family"; print "$str\n";
这将产生以下结果:
Welcome to tutorialspoint.com! Welcome to \ntutorialspoint.com! Welcome to tutorialspoint.com! WELCOME TO TUTORIALSPOINT.COM! Welcome to TUTORIALSPOINT.com! Welcome\ to\ tutorialspoint\'s\ family
Perl - 变量
变量是保留的内存位置,用于存储值。这意味着当你创建变量时,你会保留一些内存空间。
根据变量的数据类型,解释器会分配内存并决定可以在保留的内存中存储什么内容。因此,通过为变量分配不同的数据类型,你可以在这些变量中存储整数、小数或字符串。
我们已经了解到 Perl 具有以下三种基本数据类型:
- 标量
- 数组
- 哈希
相应地,我们将在 Perl 中使用三种类型的变量。标量变量前面将带有美元符号 ($) ,它可以存储数字、字符串或引用。数组变量前面将带有 @ 符号,它将存储有序的标量列表。最后,哈希变量前面将带有 % 符号,并将用于存储键/值对的集合。
Perl 在单独的命名空间中维护每种变量类型。因此,你可以不必担心冲突,可以为标量变量、数组或哈希使用相同的名称。这意味着 $foo 和 @foo 是两个不同的变量。
创建变量
Perl 变量不必显式声明即可保留内存空间。当你为变量赋值时,声明会自动发生。等号 (=) 用于为变量赋值。
请注意,如果我们在程序中使用 use strict 语句,则必须在使用变量之前声明变量。
= 运算符左侧的操作数是变量的名称,= 运算符右侧的操作数是存储在变量中的值。例如:
$age = 25; # An integer assignment $name = "John Paul"; # A string $salary = 1445.50; # A floating point
这里 25、“John Paul”和 1445.50 分别是分配给$age、$name和$salary变量的值。我们很快就会看到如何为数组和哈希赋值。
标量变量
标量是单个数据单元。该数据可能是整数、浮点数、字符、字符串、段落或整个网页。简单地说,它可以是任何东西,但只能是一件东西。
以下是用标量变量的简单示例:
#!/usr/bin/perl $age = 25; # An integer assignment $name = "John Paul"; # A string $salary = 1445.50; # A floating point print "Age = $age\n"; print "Name = $name\n"; print "Salary = $salary\n";
这将产生以下结果:
Age = 25 Name = John Paul Salary = 1445.5
数组变量
数组是一个变量,它存储有序的标量值列表。数组变量前面带有“at”符号 (@)。要引用数组的单个元素,你将使用美元符号 ($) ,后跟变量名称,然后是方括号中的元素索引。
以下是用数组变量的简单示例:
#!/usr/bin/perl @ages = (25, 30, 40); @names = ("John Paul", "Lisa", "Kumar"); print "\$ages[0] = $ages[0]\n"; print "\$ages[1] = $ages[1]\n"; print "\$ages[2] = $ages[2]\n"; print "\$names[0] = $names[0]\n"; print "\$names[1] = $names[1]\n"; print "\$names[2] = $names[2]\n";
在这里,我们在 $ 符号之前使用了转义符 (\),只是为了打印它。否则 Perl 会将其理解为变量并打印其值。执行后,将产生以下结果:
$ages[0] = 25 $ages[1] = 30 $ages[2] = 40 $names[0] = John Paul $names[1] = Lisa $names[2] = Kumar
哈希变量
哈希(Hash)是一组键值对。哈希变量以百分号 (%) 开头。要引用哈希的单个元素,可以使用哈希变量名称,后跟花括号中与值关联的“键”。
这是一个使用哈希变量的简单示例:
#!/usr/bin/perl %data = ('John Paul', 45, 'Lisa', 30, 'Kumar', 40); print "\$data{'John Paul'} = $data{'John Paul'}\n"; print "\$data{'Lisa'} = $data{'Lisa'}\n"; print "\$data{'Kumar'} = $data{'Kumar'}\n";
这将产生以下结果:
$data{'John Paul'} = 45 $data{'Lisa'} = 30 $data{'Kumar'} = 40
变量上下文
Perl 根据上下文(即变量使用的场景)以不同的方式处理相同的变量。让我们检查以下示例:
#!/usr/bin/perl @names = ('John Paul', 'Lisa', 'Kumar'); @copy = @names; $size = @names; print "Given names are : @copy\n"; print "Number of names are : $size\n";
这将产生以下结果:
Given names are : John Paul Lisa Kumar Number of names are : 3
这里 @names 是一个数组,它在两种不同的上下文中使用。首先,我们将其复制到另一个数组(即列表),因此它返回所有元素,假设上下文是列表上下文。接下来,我们使用相同的数组并尝试将此数组存储在一个标量中,因此在这种情况下,它只返回此数组中的元素数量,假设上下文是标量上下文。下表列出了各种上下文:
序号 | 上下文和描述 |
---|---|
1 | 标量 赋值给标量变量会以标量上下文评估右侧。 |
2 | 列表 (List) 赋值给数组或哈希会以列表上下文评估右侧。 |
3 | 布尔值 (Boolean) 布尔上下文只是任何评估表达式以查看其真假的地方。 |
4 | 空值 (Void) 此上下文不仅不关心返回值是什么,甚至也不想要返回值。 |
5 | 插值 (Interpolative) 此上下文仅发生在引号内或类似引号的功能中。 |
Perl - 标量
标量是单个数据单元。该数据可能是整数、浮点数、字符、字符串、段落或整个网页。
以下是用标量变量的简单示例:
#!/usr/bin/perl $age = 25; # An integer assignment $name = "John Paul"; # A string $salary = 1445.50; # A floating point print "Age = $age\n"; print "Name = $name\n"; print "Salary = $salary\n";
这将产生以下结果:
Age = 25 Name = John Paul Salary = 1445.5
数值标量 (Numeric Scalars)
标量通常是数字或字符串。以下示例演示了各种类型数值标量的用法:
#!/usr/bin/perl $integer = 200; $negative = -300; $floating = 200.340; $bigfloat = -1.2E-23; # 377 octal, same as 255 decimal $octal = 0377; # FF hex, also 255 decimal $hexa = 0xff; print "integer = $integer\n"; print "negative = $negative\n"; print "floating = $floating\n"; print "bigfloat = $bigfloat\n"; print "octal = $octal\n"; print "hexa = $hexa\n";
这将产生以下结果:
integer = 200 negative = -300 floating = 200.34 bigfloat = -1.2e-23 octal = 255 hexa = 255
字符串标量 (String Scalars)
以下示例演示了各种类型字符串标量的用法。请注意单引号字符串和双引号字符串之间的区别:
#!/usr/bin/perl $var = "This is string scalar!"; $quote = 'I m inside single quote - $var'; $double = "This is inside single quote - $var"; $escape = "This example of escape -\tHello, World!"; print "var = $var\n"; print "quote = $quote\n"; print "double = $double\n"; print "escape = $escape\n";
这将产生以下结果:
var = This is string scalar! quote = I m inside single quote - $var double = This is inside single quote - This is string scalar! escape = This example of escape - Hello, World
标量运算 (Scalar Operations)
您将在单独的章节中看到Perl中可用各种运算符的详细信息,但在这里我们将列出一些数值和字符串运算。
#!/usr/bin/perl $str = "hello" . "world"; # Concatenates strings. $num = 5 + 10; # adds two numbers. $mul = 4 * 5; # multiplies two numbers. $mix = $str . $num; # concatenates string and number. print "str = $str\n"; print "num = $num\n"; print "mix = $mix\n";
这将产生以下结果:
str = helloworld num = 15 mul = 20 mix = helloworld15
多行字符串 (Multiline Strings)
如果要将多行字符串引入程序,可以使用标准单引号,如下所示:
#!/usr/bin/perl $string = 'This is a multiline string'; print "$string\n";
这将产生以下结果:
This is a multiline string
您也可以使用“here”文档语法来存储或打印多行,如下所示:
#!/usr/bin/perl print <<EOF; This is a multiline string EOF
这也会产生相同的结果:
This is a multiline string
V字符串 (V-Strings)
形式为 v1.20.300.4000 的字面量被解析为由具有指定序数的字符组成的字符串。此形式称为 v 字符串。
v 字符串提供了一种替代且更易读的构造字符串的方法,而不是使用不太易读的插值形式“\x{1}\x{14}\x{12c}\x{fa0}”。
它们是以 v 开头,后跟一个或多个点分隔元素的任何字面量。例如:
#!/usr/bin/perl $smile = v9786; $foo = v102.111.111; $martin = v77.97.114.116.105.110; print "smile = $smile\n"; print "foo = $foo\n"; print "martin = $martin\n";
这也会产生相同的结果:
smile = ☺ foo = foo martin = Martin Wide character in print at main.pl line 7.
特殊字面量 (Special Literals)
到目前为止,您一定对字符串标量及其连接和插值操作有了一些了解。让我告诉您三个特殊字面量 __FILE__、__LINE__ 和 __PACKAGE__,它们分别代表程序中该点的当前文件名、行号和包名。
它们只能用作单独的标记,不会被插值到字符串中。检查以下示例:
#!/usr/bin/perl print "File name ". __FILE__ . "\n"; print "Line Number " . __LINE__ ."\n"; print "Package " . __PACKAGE__ ."\n"; # they can not be interpolated print "__FILE__ __LINE__ __PACKAGE__\n";
这将产生以下结果:
File name hello.pl Line Number 4 Package main __FILE__ __LINE__ __PACKAGE__
Perl - 数组
数组是一个变量,它存储有序的标量值列表。数组变量前面带有“at”符号 (@)。要引用数组的单个元素,你将使用美元符号 ($) ,后跟变量名称,然后是方括号中的元素索引。
这是一个使用数组变量的简单示例:
#!/usr/bin/perl @ages = (25, 30, 40); @names = ("John Paul", "Lisa", "Kumar"); print "\$ages[0] = $ages[0]\n"; print "\$ages[1] = $ages[1]\n"; print "\$ages[2] = $ages[2]\n"; print "\$names[0] = $names[0]\n"; print "\$names[1] = $names[1]\n"; print "\$names[2] = $names[2]\n";
在这里,我们在 $ 符号之前使用了转义符 (\),只是为了打印它。否则Perl会将其理解为变量并打印其值。执行后,将产生以下结果:
$ages[0] = 25 $ages[1] = 30 $ages[2] = 40 $names[0] = John Paul $names[1] = Lisa $names[2] = Kumar
在Perl中,列表和数组术语经常被互换使用。但是列表是数据,数组是变量。
数组创建 (Array Creation)
数组变量以 @ 符号开头,并使用圆括号或 qw 运算符填充。例如:
@array = (1, 2, 'Hello'); @array = qw/This is an array/;
第二行使用 qw// 运算符,它返回字符串列表,用空格分隔分隔符字符串。在此示例中,这导致一个四元素数组;第一个元素是 'this',最后一个(第四个)是 'array'。这意味着您可以使用不同的行,如下所示:
@days = qw/Monday Tuesday ... Sunday/;
您还可以通过分别分配每个值来填充数组,如下所示:
$array[0] = 'Monday'; ... $array[6] = 'Sunday';
访问数组元素 (Accessing Array Elements)
访问数组中的单个元素时,必须在变量前加上美元符号 ($) ,然后在变量名称后方方括号内附加元素索引。例如:
#!/usr/bin/perl @days = qw/Mon Tue Wed Thu Fri Sat Sun/; print "$days[0]\n"; print "$days[1]\n"; print "$days[2]\n"; print "$days[6]\n"; print "$days[-1]\n"; print "$days[-7]\n";
这将产生以下结果:
Mon Tue Wed Sun Sun Mon
数组索引从零开始,因此要访问第一个元素,您需要给出 0 作为索引。您也可以给出负索引,在这种情况下,您将从数组的末尾而不是开头选择元素。这意味着:
print $days[-1]; # outputs Sun print $days[-7]; # outputs Mon
连续编号数组 (Sequential Number Arrays)
Perl 为连续数字和字母提供了快捷方式。例如,不用在计算到 100 时键入每个元素,我们可以执行以下操作:
#!/usr/bin/perl @var_10 = (1..10); @var_20 = (10..20); @var_abc = (a..z); print "@var_10\n"; # Prints number from 1 to 10 print "@var_20\n"; # Prints number from 10 to 20 print "@var_abc\n"; # Prints number from a to z
这里双点 (..) 称为范围运算符。这将产生以下结果:
1 2 3 4 5 6 7 8 9 10 10 11 12 13 14 15 16 17 18 19 20 a b c d e f g h i j k l m n o p q r s t u v w x y z
数组大小 (Array Size)
可以使用数组上的标量上下文来确定数组的大小——返回值将是数组中的元素个数:
@array = (1,2,3); print "Size: ",scalar @array,"\n";
返回的值始终是数组的物理大小,而不是有效元素的数量。您可以使用此片段演示这一点以及标量 @array 和 $#array 之间的区别,如下所示:
#!/usr/bin/perl @array = (1,2,3); $array[50] = 4; $size = @array; $max_index = $#array; print "Size: $size\n"; print "Max Index: $max_index\n";
这将产生以下结果:
Size: 51 Max Index: 50
数组中只有四个包含信息的元素,但数组长度为 51 个元素,最高索引为 50。
在数组中添加和删除元素 (Adding and Removing Elements in Array)
Perl 提供了许多有用的函数来在数组中添加和删除元素。您可能想知道什么是函数?到目前为止,您已使用print函数来打印各种值。类似地,还有各种其他函数,有时称为子例程,可用于各种其他功能。
序号 | 类型与说明 |
---|---|
1 | push @ARRAY, LIST 将列表的值推送到数组的末尾。 |
2 | pop @ARRAY 弹出并返回数组的最后一个值。 |
3 | shift @ARRAY 将数组的第一个值移出并返回它,使数组缩短 1 并将所有内容向下移动。 |
4 | unshift @ARRAY, LIST 将列表添加到数组的前面,并返回新数组中的元素数量。 |
#!/usr/bin/perl # create a simple array @coins = ("Quarter","Dime","Nickel"); print "1. \@coins = @coins\n"; # add one element at the end of the array push(@coins, "Penny"); print "2. \@coins = @coins\n"; # add one element at the beginning of the array unshift(@coins, "Dollar"); print "3. \@coins = @coins\n"; # remove one element from the last of the array. pop(@coins); print "4. \@coins = @coins\n"; # remove one element from the beginning of the array. shift(@coins); print "5. \@coins = @coins\n";
这将产生以下结果:
1. @coins = Quarter Dime Nickel 2. @coins = Quarter Dime Nickel Penny 3. @coins = Dollar Quarter Dime Nickel Penny 4. @coins = Dollar Quarter Dime Nickel 5. @coins = Quarter Dime Nickel
切片数组元素 (Slicing Array Elements)
您还可以从数组中提取“切片”——也就是说,您可以按顺序从数组中选择多个项目以产生另一个数组。
#!/usr/bin/perl @days = qw/Mon Tue Wed Thu Fri Sat Sun/; @weekdays = @days[3,4,5]; print "@weekdays\n";
这将产生以下结果:
Thu Fri Sat
切片的规范必须包含一个有效的索引列表,无论是正数还是负数,每个索引之间用逗号分隔。为了速度,您也可以使用..范围运算符:
#!/usr/bin/perl @days = qw/Mon Tue Wed Thu Fri Sat Sun/; @weekdays = @days[3..5]; print "@weekdays\n";
这将产生以下结果:
Thu Fri Sat
替换数组元素 (Replacing Array Elements)
现在我们将介绍另一个名为splice()的函数,其语法如下:
splice @ARRAY, OFFSET [ , LENGTH [ , LIST ] ]
此函数将删除由 OFFSET 和 LENGTH 指定的 @ARRAY 的元素,如果指定,则用 LIST 替换它们。最后,它返回从数组中删除的元素。以下是一个例子:
#!/usr/bin/perl @nums = (1..20); print "Before - @nums\n"; splice(@nums, 5, 5, 21..25); print "After - @nums\n";
这将产生以下结果:
Before - 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 After - 1 2 3 4 5 21 22 23 24 25 11 12 13 14 15 16 17 18 19 20
在这里,实际替换从第六个数开始,然后用数字 21、22、23、24 和 25 替换从 6 到 10 的五个元素。
将字符串转换为数组 (Transform Strings to Arrays)
让我们来看另一个名为split()的函数,其语法如下:
split [ PATTERN [ , EXPR [ , LIMIT ] ] ]
此函数将字符串拆分为字符串数组,并返回它。如果指定了 LIMIT,则最多拆分为该数量的字段。如果省略 PATTERN,则在空格处拆分。以下是一个例子:
#!/usr/bin/perl # define Strings $var_string = "Rain-Drops-On-Roses-And-Whiskers-On-Kittens"; $var_names = "Larry,David,Roger,Ken,Michael,Tom"; # transform above strings into arrays. @string = split('-', $var_string); @names = split(',', $var_names); print "$string[3]\n"; # This will print Roses print "$names[4]\n"; # This will print Michael
这将产生以下结果:
Roses Michael
将数组转换为字符串 (Transform Arrays to Strings)
我们可以使用join()函数重新连接数组元素并形成一个长的标量字符串。此函数具有以下语法:
join EXPR, LIST
此函数将 LIST 的单独字符串连接成一个字符串,字段由 EXPR 的值分隔,并返回该字符串。以下是一个例子:
#!/usr/bin/perl # define Strings $var_string = "Rain-Drops-On-Roses-And-Whiskers-On-Kittens"; $var_names = "Larry,David,Roger,Ken,Michael,Tom"; # transform above strings into arrays. @string = split('-', $var_string); @names = split(',', $var_names); $string1 = join( '-', @string ); $string2 = join( ',', @names ); print "$string1\n"; print "$string2\n";
这将产生以下结果:
Rain-Drops-On-Roses-And-Whiskers-On-Kittens Larry,David,Roger,Ken,Michael,Tom
排序数组 (Sorting Arrays)
sort()函数根据 ASCII 数字标准对数组的每个元素进行排序。此函数具有以下语法:
sort [ SUBROUTINE ] LIST
此函数对 LIST 进行排序并返回已排序的数组值。如果指定了 SUBROUTINE,则在排序元素时会应用 SUBTROUTINE 中的指定逻辑。
#!/usr/bin/perl # define an array @foods = qw(pizza steak chicken burgers); print "Before: @foods\n"; # sort this array @foods = sort(@foods); print "After: @foods\n";
这将产生以下结果:
Before: pizza steak chicken burgers After: burgers chicken pizza steak
请注意,排序是根据单词的 ASCII 数字值执行的。因此,最好的方法是首先将数组的每个元素转换为小写字母,然后执行排序函数。
合并数组 (Merging Arrays)
因为数组只是一系列用逗号分隔的值,所以您可以将它们组合在一起,如下所示:
#!/usr/bin/perl @numbers = (1,3,(4,5,6)); print "numbers = @numbers\n";
这将产生以下结果:
numbers = 1 3 4 5 6
嵌入式数组只是成为主数组的一部分,如下所示:
#!/usr/bin/perl @odd = (1,3,5); @even = (2, 4, 6); @numbers = (@odd, @even); print "numbers = @numbers\n";
这将产生以下结果:
numbers = 1 3 5 2 4 6
从列表中选择元素 (Selecting Elements from Lists)
列表表示法与数组的表示法相同。您可以通过将方括号附加到列表并提供一个或多个索引来从数组中提取元素:
#!/usr/bin/perl $var = (5,4,3,2,1)[4]; print "value of var = $var\n"
这将产生以下结果:
value of var = 1
类似地,我们可以提取切片,但不需要前导 @ 字符:
#!/usr/bin/perl @list = (5,4,3,2,1)[1..3]; print "Value of list = @list\n";
这将产生以下结果:
Value of list = 4 3 2
Perl - 哈希表
哈希是一组键值对。哈希变量以百分号 (%) 开头。要引用哈希的单个元素,可以使用哈希变量名称,前面加"$"符号,后跟花括号中与值关联的“键”。
这是一个使用哈希变量的简单示例:
#!/usr/bin/perl %data = ('John Paul', 45, 'Lisa', 30, 'Kumar', 40); print "\$data{'John Paul'} = $data{'John Paul'}\n"; print "\$data{'Lisa'} = $data{'Lisa'}\n"; print "\$data{'Kumar'} = $data{'Kumar'}\n";
这将产生以下结果:
$data{'John Paul'} = 45 $data{'Lisa'} = 30 $data{'Kumar'} = 40
创建哈希 (Creating Hashes)
哈希可以通过以下两种方式之一创建。在第一种方法中,您可以逐个将值分配给命名键:
$data{'John Paul'} = 45; $data{'Lisa'} = 30; $data{'Kumar'} = 40;
在第二种情况下,您使用列表,该列表通过从列表中获取单个对来转换:对的第一个元素用作键,第二个元素用作值。例如:
%data = ('John Paul', 45, 'Lisa', 30, 'Kumar', 40);
为清晰起见,您可以使用 => 作为 , 的别名,以指示键值对,如下所示:
%data = ('John Paul' => 45, 'Lisa' => 30, 'Kumar' => 40);
这是上述形式的另一种变体,请看一看,这里所有键都以连字符 (-) 开头,并且不需要在其周围使用引号:
%data = (-JohnPaul => 45, -Lisa => 30, -Kumar => 40);
但需要注意的是,这种形式的哈希形成使用的是单个单词(即没有空格的键),如果您以这种方式构建哈希,则只能使用连字符访问键,如下所示。
$val = %data{-JohnPaul} $val = %data{-Lisa}
访问哈希元素 (Accessing Hash Elements)
访问哈希中的单个元素时,必须在变量前加上美元符号 ($) ,然后在变量名称后方括号内附加元素键。例如:
#!/usr/bin/perl %data = ('John Paul' => 45, 'Lisa' => 30, 'Kumar' => 40); print "$data{'John Paul'}\n"; print "$data{'Lisa'}\n"; print "$data{'Kumar'}\n";
这将产生以下结果:
45 30 40
提取切片 (Extracting Slices)
您可以提取哈希的切片,就像您可以从数组中提取切片一样。您需要使用 @ 前缀作为变量来存储返回值,因为它们将是值列表:
#!/uer/bin/perl %data = (-JohnPaul => 45, -Lisa => 30, -Kumar => 40); @array = @data{-JohnPaul, -Lisa}; print "Array : @array\n";
这将产生以下结果:
Array : 45 30
提取键和值 (Extracting Keys and Values)
您可以使用keys函数获取哈希的所有键的列表,该函数具有以下语法:
keys %HASH
此函数返回命名哈希的所有键的数组。以下是一个例子:
#!/usr/bin/perl %data = ('John Paul' => 45, 'Lisa' => 30, 'Kumar' => 40); @names = keys %data; print "$names[0]\n"; print "$names[1]\n"; print "$names[2]\n";
这将产生以下结果:
Lisa John Paul Kumar
类似地,您可以使用values函数获取所有值的列表。此函数具有以下语法:
values %HASH
此函数返回一个普通数组,其中包含命名哈希的所有值。以下是一个例子:
#!/usr/bin/perl %data = ('John Paul' => 45, 'Lisa' => 30, 'Kumar' => 40); @ages = values %data; print "$ages[0]\n"; print "$ages[1]\n"; print "$ages[2]\n";
这将产生以下结果:
30 45 40
检查是否存在 (Checking for Existence)
如果您尝试从不存在的哈希中访问键值对,通常会得到未定义的值,如果您启用了警告,则会在运行时生成警告。您可以使用exists函数来解决此问题,如果命名键存在,无论其值是什么,此函数都返回 true:
#!/usr/bin/perl %data = ('John Paul' => 45, 'Lisa' => 30, 'Kumar' => 40); if( exists($data{'Lisa'} ) ) { print "Lisa is $data{'Lisa'} years old\n"; } else { print "I don't know age of Lisa\n"; }
这里我们介绍了 IF...ELSE 语句,我们将在单独的章节中学习它。目前,你只需要假设只有当给定的条件为真时才会执行if( condition )部分,否则将执行else部分。因此,当我们执行上述程序时,它会产生以下结果,因为这里给定的条件exists($data{'Lisa'}返回 true −
Lisa is 30 years old
获取哈希大小
你可以通过在 keys 或 values 上使用标量上下文来获取哈希的大小,也就是元素的数量。简单来说,你首先需要获取 keys 或 values 的数组,然后就可以按照如下方式获取数组的大小:
#!/usr/bin/perl %data = ('John Paul' => 45, 'Lisa' => 30, 'Kumar' => 40); @keys = keys %data; $size = @keys; print "1 - Hash size: is $size\n"; @values = values %data; $size = @values; print "2 - Hash size: is $size\n";
这将产生以下结果:
1 - Hash size: is 3 2 - Hash size: is 3
在哈希中添加和删除元素
使用简单的赋值运算符,一行代码就可以添加新的键值对。但是要从哈希中删除元素,需要使用delete函数,如下例所示:
#!/usr/bin/perl %data = ('John Paul' => 45, 'Lisa' => 30, 'Kumar' => 40); @keys = keys %data; $size = @keys; print "1 - Hash size: is $size\n"; # adding an element to the hash; $data{'Ali'} = 55; @keys = keys %data; $size = @keys; print "2 - Hash size: is $size\n"; # delete the same element from the hash; delete $data{'Ali'}; @keys = keys %data; $size = @keys; print "3 - Hash size: is $size\n";
这将产生以下结果:
1 - Hash size: is 3 2 - Hash size: is 4 3 - Hash size: is 3
Perl 条件语句 - IF...ELSE
Perl 条件语句有助于决策,这需要程序员指定一个或多个条件由程序进行评估或测试,以及如果条件确定为真则要执行的语句,以及可选地,如果条件确定为假则要执行的其他语句。
以下是大多数编程语言中常见的典型决策结构的一般形式:
数字 0,字符串 '0' 和 "",空列表 () 和 undef 在布尔上下文中都为false,所有其他值都为true。使用!或not对真值取反会返回一个特殊的假值。
Perl 编程语言提供以下类型的条件语句。
序号 | 语句 & 说明 |
---|---|
1 | if 语句
一个if 语句由一个布尔表达式后跟一个或多个语句组成。 |
2 | if...else 语句
一个if 语句可以后跟一个可选的else 语句。 |
3 | if...elsif...else 语句
一个if 语句可以后跟一个可选的elsif 语句,然后是一个可选的else 语句。 |
4 | unless 语句
一个unless 语句由一个布尔表达式后跟一个或多个语句组成。 |
5 | unless...else 语句
一个unless 语句可以后跟一个可选的else 语句。 |
6 | unless...elsif..else 语句
一个unless 语句可以后跟一个可选的elsif 语句,然后是一个可选的else 语句。 |
7 | switch 语句
使用最新版本的 Perl,你可以使用switch语句,它允许以简单的方式将变量值与各种条件进行比较。 |
?: 运算符
让我们检查一下条件运算符 ?:,它可以用来替换if...else语句。它具有以下一般形式:
Exp1 ? Exp2 : Exp3;
其中 Exp1、Exp2 和 Exp3 是表达式。注意冒号的使用和位置。
?: 表达式的值是这样确定的:Exp1 被评估。如果为真,则评估 Exp2 并成为整个 ?: 表达式的值。如果 Exp1 为假,则评估 Exp3,其值成为表达式的值。下面是一个使用此运算符的简单示例:
#!/usr/local/bin/perl $name = "Ali"; $age = 10; $status = ($age > 60 )? "A senior citizen" : "Not a senior citizen"; print "$name is - $status\n";
这将产生以下结果:
Ali is - Not a senior citizen
Perl - 循环
你可能需要多次执行一段代码的情况。一般来说,语句是顺序执行的:函数中的第一个语句首先执行,然后是第二个,依此类推。
编程语言提供各种控制结构,允许更复杂的执行路径。
循环语句允许我们多次执行一个语句或一组语句,以下是大多数编程语言中循环语句的一般形式:
Perl 编程语言提供以下类型的循环来处理循环需求。
序号 | 循环类型 & 说明 |
---|---|
1 | while 循环
当给定条件为真时,重复执行一个语句或一组语句。它在执行循环体之前测试条件。 |
2 | until 循环
重复执行一个语句或一组语句,直到给定条件变为真。它在执行循环体之前测试条件。 |
3 | for 循环
多次执行一系列语句,并缩写管理循环变量的代码。 |
4 | foreach 循环
foreach 循环迭代一个普通的列表值,并将变量 VAR 设置为依次为列表中的每个元素。 |
5 | do...while 循环
类似于 while 语句,只是它在循环体末尾测试条件 |
6 | 嵌套循环
你可以在任何其他 while、for 或 do..while 循环内使用一个或多个循环。 |
循环控制语句
循环控制语句改变执行的正常顺序。当执行离开作用域时,在该作用域中创建的所有自动对象都会被销毁。
Perl 支持以下控制语句。点击以下链接查看其详细信息。
序号 | 控制语句 & 说明 |
---|---|
1 | next 语句
导致循环跳过其主体其余部分,并在重复之前立即重新测试其条件。 |
2 | last 语句
终止循环语句并将执行转移到循环后紧跟的语句。 |
3 | continue 语句
一个 continue 块,它总是在条件即将再次被评估之前执行。 |
4 | redo 语句
redo 命令重新启动循环块,而无需再次评估条件。不会执行 continue 块(如果有的话)。 |
5 | goto 语句
Perl 支持三种形式的 goto 命令:goto label、goto expr 和 goto &name。 |
无限循环
如果条件永远不会变为 false,则循环将变成无限循环。for循环传统上用于此目的。由于构成for循环的三个表达式都不需要,因此你可以通过省略条件表达式来创建一个无限循环。
#!/usr/local/bin/perl for( ; ; ) { printf "This loop will run forever.\n"; }
你可以通过按 Ctrl + C 键来终止上述无限循环。
当条件表达式不存在时,它被认为是 true。你可能有初始化和增量表达式,但作为程序员更常用 for (;;) 结构来表示无限循环。
Perl - 运算符
什么是运算符?
可以使用表达式4 + 5 等于 9给出简单的答案。这里 4 和 5 称为操作数,+ 称为运算符。Perl 语言支持许多运算符类型,但以下是重要且最常用的运算符列表:
- 算术运算符
- 等式运算符
- 逻辑运算符
- 赋值运算符
- 位运算符
- 逻辑运算符
- 类似引号的运算符
- 其他运算符
让我们逐一查看所有运算符。
Perl 算术运算符
假设变量 $a 包含 10,变量 $b 包含 20,则以下是 Perl 算术运算符:
序号 | 运算符 & 说明 |
---|---|
1 | + (加法) 将运算符两侧的值相加 示例 − $a + $b 将得到 30 |
2 | - (减法) 从左操作数中减去右操作数 示例 − $a - $b 将得到 -10 |
3 | * (乘法) 将运算符两侧的值相乘 示例 − $a * $b 将得到 200 |
4 | / (除法) 将左操作数除以右操作数 示例 − $b / $a 将得到 2 |
5 | % (模) 将左操作数除以右操作数并返回余数 示例 − $b % $a 将得到 0 |
6 | ** (指数) 对运算符执行指数(幂)计算 示例 − $a**$b 将得到 10 的 20 次幂 |
Perl 等式运算符
这些也称为关系运算符。假设变量 $a 包含 10,变量 $b 包含 20,那么让我们检查以下数值等式运算符:
序号 | 运算符 & 说明 |
---|---|
1 | == (等于) 检查两个操作数的值是否相等,如果相等则条件变为真。 示例 − ($a == $b) 为假。 |
2 | != (不等于) 检查两个操作数的值是否相等,如果不相等则条件变为真。 示例 − ($a != $b) 为真。 |
3 | <=> 检查两个操作数的值是否相等,并返回 -1、0 或 1,具体取决于左参数在数值上是否小于、等于或大于右参数。 示例 − ($a <=> $b) 返回 -1。 |
4 | > (大于) 检查左操作数的值是否大于右操作数的值,如果大于则条件变为真。 示例 − ($a > $b) 为假。 |
5 | < (小于) 检查左操作数的值是否小于右操作数的值,如果小于则条件变为真。 示例 − ($a < $b) 为真。 |
6 | >= (大于或等于) 检查左操作数的值是否大于或等于右操作数的值,如果大于或等于则条件变为真。 示例 − ($a >= $b) 为假。 |
7 | <= (小于或等于) 检查左操作数的值是否小于或等于右操作数的值,如果小于或等于则条件变为真。 示例 − ($a <= $b) 为真。 |
以下是等式运算符的列表。假设变量 $a 包含 "abc",变量 $b 包含 "xyz",那么让我们检查以下字符串等式运算符:
序号 | 运算符 & 说明 |
---|---|
1 | lt 如果左参数在字符串上小于右参数,则返回 true。 示例 − ($a lt $b) 为真。 |
2 | gt 如果左参数在字符串上大于右参数,则返回 true。 示例 − ($a gt $b) 为假。 |
3 | le 如果左参数在字符串上小于或等于右参数,则返回 true。 示例 − ($a le $b) 为真。 |
4 | ge 如果左参数在字符串上大于或等于右参数,则返回 true。 示例 − ($a ge $b) 为假。 |
5 | eq 如果左参数在字符串上等于右参数,则返回 true。 示例 − ($a eq $b) 为假。 |
6 | ne 如果左参数在字符串上不等于右参数,则返回 true。 示例 − ($a ne $b) 为真。 |
7 | cmp 返回 -1、0 或 1,具体取决于左参数在字符串上是否小于、等于或大于右参数。 示例 − ($a cmp $b) 为 -1。 |
Perl 赋值运算符
假设变量 $a 包含 10,变量 $b 包含 20,则以下是 Perl 中可用的赋值运算符及其用法:
序号 | 运算符 & 说明 |
---|---|
1 | = 简单的赋值运算符,将值从右侧操作数赋值给左侧操作数 示例 − $c = $a + $b 将 $a + $b 的值赋值给 $c |
2 | += 加法和赋值运算符,它将右操作数添加到左操作数,并将结果赋值给左操作数 示例 − $c += $a 等效于 $c = $c + $a |
3 | -= 减法和赋值运算符,它从左操作数中减去右操作数,并将结果赋值给左操作数 示例 − $c -= $a 等效于 $c = $c - $a |
4 | *= 乘法和赋值运算符,它将右操作数乘以左操作数,并将结果赋值给左操作数 示例 − $c *= $a 等效于 $c = $c * $a |
5 | /= 除法并赋值运算符,它将左操作数除以右操作数并将结果赋值给左操作数。 示例 − $c /= $a 等效于 $c = $c / $a |
6 | %= 取模并赋值运算符,它使用两个操作数进行取模运算并将结果赋值给左操作数。 示例 − $c %= $a 等效于 $c = $c % $a |
7 | **= 指数并赋值运算符,对操作数执行指数(幂)计算并将结果赋值给左操作数。 示例 − $c **= $a 等效于 $c = $c ** $a |
Perl 位运算符
位运算符对位进行操作并执行逐位运算。假设 $a = 60;和 $b = 13;现在它们的二进制格式如下:
$a = 0011 1100
$b = 0000 1101
-----------------
$a & $b = 0000 1100
$a | $b = 0011 1101
$a ^ $b = 0011 0001
~$a = 1100 0011
Perl 语言支持以下位运算符,假设 $a = 60;和 $b = 13
序号 | 运算符 & 说明 |
---|---|
1 | & 二进制 AND 运算符:如果位同时存在于两个操作数中,则将其复制到结果中。 示例 − ($a & $b) 将得到 12,即 0000 1100 |
2 | | 二进制 OR 运算符:如果位存在于任一操作数中,则将其复制。 示例 − ($a | $b) 将得到 61,即 0011 1101 |
3 | ^ 二进制 XOR 运算符:如果位在一个操作数中设置,但在另一个操作数中未设置,则将其复制。 示例 − ($a ^ $b) 将得到 49,即 0011 0001 |
4 | ~ 二进制反码运算符:是单目运算符,具有“翻转”位的作用。 示例 − (~$a) 将得到 -61,由于是带符号二进制数,因此其二进制补码形式为 1100 0011。 |
5 | << 二进制左移运算符:左操作数的值向左移动由右操作数指定的位数。 示例 − $a << 2 将得到 240,即 1111 0000 |
6 | >> 二进制右移运算符:左操作数的值向右移动由右操作数指定的位数。 示例 − $a >> 2 将得到 15,即 0000 1111 |
Perl 逻辑运算符
Perl 语言支持以下逻辑运算符。假设变量 $a 为真,变量 $b 为假,则:
序号 | 运算符 & 说明 |
---|---|
1 | and 称为逻辑 AND 运算符。如果两个操作数都为真,则条件为真。 示例 − ($a and $b) 为假。 |
2 | && C 风格的逻辑 AND 运算符:如果位同时存在于两个操作数中,则将其复制到结果中。 示例 − ($a && $b) 为假。 |
3 | or 称为逻辑 OR 运算符。如果两个操作数中任何一个非零,则条件为真。 示例 − ($a or $b) 为真。 |
4 | || C 风格的逻辑 OR 运算符:如果位存在于任一操作数中,则将其复制。 示例 − ($a || $b) 为真。 |
5 | not 称为逻辑 NOT 运算符。用于反转其操作数的逻辑状态。如果条件为真,则逻辑 NOT 运算符将使其为假。 示例 − not($a and $b) 为真。 |
类似引号的运算符
Perl 语言支持以下类似引号的运算符。在下表中,{} 代表您选择的任何一对定界符。
序号 | 运算符 & 说明 |
---|---|
1 | q{ } 用单引号括起字符串 示例 − q{abcd} 得到 'abcd' |
2 | qq{ } 用双引号括起字符串 示例 − qq{abcd} 得到 "abcd" |
3 | qx{ } 用反引号括起字符串 示例 − qx{abcd} 得到 `abcd` |
其他运算符
Perl 语言支持以下其他运算符。假设变量 a 为 10,变量 b 为 20,则:
序号 | 运算符 & 说明 |
---|---|
1 | . 二元点运算符 (.) 用于连接两个字符串。 示例 − 如果 $a = "abc",$b = "def",则 $a.$b 将得到 "abcdef" |
2 | x 重复运算符 x 返回一个字符串,该字符串由左操作数重复右操作数指定的次数组成。 示例 − ('-' x 3) 将得到 ---。 |
3 | .. 范围运算符 .. 返回一个值列表,这些值从左值计数(每次递增一个)到右值。 示例 − (2..5) 将得到 (2, 3, 4, 5) |
4 | ++ 自动递增运算符将整数的值增加一。 示例 − $a++ 将得到 11 |
5 | -- 自动递减运算符将整数的值减少一。 示例 − $a-- 将得到 9 |
6 | -> 箭头运算符主要用于从对象或类名取消引用方法或变量。 示例 − $obj->{$a} 是从对象 $obj 访问变量 $a 的示例。 |
Perl 运算符优先级
下表列出了所有运算符,从最高优先级到最低优先级。
left terms and list operators (leftward) left -> nonassoc ++ -- right ** right ! ~ \ and unary + and - left =~ !~ left * / % x left + - . left << >> nonassoc named unary operators nonassoc < > <= >= lt gt le ge nonassoc == != <=> eq ne cmp ~~ left & left | ^ left && left || // nonassoc .. ... right ?: right = += -= *= etc. left , => nonassoc list operators (rightward) right not left and left or xor
Perl - 日期和时间
本章将帮助您了解如何在 Perl 中处理和操作日期和时间。
当前日期和时间
让我们从localtime()函数开始,如果未提供任何参数,则该函数将返回当前日期和时间的值。以下是localtime函数在列表上下文中使用的返回值(9 个元素的列表):
sec, # seconds of minutes from 0 to 61 min, # minutes of hour from 0 to 59 hour, # hours of day from 0 to 24 mday, # day of month from 1 to 31 mon, # month of year from 0 to 11 year, # year since 1900 wday, # days since sunday yday, # days since January 1st isdst # hours of daylight savings time
尝试以下示例以打印 localtime() 函数返回的不同元素:
#!/usr/local/bin/perl @months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); @days = qw(Sun Mon Tue Wed Thu Fri Sat Sun); ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); print "$mday $months[$mon] $days[$wday]\n";
执行上述代码时,将产生以下结果:
16 Feb Sat
如果在标量上下文中使用 localtime() 函数,则它将返回系统中设置的当前时区的日期和时间。尝试以下示例以打印完整格式的当前日期和时间:
#!/usr/local/bin/perl $datestring = localtime(); print "Local date and time $datestring\n";
执行上述代码时,将产生以下结果:
Local date and time Sat Feb 16 06:50:45 2013
GMT 时间
函数gmtime()的工作方式与 localtime() 函数类似,但返回值已针对标准格林威治时间区域进行了本地化。在列表上下文中调用时,gmtime 返回的最后一个值 $isdst 始终为 0。GMT 没有夏令时。
请注意,localtime() 将返回运行脚本的计算机上的当前本地时间,而 gmtime() 将返回世界标准时间,即 GMT(或 UTC)。
尝试以下示例以打印当前日期和时间,但以 GMT 标准显示:
#!/usr/local/bin/perl $datestring = gmtime(); print "GMT date and time $datestring\n";
执行上述代码时,将产生以下结果:
GMT date and time Sat Feb 16 13:50:45 2013
格式化日期和时间
您可以使用 localtime() 函数获取 9 个元素的列表,然后可以使用printf()函数根据您的要求格式化日期和时间,如下所示:
#!/usr/local/bin/perl ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); printf("Time Format - HH:MM:SS\n"); printf("%02d:%02d:%02d", $hour, $min, $sec);
执行上述代码时,将产生以下结果:
Time Format - HH:MM:SS 06:58:52
纪元时间
您可以使用 time() 函数获取纪元时间,即自给定日期以来经过的秒数,在 Unix 中是 1970 年 1 月 1 日。
#!/usr/local/bin/perl $epoc = time(); print "Number of seconds since Jan 1, 1970 - $epoc\n";
执行上述代码时,将产生以下结果:
Number of seconds since Jan 1, 1970 - 1361022130
您可以将给定的秒数转换为日期和时间字符串,如下所示:
#!/usr/local/bin/perl $datestring = localtime(); print "Current date and time $datestring\n"; $epoc = time(); $epoc = $epoc - 24 * 60 * 60; # one day before of current date. $datestring = localtime($epoc); print "Yesterday's date and time $datestring\n";
执行上述代码时,将产生以下结果:
Current date and time Tue Jun 5 05:54:43 2018 Yesterday's date and time Mon Jun 4 05:54:43 2018
POSIX 函数 strftime()
您可以使用 POSIX 函数strftime()来格式化日期和时间,方法是使用下表。请注意,用星号 (*) 标记的说明符是依赖于区域设置的。
说明符 | 替换为 | 示例 |
---|---|---|
%a |
缩写的星期几名称* | Thu |
%A |
完整的星期几名称* | Thursday |
%b |
缩写的月份名称* | Aug |
%B |
完整的月份名称* | August |
%c |
日期和时间的表示* | Thu Aug 23 14:55:02 2001 |
%C |
年份除以 100 并截断为整数 (00-99 ) |
20 |
%d |
月份中的天数,用零填充 (01-31 ) |
23 |
%D |
简短的 MM/DD/YY 日期,相当于 %m/%d/%y |
08/23/01 |
%e |
月份中的天数,用空格填充 ( 1-31 ) |
23 |
%F |
简短的 YYYY-MM-DD 日期,相当于 %Y-%m-%d |
2001-08-23 |
%g |
基于星期的年份,后两位数字 (00-99 ) |
01 |
%G |
基于星期的年份 | 2001 |
%h |
缩写的月份名称*(与%b 相同) |
Aug |
%H |
24 小时制的小时数 (00-23 ) |
14 |
%I |
12 小时制的小时数 (01-12 ) |
02 |
%j |
一年中的天数 (001-366 ) |
235 |
%m |
月份的十进制数 (01-12 ) |
08 |
%M |
分钟 (00-59 ) |
55 |
%n |
换行符 ('\n' ) |
|
%p |
AM 或 PM 表示 | PM |
%r |
12 小时制的时间* | 02:55:02 pm |
%R |
24 小时制 HH:MM 时间,相当于 %H:%M |
14:55 |
%S |
秒 (00-61 ) |
02 |
%t |
水平制表符 ('\t' ) |
|
%T |
ISO 8601 时间格式 (HH:MM:SS ),相当于 %H:%M:%S |
14:55 |
%u |
ISO 8601 星期几,星期一为 1 (1-7 ) |
4 |
%U |
星期数,以第一个星期日为第一周的第一天 (00-53 ) |
33 |
%V |
ISO 8601 星期数 (00-53 ) |
34 |
%w |
星期几的十进制数,星期日为 0 (0-6 ) |
4 |
%W |
星期数,以第一个星期一为第一周的第一天 (00-53 ) |
34 |
%x |
日期表示* | 08/23/01 |
%X |
时间表示* | 14:55:02 |
%y |
年份,后两位数字 (00-99 ) |
01 |
%Y |
年份 | 2001 |
%z |
ISO 8601 与 UTC 的时区偏移量(1 分钟 = 1,1 小时 = 100) 如果无法确定时区,则无字符 |
+100 |
%Z |
时区名称或缩写* 如果无法确定时区,则无字符 |
CDT |
%% |
% 符号 |
% |
让我们检查以下示例以了解其用法:
#!/usr/local/bin/perl use POSIX qw(strftime); $datestring = strftime "%a %b %e %H:%M:%S %Y", localtime; printf("date and time - $datestring\n"); # or for GMT formatted appropriately for your locale: $datestring = strftime "%a %b %e %H:%M:%S %Y", gmtime; printf("date and time - $datestring\n");
执行上述代码时,将产生以下结果:
date and time - Sat Feb 16 07:10:23 2013 date and time - Sat Feb 16 14:10:23 2013
Perl - 子程序
Perl 子例程或函数是一组语句,这些语句一起执行一项任务。您可以将代码划分为单独的子例程。如何将代码划分为不同的子例程取决于您,但从逻辑上讲,划分通常是让每个函数执行特定任务。
Perl 互换使用子例程、方法和函数。
定义和调用子例程
Perl 编程语言中子例程定义的一般形式如下:
sub subroutine_name { body of the subroutine }
调用 Perl 子例程的典型方法如下:
subroutine_name( list of arguments );
在 5.0 之前的 Perl 版本中,调用子例程的语法略有不同,如下所示。这仍然适用于最新版本的 Perl,但不推荐,因为它绕过了子例程原型。
&subroutine_name( list of arguments );
让我们来看以下示例,该示例定义了一个简单的函数,然后调用它。因为 Perl 在执行程序之前会编译程序,所以您声明子例程的位置无关紧要。
#!/usr/bin/perl # Function definition sub Hello { print "Hello, World!\n"; } # Function call Hello();
执行上述程序时,将产生以下结果:
Hello, World!
向子例程传递参数
您可以像在其他任何编程语言中一样,向子程序传递各种参数,并且可以使用特殊的数组 `@_` 在函数内部访问它们。因此,函数的第一个参数在 `$_[0]` 中,第二个参数在 `$_[1]` 中,依此类推。
您可以像任何标量一样传递数组和哈希作为参数,但是传递多个数组或哈希通常会导致它们丢失各自的标识。因此,我们将使用引用(下一章将解释)来传递任何数组或哈希。
让我们尝试以下示例,它接收一组数字并打印它们的平均值:
#!/usr/bin/perl # Function definition sub Average { # get total number of arguments passed. $n = scalar(@_); $sum = 0; foreach $item (@_) { $sum += $item; } $average = $sum / $n; print "Average for the given numbers : $average\n"; } # Function call Average(10, 20, 30);
执行上述程序时,将产生以下结果:
Average for the given numbers : 20
向子程序传递列表
因为 `@_` 变量是一个数组,所以它可以用来向子程序提供列表。但是,由于 Perl 接受和解析列表和数组的方式,从 `@_` 中提取单个元素可能很困难。如果您必须与其他标量参数一起传递列表,则应将列表作为最后一个参数,如下所示:
#!/usr/bin/perl # Function definition sub PrintList { my @list = @_; print "Given list is @list\n"; } $a = 10; @b = (1, 2, 3, 4); # Function call with list parameter PrintList($a, @b);
执行上述程序时,将产生以下结果:
Given list is 10 1 2 3 4
向子程序传递哈希
当您向接受列表的子程序或运算符提供哈希时,哈希会自动转换为键/值对列表。例如:
#!/usr/bin/perl # Function definition sub PrintHash { my (%hash) = @_; foreach my $key ( keys %hash ) { my $value = $hash{$key}; print "$key : $value\n"; } } %hash = ('name' => 'Tom', 'age' => 19); # Function call with hash parameter PrintHash(%hash);
执行上述程序时,将产生以下结果:
name : Tom age : 19
从子程序返回值
您可以像在任何其他编程语言中一样从子程序返回值。如果您没有从子程序返回值,则子程序中最后执行的任何计算也自动成为返回值。
您可以像任何标量一样从子程序返回数组和哈希,但是返回多个数组或哈希通常会导致它们丢失各自的标识。因此,我们将使用引用(下一章将解释)从函数返回任何数组或哈希。
让我们尝试以下示例,它接收一组数字并返回它们的平均值:
#!/usr/bin/perl # Function definition sub Average { # get total number of arguments passed. $n = scalar(@_); $sum = 0; foreach $item (@_) { $sum += $item; } $average = $sum / $n; return $average; } # Function call $num = Average(10, 20, 30); print "Average for the given numbers : $num\n";
执行上述程序时,将产生以下结果:
Average for the given numbers : 20
子程序中的私有变量
默认情况下,Perl 中的所有变量都是全局变量,这意味着可以从程序的任何位置访问它们。但是您可以随时使用 `my` 运算符创建称为**词法变量**的**私有**变量。
`my` 运算符将变量限制在可以使用和访问它的特定代码区域内。在此区域之外,无法使用或访问此变量。此区域称为其作用域。词法作用域通常是一块代码块,周围有一组大括号,例如定义子程序主体的大括号,或标记 `if`、`while`、`for`、`foreach` 和 `eval` 语句的代码块的大括号。
以下是一个示例,展示了如何使用 `my` 运算符定义单个或多个私有变量:
sub somefunc { my $variable; # $variable is invisible outside somefunc() my ($another, @an_array, %a_hash); # declaring many variables at once }
让我们检查以下示例,以区分全局变量和私有变量:
#!/usr/bin/perl # Global variable $string = "Hello, World!"; # Function definition sub PrintHello { # Private variable for PrintHello function my $string; $string = "Hello, Perl!"; print "Inside the function $string\n"; } # Function call PrintHello(); print "Outside the function $string\n";
执行上述程序时,将产生以下结果:
Inside the function Hello, Perl! Outside the function Hello, World!
通过 `local()` 使用临时值
`local` 主要用于当前变量的值必须对调用的子程序可见的情况。`local` 只是为全局(即包)变量提供临时值。这被称为 *动态作用域*。词法作用域使用 `my` 完成,其工作方式更类似于 C 语言的 `auto` 声明。
如果向 `local` 提供多个变量或表达式,则必须将它们放在括号中。此运算符的工作方式是将其参数列表中这些变量的当前值保存在隐藏的堆栈上,并在退出块、子程序或 `eval` 时将其恢复。
让我们检查以下示例,以区分全局变量和局部变量:
#!/usr/bin/perl # Global variable $string = "Hello, World!"; sub PrintHello { # Private variable for PrintHello function local $string; $string = "Hello, Perl!"; PrintMe(); print "Inside the function PrintHello $string\n"; } sub PrintMe { print "Inside the function PrintMe $string\n"; } # Function call PrintHello(); print "Outside the function $string\n";
执行上述程序时,将产生以下结果:
Inside the function PrintMe Hello, Perl! Inside the function PrintHello Hello, Perl! Outside the function Hello, World!
通过 `state()` 使用状态变量
还有一种词法变量,它们类似于私有变量,但它们保持其状态,并且在多次调用子程序时不会重新初始化。这些变量使用 `state` 运算符定义,并且从 Perl 5.9.4 开始可用。
让我们检查以下示例,以演示 `state` 变量的使用:
#!/usr/bin/perl use feature 'state'; sub PrintCount { state $count = 0; # initial value print "Value of counter is $count\n"; $count++; } for (1..5) { PrintCount(); }
执行上述程序时,将产生以下结果:
Value of counter is 0 Value of counter is 1 Value of counter is 2 Value of counter is 3 Value of counter is 4
在 Perl 5.10 之前,您必须这样编写:
#!/usr/bin/perl { my $count = 0; # initial value sub PrintCount { print "Value of counter is $count\n"; $count++; } } for (1..5) { PrintCount(); }
子程序调用上下文
子程序或语句的上下文定义为预期返回的类型。这允许您使用单个函数根据用户期望接收的内容返回不同的值。例如,以下 `localtime()` 在标量上下文中调用时返回字符串,但在列表上下文中调用时返回列表。
my $datestring = localtime( time );
在此示例中,`$timestr` 的值现在是一个由当前日期和时间组成的字符串,例如 Thu Nov 30 15:21:33 2000。相反:
($sec,$min,$hour,$mday,$mon, $year,$wday,$yday,$isdst) = localtime(time);
现在各个变量包含 `localtime()` 子程序返回的相应值。
Perl - 引用
Perl 引用是一种标量数据类型,它保存另一个值的地址,该值可以是标量、数组或哈希。由于其标量特性,引用可以在任何可以使用标量的地方使用。
您可以构造包含对其他列表的引用的列表,这些列表可以包含对哈希的引用,依此类推。这就是 Perl 中嵌套数据结构的构建方式。
创建引用
通过在任何变量、子程序或值前面加上反斜杠,可以轻松地为其创建引用,如下所示:
$scalarref = \$foo; $arrayref = \@ARGV; $hashref = \%ENV; $coderef = \&handler; $globref = \*foo;
您不能使用反斜杠运算符为 I/O 句柄(文件句柄或目录句柄)创建引用,但可以使用方括号创建对匿名数组的引用,如下所示:
$arrayref = [1, 2, ['a', 'b', 'c']];
类似地,您可以使用花括号创建对匿名哈希的引用,如下所示:
$hashref = { 'Adam' => 'Eve', 'Clyde' => 'Bonnie', };
可以使用不带子程序名的 `sub` 创建对匿名子程序的引用,如下所示:
$coderef = sub { print "Boink!\n" };
取消引用
取消引用从指向该位置的引用返回该值。要取消引用,只需根据引用指向标量、数组还是哈希,在引用变量前面使用 `$`、`@` 或 `%` 即可。以下示例解释了这个概念:
#!/usr/bin/perl $var = 10; # Now $r has reference to $var scalar. $r = \$var; # Print value available at the location stored in $r. print "Value of $var is : ", $$r, "\n"; @var = (1, 2, 3); # Now $r has reference to @var array. $r = \@var; # Print values available at the location stored in $r. print "Value of @var is : ", @$r, "\n"; %var = ('key1' => 10, 'key2' => 20); # Now $r has reference to %var hash. $r = \%var; # Print values available at the location stored in $r. print "Value of %var is : ", %$r, "\n";
执行上述程序时,将产生以下结果:
Value of 10 is : 10 Value of 1 2 3 is : 123 Value of %var is : key220key110
如果您不确定变量类型,则可以使用 `ref` 轻松了解其类型,如果其参数是引用,则返回以下字符串之一。否则,它返回 false:
SCALAR ARRAY HASH CODE GLOB REF
让我们尝试以下示例:
#!/usr/bin/perl $var = 10; $r = \$var; print "Reference type in r : ", ref($r), "\n"; @var = (1, 2, 3); $r = \@var; print "Reference type in r : ", ref($r), "\n"; %var = ('key1' => 10, 'key2' => 20); $r = \%var; print "Reference type in r : ", ref($r), "\n";
执行上述程序时,将产生以下结果:
Reference type in r : SCALAR Reference type in r : ARRAY Reference type in r : HASH
循环引用
当两个引用相互包含引用时,就会发生循环引用。创建引用时必须小心,否则循环引用会导致内存泄漏。以下是一个示例:
#!/usr/bin/perl my $foo = 100; $foo = \$foo; print "Value of foo is : ", $$foo, "\n";
执行上述程序时,将产生以下结果:
Value of foo is : REF(0x9aae38)
对函数的引用
如果您需要创建信号处理程序,则可能会发生这种情况,您可以通过在函数名前面加上 `\&` 来生成对函数的引用,要取消引用该引用,您只需使用 `&` 前缀引用变量即可。以下是一个示例:
#!/usr/bin/perl # Function definition sub PrintHash { my (%hash) = @_; foreach $item (%hash) { print "Item : $item\n"; } } %hash = ('name' => 'Tom', 'age' => 19); # Create a reference to above function. $cref = \&PrintHash; # Function call using reference. &$cref(%hash);
执行上述程序时,将产生以下结果:
Item : name Item : Tom Item : age Item : 19
Perl - 格式
Perl 使用称为“格式”的编写模板来输出报表。要使用 Perl 的格式功能,您必须首先定义格式,然后可以使用该格式来写入格式化数据。
定义格式
以下是定义 Perl 格式的语法:
format FormatName = fieldline value_one, value_two, value_three fieldline value_one, value_two .
这里 `FormatName` 代表格式的名称。`fieldline` 是数据应如何格式化的特定方式。`values lines` 代表将输入到 `fieldline` 中的值。您用一个句点结束格式。
下一个 `fieldline` 可以包含任何文本或字段占位符。字段占位符为稍后要放置在那里的数据保留空间。字段占位符的格式为:
@<<<<
此字段占位符左对齐,字段空间为 5。您必须计算 `@` 符号和 `<` 符号来了解字段中的空格数。其他字段占位符包括:
@>>>> right-justified @|||| centered @####.## numeric field holder @* multiline field holder
一个示例格式将是:
format EMPLOYEE = =================================== @<<<<<<<<<<<<<<<<<<<<<< @<< $name $age @#####.## $salary =================================== .
在此示例中,`$name` 将左对齐写入 22 个字符空间内,然后年龄将写入两个空间。
使用格式
为了调用此格式声明,我们将使用 `write` 关键字:
write EMPLOYEE;
问题是格式名称通常是打开的文件句柄的名称,`write` 语句会将输出发送到此文件句柄。因为我们希望数据发送到 STDOUT,所以我们必须使用 `select()` 函数将 EMPLOYEE 与 STDOUT 文件句柄关联。但是,首先我们必须确保 STDOUT 是我们选择的文件句柄。
select(STDOUT);
然后,我们将通过使用特殊变量 `$~` 或 `$FORMAT_NAME` 设置新的格式名称来将 EMPLOYEE 与 STDOUT 关联,如下所示:
$~ = "EMPLOYEE";
当我们现在执行 `write()` 时,数据将发送到 STDOUT。记住:如果您要在任何其他文件句柄(而不是 STDOUT)中写入报表,则可以使用 `select()` 函数选择该文件句柄,其余逻辑保持不变。
让我们来看下面的例子。这里我们只是为了演示用法而硬编码了值。在实际使用中,您将从文件或数据库读取值以生成实际报表,并且您可能需要再次将最终报表写入文件。
#!/usr/bin/perl format EMPLOYEE = =================================== @<<<<<<<<<<<<<<<<<<<<<< @<< $name $age @#####.## $salary =================================== . select(STDOUT); $~ = EMPLOYEE; @n = ("Ali", "Raza", "Jaffer"); @a = (20,30, 40); @s = (2000.00, 2500.00, 4000.000); $i = 0; foreach (@n) { $name = $_; $age = $a[$i]; $salary = $s[$i++]; write; }
执行后,将产生以下结果:
=================================== Ali 20 2000.00 =================================== =================================== Raza 30 2500.00 =================================== =================================== Jaffer 40 4000.00 ===================================
定义报表页眉
一切看起来都很好。但是您可能希望向报表中添加页眉。此页眉将打印在每一页的顶部。这样做很简单。除了定义模板之外,您还必须定义页眉并将其分配给 `$^` 或 `$FORMAT_TOP_NAME` 变量:
#!/usr/bin/perl format EMPLOYEE = =================================== @<<<<<<<<<<<<<<<<<<<<<< @<< $name $age @#####.## $salary =================================== . format EMPLOYEE_TOP = =================================== Name Age =================================== . select(STDOUT); $~ = EMPLOYEE; $^ = EMPLOYEE_TOP; @n = ("Ali", "Raza", "Jaffer"); @a = (20,30, 40); @s = (2000.00, 2500.00, 4000.000); $i = 0; foreach (@n) { $name = $_; $age = $a[$i]; $salary = $s[$i++]; write; }
现在您的报表将如下所示:
=================================== Name Age =================================== =================================== Ali 20 2000.00 =================================== =================================== Raza 30 2500.00 =================================== =================================== Jaffer 40 4000.00 ===================================
定义分页
如果您的报表超过一页怎么办?您有一个解决方案,只需使用 `$%` 或 `$FORMAT_PAGE_NUMBER` 变量以及页眉,如下所示:
format EMPLOYEE_TOP = =================================== Name Age Page @< $% =================================== .
现在您的输出将如下所示:
=================================== Name Age Page 1 =================================== =================================== Ali 20 2000.00 =================================== =================================== Raza 30 2500.00 =================================== =================================== Jaffer 40 4000.00 ===================================
每页的行数
您可以使用特殊变量 `$=`(或 `$FORMAT_LINES_PER_PAGE`)设置每页的行数,默认为 `$=` 为 60。
定义报表页脚
虽然 `$^` 或 `$FORMAT_TOP_NAME` 包含当前页眉格式的名称,但是没有相应的机制可以自动对页脚执行相同的操作。如果您有固定大小的页脚,则可以在每次 `write()` 之前检查变量 `$-` 或 `$FORMAT_LINES_LEFT` 并根据需要使用另一个定义的格式打印页脚,如下所示:
format EMPLOYEE_BOTTOM = End of Page @< $% .
有关与格式相关的完整变量集,请参阅 Perl 特殊变量 部分。
Perl - 文件I/O
文件处理的基础知识很简单:您将**文件句柄**与外部实体(通常是文件)关联,然后使用 Perl 中的各种运算符和函数来读取和更新与文件句柄关联的数据流中存储的数据。
文件句柄是命名的内部 Perl 结构,它将物理文件与名称关联。所有文件句柄都能够进行读/写访问,因此您可以读取和更新与文件句柄关联的任何文件或设备。但是,当您关联文件句柄时,您可以指定打开文件句柄的模式。
三个基本文件句柄是 `STDIN`、`STDOUT` 和 `STDERR`,它们分别代表标准输入、标准输出和标准错误设备。
打开和关闭文件
以下有两个具有多种形式的函数,可用于在 Perl 中打开任何新的或现有文件。
open FILEHANDLE, EXPR open FILEHANDLE sysopen FILEHANDLE, FILENAME, MODE, PERMS sysopen FILEHANDLE, FILENAME, MODE
此处 FILEHANDLE 是由 **open** 函数返回的文件句柄,而 EXPR 是包含文件名和文件打开模式的表达式。
Open 函数
以下是以只读模式打开 **file.txt** 的语法。这里的小于号 < 表示文件需要以只读模式打开。
open(DATA, "<file.txt");
此处 DATA 是文件句柄,将用于读取文件。以下是一个示例,它将打开一个文件并将内容打印到屏幕上。
#!/usr/bin/perl open(DATA, "<file.txt") or die "Couldn't open file file.txt, $!"; while(<DATA>) { print "$_"; }
以下是以写入模式打开 file.txt 的语法。这里的大于号 > 表示文件需要以写入模式打开。
open(DATA, ">file.txt") or die "Couldn't open file file.txt, $!";
此示例在打开文件写入之前实际上会截断(清空)文件,这可能不是所需的效果。如果要打开文件进行读写,可以在 > 或 < 字符前加上加号。
例如,要打开文件进行更新而不截断它:
open(DATA, "+<file.txt"); or die "Couldn't open file file.txt, $!";
要先截断文件:
open DATA, "+>file.txt" or die "Couldn't open file file.txt, $!";
可以以追加模式打开文件。在此模式下,写入指针将设置为文件的末尾。
open(DATA,">>file.txt") || die "Couldn't open file file.txt, $!";
双 >> 将打开文件以进行追加,并将文件指针置于末尾,以便可以立即开始追加信息。但是,除非您还在其前面加上加号,否则无法从中读取:
open(DATA,"+>>file.txt") || die "Couldn't open file file.txt, $!";
下表列出了不同模式的可能值
序号 | 实体 & 定义 |
---|---|
1 | < 或 r 只读访问 |
2 | > 或 w 创建、写入和截断 |
3 | > 或 a 写入、追加和创建 |
4 | +< 或 r+ 读写 |
5 | +> 或 w+ 读取、写入、创建和截断 |
6 | +>> 或 a+ 读取、写入、追加和创建 |
Sysopen 函数
**sysopen** 函数类似于主要的 open 函数,不同之处在于它使用系统 **open()** 函数,并将其提供的参数用作系统函数的参数:
例如,要打开文件进行更新,模拟 open 的 **+<filename** 格式:
sysopen(DATA, "file.txt", O_RDWR);
或者在更新之前截断文件:
sysopen(DATA, "file.txt", O_RDWR|O_TRUNC );
可以使用 O_CREAT 创建新文件,使用 O_WRONLY 打开只写模式的文件,使用 O_RDONLY 打开只读模式的文件。
如果必须创建 **PERMS** 参数,则指定文件的权限。默认情况下,它取值为 **0x666**。
下表列出了 MODE 的可能值。
序号 | 实体 & 定义 |
---|---|
1 | O_RDWR 读写 |
2 | O_RDONLY 只读 |
3 | O_WRONLY 只写 |
4 | O_CREAT 创建文件 |
5 | O_APPEND 追加到文件 |
6 | O_TRUNC 截断文件 |
7 | O_EXCL 如果文件已存在则停止 |
8 | O_NONBLOCK 非阻塞可用性 |
Close 函数
要关闭文件句柄,从而取消文件句柄与相应文件的关联,可以使用 **close** 函数。这将刷新文件句柄的缓冲区并关闭系统的文件描述符。
close FILEHANDLE close
如果没有指定 FILEHANDLE,则它将关闭当前选择的文件句柄。只有在成功刷新缓冲区并关闭文件时,它才返回 true。
close(DATA) || die "Couldn't close file properly";
读取和写入文件
打开文件句柄后,需要能够读取和写入信息。有多种不同的方法可以读取和写入文件中的数据。
<FILEHANDL> 运算符
从打开的文件句柄读取信息的主要方法是 <FILEHANDLE> 运算符。在标量上下文中,它从文件句柄返回一行。例如:
#!/usr/bin/perl print "What is your name?\n"; $name = <STDIN>; print "Hello $name\n";
在列表上下文中使用 <FILEHANDLE> 运算符时,它将从指定的文件句柄返回一个行列表。例如,要将文件中的所有行导入数组:
#!/usr/bin/perl open(DATA,"<import.txt") or die "Can't open data"; @lines = <DATA>; close(DATA);
getc 函数
getc 函数从指定 FILEHANDLE 或 STDIN(如果未指定)返回单个字符:
getc FILEHANDLE getc
如果发生错误,或者文件句柄位于文件末尾,则返回 undef。
read 函数
read 函数从缓冲的文件句柄读取一段信息:此函数用于从文件读取二进制数据。
read FILEHANDLE, SCALAR, LENGTH, OFFSET read FILEHANDLE, SCALAR, LENGTH
读取的数据长度由 LENGTH 定义,如果未指定 OFFSET,则数据将放在 SCALAR 的开头。否则,数据将放在 SCALAR 中 OFFSET 字节之后。该函数在成功时返回读取的字节数,在文件末尾返回零,如果发生错误则返回 undef。
print 函数
对于所有用于从文件句柄读取信息的不同方法,写入信息的主要函数是 print 函数。
print FILEHANDLE LIST print LIST print
print 函数将 LIST 的计算值打印到 FILEHANDLE 或当前输出文件句柄(默认为 STDOUT)。例如:
print "Hello World!\n";
复制文件
以下示例打开现有文件 file1.txt 并逐行读取它,然后生成另一个副本文件 file2.txt。
#!/usr/bin/perl # Open file to read open(DATA1, "<file1.txt"); # Open new file to write open(DATA2, ">file2.txt"); # Copy data from one file to another. while(<DATA1>) { print DATA2 $_; } close( DATA1 ); close( DATA2 );
重命名文件
以下是一个示例,它演示了如何将文件 file1.txt 重命名为 file2.txt。假设文件位于 /usr/test 目录中。
#!/usr/bin/perl rename ("/usr/test/file1.txt", "/usr/test/file2.txt" );
此函数 **rename** 接受两个参数,它只是重命名现有文件。
删除现有文件
以下是一个示例,它演示了如何使用 **unlink** 函数删除文件 file1.txt。
#!/usr/bin/perl unlink ("/usr/test/file1.txt");
在文件中定位
可以使用 **tell** 函数来了解文件的当前位置,并使用 **seek** 函数来指向文件内的特定位置。
tell 函数
第一个要求是在文件中找到您的位置,您可以使用 tell 函数:
tell FILEHANDLE tell
如果指定了 FILEHANDLE,则返回文件指针在 FILEHANDLE 中的字节位置;如果没有指定,则返回当前默认选择的文件句柄的字节位置。
seek 函数
seek 函数将文件指针定位到文件中的指定字节数:
seek FILEHANDLE, POSITION, WHENCE
该函数使用 fseek 系统函数,您可以相对三个不同的点进行定位:开头、结尾和当前位置。您可以通过为 WHENCE 指定一个值来实现这一点。
零将定位设置为相对于文件的开头。例如,该行将文件指针设置为文件中的第 256 个字节。
seek DATA, 256, 0;
文件信息
您可以使用一系列统称为 -X 测试的测试运算符在 Perl 中快速测试某些功能。例如,要对文件的各种权限进行快速测试,可以使用这样的脚本:
#/usr/bin/perl my $file = "/usr/test/file1.txt"; my (@description, $size); if (-e $file) { push @description, 'binary' if (-B _); push @description, 'a socket' if (-S _); push @description, 'a text file' if (-T _); push @description, 'a block special file' if (-b _); push @description, 'a character special file' if (-c _); push @description, 'a directory' if (-d _); push @description, 'executable' if (-x _); push @description, (($size = -s _)) ? "$size bytes" : 'empty'; print "$file is ", join(', ',@description),"\n"; }
以下是您可以检查文件或目录的功能列表:
序号 | 运算符 & 定义 |
---|---|
1 | -A 脚本启动时间减去文件上次访问时间(以天为单位)。 |
2 | -B 它是二进制文件吗? |
3 | -C 脚本启动时间减去文件上次 inode 更改时间(以天为单位)。 |
3 | -M 脚本启动时间减去文件修改时间(以天为单位)。 |
4 | -O 该文件是否由真实用户 ID 拥有? |
5 | -R 真实用户 ID 或真实组是否可以读取该文件? |
6 | -S 该文件是套接字吗? |
7 | -T 它是文本文件吗? |
8 | -W 真实用户 ID 或真实组是否可以写入该文件? |
9 | -X 真实用户 ID 或真实组是否可以执行该文件? |
10 | -b 它是块特殊文件吗? |
11 | -c 它是字符特殊文件吗? |
12 | -d 该文件是目录吗? |
13 | -e 文件是否存在? |
14 | -f 它是普通文件吗? |
15 | -g 该文件是否设置了 setgid 位? |
16 | -k 该文件是否设置了粘滞位? |
17 | -l 该文件是符号链接吗? |
18 | -o 该文件是否由有效用户 ID 拥有? |
19 | -p 该文件是命名管道吗? |
20 | -r 有效用户或组 ID 是否可以读取该文件? |
21 | -s 返回文件的大小,零大小 = 空文件。 |
22 | -t 文件句柄是由 TTY(终端)打开的吗? |
23 | -u 该文件是否设置了 setuid 位? |
24 | -w 有效用户或组 ID 是否可以写入该文件? |
25 | -x 有效用户或组 ID 是否可以执行该文件? |
26 | -z 文件大小是否为零? |
Perl - 目录
以下是用于处理目录的标准函数。
opendir DIRHANDLE, EXPR # To open a directory readdir DIRHANDLE # To read a directory rewinddir DIRHANDLE # Positioning pointer to the begining telldir DIRHANDLE # Returns current position of the dir seekdir DIRHANDLE, POS # Pointing pointer to POS inside dir closedir DIRHANDLE # Closing a directory.
显示所有文件
有多种方法可以列出特定目录中所有可用的文件。首先,让我们使用简单的方法来获取并列出使用 **glob** 运算符的所有文件:
#!/usr/bin/perl # Display all the files in /tmp directory. $dir = "/tmp/*"; my @files = glob( $dir ); foreach (@files ) { print $_ . "\n"; } # Display all the C source files in /tmp directory. $dir = "/tmp/*.c"; @files = glob( $dir ); foreach (@files ) { print $_ . "\n"; } # Display all the hidden files. $dir = "/tmp/.*"; @files = glob( $dir ); foreach (@files ) { print $_ . "\n"; } # Display all the files from /tmp and /home directories. $dir = "/tmp/* /home/*"; @files = glob( $dir ); foreach (@files ) { print $_ . "\n"; }
以下是一个示例,它打开一个目录并列出此目录中所有可用的文件。
#!/usr/bin/perl opendir (DIR, '.') or die "Couldn't open directory, $!"; while ($file = readdir DIR) { print "$file\n"; } closedir DIR;
另一个打印 C 源文件列表的示例:
#!/usr/bin/perl opendir(DIR, '.') or die "Couldn't open directory, $!"; foreach (sort grep(/^.*\.c$/,readdir(DIR))) { print "$_\n"; } closedir DIR;
创建新目录
可以使用 **mkdir** 函数创建一个新目录。您需要具有创建目录所需的权限。
#!/usr/bin/perl $dir = "/tmp/perl"; # This creates perl directory in /tmp directory. mkdir( $dir ) or die "Couldn't create $dir directory, $!"; print "Directory created successfully\n";
删除目录
可以使用 **rmdir** 函数删除目录。您需要具有删除目录所需的权限。此外,在尝试删除目录之前,该目录必须为空。
#!/usr/bin/perl $dir = "/tmp/perl"; # This removes perl directory from /tmp directory. rmdir( $dir ) or die "Couldn't remove $dir directory, $!"; print "Directory removed successfully\n";
更改目录
可以使用 **chdir** 函数更改目录并转到新位置。您需要具有更改目录并进入新目录所需的权限。
#!/usr/bin/perl $dir = "/home"; # This changes perl directory and moves you inside /home directory. chdir( $dir ) or die "Couldn't go inside $dir directory, $!"; print "Your new location is $dir\n";
Perl - 错误处理
执行和错误总是同时出现。如果您打开一个不存在的文件,那么如果您没有正确处理这种情况,那么您的程序就被认为质量不好。
如果发生错误,程序将停止。因此,使用正确的错误处理来处理程序执行期间可能发生的各种类型的错误,并采取适当的措施,而不是完全停止程序。
您可以通过多种不同的方式识别和捕获错误。在 Perl 中捕获错误然后正确处理它们非常容易。这里有一些可以使用的方法。
if 语句
当您需要检查语句的返回值时,**if 语句** 是显而易见的选择;例如:
if(open(DATA, $file)) { ... } else { die "Error: Couldn't open the file - $!"; }
这里的变量 $! 返回实际的错误消息。或者,在有意义的情况下,我们可以将语句缩减为一行;例如:
open(DATA, $file) || die "Error: Couldn't open the file $!";
unless 函数
unless 函数与 if 函数逻辑相反:语句可以完全绕过成功状态,只有在表达式返回 false 时才执行。例如:
unless(chdir("/etc")) { die "Error: Can't change directory - $!"; }
当您只想在表达式失败时引发错误或备选方案时,最好使用 unless 语句。当用于单行语句时,该语句也说得通:
die "Error: Can't change directory!: $!" unless(chdir("/etc"));
这里只有当 chdir 操作失败时我们才会终止程序,而且读起来也很流畅。
三元运算符
对于非常短的测试,可以使用条件运算符 ?:
print(exists($hash{value}) ? 'There' : 'Missing',"\n");
这里不太清楚我们试图实现什么,但效果与使用 if 或 unless 语句相同。当您想快速在一个表达式或语句中返回两个值中的一个时,最好使用条件运算符。
warn 函数
warn 函数只发出警告,一条消息将打印到 STDERR,但不会采取进一步的操作。因此,如果您只想为用户打印警告并继续执行其余操作,它将更有用:
chdir('/etc') or warn "Can't change directory";
die 函数
die 函数的工作方式与 warn 函数类似,不同之处在于它还会调用 exit。在普通脚本中,此函数的效果是立即终止执行。如果程序出现错误,继续执行毫无意义,则应使用此函数:
chdir('/etc') or die "Can't change directory";
模块中的错误
我们应该能够处理两种不同的情况:
报告模块中的错误,该错误引用模块的文件名和行号——这在调试模块时很有用,或者当您特别想要引发与模块相关的而不是与脚本相关的错误时。
报告模块中引用的调用者信息的错误,以便您可以调试导致错误的脚本中的行。这样引发的错误对最终用户很有用,因为它们突出了与调用脚本的起始行相关的错误。
从模块中调用时,warn 和 die 函数的工作方式与您预期的略有不同。例如,简单的模块:
package T; require Exporter; @ISA = qw/Exporter/; @EXPORT = qw/function/; use Carp; sub function { warn "Error in module!"; } 1;
从如下脚本调用时:
use T; function();
它将产生以下结果:
Error in module! at T.pm line 9.
这或多或少是您可能期望的,但不一定是您想要的。从模块程序员的角度来看,该信息很有用,因为它有助于指出模块本身中的错误。对于最终用户而言,提供的信息几乎毫无用处,对于除经验丰富的程序员以外的所有人来说,它完全毫无意义。
解决此类问题的方案是 Carp 模块,它提供了一种简化的方法来报告模块中的错误,这些错误返回有关调用脚本的信息。Carp 模块提供四个函数:carp、cluck、croak 和 confess。下面将讨论这些函数。
carp 函数
carp 函数是 warn 的基本等效项,它将消息打印到 STDERR,而不会实际退出脚本并打印脚本名称。
package T; require Exporter; @ISA = qw/Exporter/; @EXPORT = qw/function/; use Carp; sub function { carp "Error in module!"; } 1;
从如下脚本调用时:
use T; function();
它将产生以下结果:
Error in module! at test.pl line 4
cluck 函数
cluck 函数是一种增强版的 carp,它遵循相同的基本原理,但也打印导致调用该函数的所有模块的堆栈跟踪,包括原始脚本的信息。
package T; require Exporter; @ISA = qw/Exporter/; @EXPORT = qw/function/; use Carp qw(cluck); sub function { cluck "Error in module!"; } 1;
从如下脚本调用时:
use T; function();
它将产生以下结果:
Error in module! at T.pm line 9 T::function() called at test.pl line 4
croak 函数
croak 函数等效于 die,不同之处在于它向上报告一个级别的调用者。与 die 一样,此函数还会在将错误报告到 STDERR 后退出脚本:
package T; require Exporter; @ISA = qw/Exporter/; @EXPORT = qw/function/; use Carp; sub function { croak "Error in module!"; } 1;
从如下脚本调用时:
use T; function();
它将产生以下结果:
Error in module! at test.pl line 4
与 carp 一样,关于根据 warn 和 die 函数包含行和文件信息的相同基本规则适用。
confess 函数
confess 函数类似于 cluck;它调用 die,然后打印一直到原始脚本的堆栈跟踪。
package T; require Exporter; @ISA = qw/Exporter/; @EXPORT = qw/function/; use Carp; sub function { confess "Error in module!"; } 1;
从如下脚本调用时:
use T; function();
它将产生以下结果:
Error in module! at T.pm line 9 T::function() called at test.pl line 4
Perl - 特殊变量
Perl 中有一些变量具有预定义的特殊含义。它们是在通常的变量指示符($、@ 或 %)之后使用标点符号的变量,例如 $_(如下所述)。
大多数特殊变量都有一个类似英文的长名称,例如,操作系统错误变量 $! 可以写成 $OS_ERROR。但是,如果您要使用类似英文的名称,则必须在程序文件的顶部添加一行 use English;。这指导解释器拾取变量的确切含义。
最常用的特殊变量是 $_,它包含默认的输入和模式搜索字符串。例如,在以下几行中:
#!/usr/bin/perl foreach ('hickory','dickory','doc') { print $_; print "\n"; }
执行后,将产生以下结果:
hickory dickory doc
同样,让我们检查一下不显式使用 $_ 变量的相同示例:
#!/usr/bin/perl foreach ('hickory','dickory','doc') { print; print "\n"; }
执行后,这也将产生以下结果:
hickory dickory doc
第一次执行循环时,打印 "hickory"。第二次打印 "dickory",第三次打印 "doc"。这是因为在每次循环迭代中,当前字符串都放在 $_ 中,并被 print 默认使用。以下是 Perl 即使您没有指定它也会假定 $_ 的地方:
各种一元函数,包括诸如 ord 和 int 之类的函数,以及所有文件测试(-f、-d),除了 -t,它默认为 STDIN。
各种列表函数,如 print 和 unlink。
模式匹配操作 m//、s/// 和 tr///,当不使用 =~ 运算符时。
如果没有提供其他变量,则在 foreach 循环中的默认迭代器变量。
grep 和 map 函数中的隐式迭代器变量。
当行输入操作的结果本身作为 while 测试的唯一条件进行测试时(即,),将输入记录放在默认位置。请注意,在 while 测试之外,不会发生这种情况。
特殊变量类型
根据特殊变量的用法和性质,我们可以将它们分为以下几类:
- 全局标量特殊变量。
- 全局数组特殊变量。
- 全局哈希特殊变量。
- 全局特殊文件句柄。
- 全局特殊常量。
- 正则表达式特殊变量。
- 文件句柄特殊变量。
全局标量特殊变量
以下是所有标量特殊变量的列表。我们列出了相应的类似英文的名称以及符号名称。
$_ | 默认输入和模式搜索空间。 |
$ARG | |
$. | 最后读取的文件句柄的当前输入行号。对文件句柄的显式关闭将重置行号。 |
$NR | |
$/ | 输入记录分隔符;默认为换行符。如果设置为空字符串,则将其视为空白行作为分隔符。 |
$RS | |
$, | print 运算符的输出字段分隔符。 |
$OFS | |
$\ | print 运算符的输出记录分隔符。 |
$ORS | |
$" | 类似于 "$,",但它适用于插入到双引号字符串(或类似的解释字符串)中的列表值。默认为空格。 |
$LIST_SEPARATOR | |
$; | 用于多维数组仿真的下标分隔符。默认为 "\034"。 |
$SUBSCRIPT_SEPARATOR | |
$^L | 格式输出以执行换页符。默认为 "\f"。 |
$FORMAT_FORMFEED | |
$: | 当前字符集,字符串之后可以中断以填充格式中的延续字段(以 ^ 开头)。默认为 "\n""。 |
$FORMAT_LINE_BREAK_CHARACTERS | |
$^A | 格式行的写累加器的当前值。 |
$ACCUMULATOR | |
$# | 包含打印数字的输出格式(已弃用)。 |
$OFMT | |
$? | 上次管道关闭、反引号 (``) 命令或系统运算符返回的状态。 |
$CHILD_ERROR | |
$! | 如果在数值上下文中使用,则产生 errno 变量的当前值,标识上次系统调用错误。如果在字符串上下文中使用,则产生相应的系统错误字符串。 |
$OS_ERROR 或 $ERRNO | |
$@ | 来自上次 eval 命令的 Perl 语法错误消息。 |
$EVAL_ERROR | |
$$ | 运行此脚本的 Perl 进程的 pid。 |
$PROCESS_ID 或 $PID | |
$< | 此进程的真实用户 ID (uid)。 |
$REAL_USER_ID 或 $UID | |
$> | 此进程的有效用户 ID。 |
$EFFECTIVE_USER_ID 或 $EUID | |
$( | 此进程的真实组 ID (gid)。 |
$REAL_GROUP_ID 或 $GID | |
$) | 此进程的有效 gid。 |
$EFFECTIVE_GROUP_ID 或 $EGID | |
$0 | 包含正在执行的 Perl 脚本的文件名。 |
$PROGRAM_NAME | |
$[ | 数组中第一个元素和子字符串中第一个字符的索引。默认为 0。 |
$] | 返回版本加上补丁级别除以 1000。 |
$PERL_VERSION | |
$^D | 调试标志的当前值。 |
$DEBUGGING | |
$^E | 某些平台上的扩展错误消息。 |
$EXTENDED_OS_ERROR | |
$^F | 最大系统文件描述符,通常为 2。 |
$SYSTEM_FD_MAX | |
$^H | 包含某些实用模块启用的内部编译器提示。 |
$^I | 就地编辑扩展的当前值。使用 undef 禁用就地编辑。 |
$INPLACE_EDIT | |
$^M | $M 的内容可以用作紧急内存池,以防 Perl 因内存不足错误而崩溃。$M 的使用需要特殊编译的 Perl。有关更多信息,请参阅 INSTALL 文档。 |
$^O | 包含当前 Perl 二进制文件编译的 operating system 的名称。 |
$OSNAME | |
$^P | 调试器清除的内部标志,以便它不调试自身。 |
$PERLDB | |
$^T | 脚本开始运行的时间,以自纪元以来的秒数为单位。 |
$BASETIME | |
$^W | 警告开关的当前值,为 true 或 false。 |
$WARNING | |
$^X | Perl 二进制文件本身执行的名称。 |
$EXECUTABLE_NAME | |
$ARGV | 从 <ARGV> 读取时包含当前文件名的名称。 |
全局数组特殊变量
@ARGV | 包含针对脚本的命令行参数的数组。 |
@INC | 包含要由 do、require 或 use 结构评估的 Perl 脚本查找位置列表的数组。 |
@F | 当给出 -a 命令行开关时,输入行被分割到的数组。 |
全局哈希特殊变量
%INC | 包含已通过 do 或 require 包含的每个文件的 filenames 条目的哈希。 |
%ENV | 包含当前环境的哈希。 |
%SIG | 用于为各种信号设置信号处理程序的哈希。 |
全局特殊文件句柄
ARGV | 特殊文件句柄,迭代 @ARGV 中的命令行文件名。通常在 <> 中写成空文件句柄。 |
STDERR | 任何包中的标准错误的特殊文件句柄。 |
STDIN | 任何包中的标准输入的特殊文件句柄。 |
STDOUT | 任何包中的标准输出的特殊文件句柄。 |
DATA | 特殊文件句柄,引用包含脚本的文件中 __END__ 令牌之后的任何内容。或者,对于所需文件中 __DATA__ 令牌之后的任何内容的特殊文件句柄,只要您在找到 __DATA__ 的同一包中读取数据即可。 |
_(下划线) | 特殊文件句柄,用于缓存来自上次 stat、lstat 或文件测试运算符的信息。 |
全局特殊常量
__END__ | 指示程序的逻辑结尾。任何后续文本都将被忽略,但可以通过 DATA 文件句柄读取。 |
__FILE__ | 表示程序中使用它的点的文件名。不会被插入到字符串中。 |
__LINE__ | 表示当前行号。不会被插入到字符串中。 |
__PACKAGE__ | 表示编译时的当前包名,如果没有当前包,则未定义。不会被插入到字符串中。 |
正则表达式特殊变量
$digit | 包含上次匹配模式中对应括号组匹配到的文本。例如,$1 匹配上一个正则表达式中第一个括号组包含的内容。 |
$& | 上次成功模式匹配匹配到的字符串。 |
$MATCH | |
$` | 上次成功模式匹配之前匹配到的字符串。 |
$PREMATCH | |
$' | 上次成功模式匹配之后匹配到的字符串。 |
$POSTMATCH | |
$+ | 上次搜索模式匹配到的最后一个括号。如果您不知道匹配了一组备选模式中的哪一个,这将非常有用。例如:/Version: (.*)|Revision: (.*)/ && ($rev = $+); |
$LAST_PAREN_MATCH |
文件句柄特殊变量
$| | 如果设置为非零值,则强制在当前选择的输出通道上的每次写入或打印后执行 fflush(3)。 |
$OUTPUT_AUTOFLUSH | |
$% | 当前选择的输出通道的当前页码。 |
$FORMAT_PAGE_NUMBER | |
$= | 当前选择的输出通道的当前页长(可打印行数)。默认为 60。 |
$FORMAT_LINES_PER_PAGE | |
$- | 当前选择的输出通道页面上剩余的行数。 |
$FORMAT_LINES_LEFT | |
$~ | 当前选择的输出通道的当前报表格式的名称。默认为文件句柄的名称。 |
$FORMAT_NAME | |
$^ | 当前选择的输出通道的当前页首格式的名称。默认为文件句柄名称后附加 _TOP。 |
$FORMAT_TOP_NAME |
Perl - 编码规范
当然,每个程序员都会有自己关于格式的偏好,但有一些通用的指导原则可以让您的程序更易于阅读、理解和维护。
最重要的是始终在 -w 标志下运行您的程序。如果您必须这样做,您可以通过 no warnings pragma 或 $^W 变量显式关闭特定代码部分的警告。您还应该始终在 use strict 下运行,或者知道不这样做的原因。use sigtrap 和 even use diagnostics pragma 也可能证明有用。
关于代码布局的美感,Larry 唯一强烈关注的是多行块的闭合大括号应该与启动该结构的关键字对齐。除此之外,他还有其他不太强烈的偏好:
- 4 列缩进。
- 如果可能,在同一行上打开大括号,否则对齐。
- 多行块的左大括号前留一个空格。
- 单行块可以放在一行上,包括大括号。
- 分号前不留空格。
- 在“短”单行块中省略分号。
- 大多数运算符周围留有空格。
- 复杂下标(括号内)周围留有空格。
- 执行不同操作的代码块之间留空行。
- 避免 else 语句嵌套。
- 函数名称与其左括号之间不留空格。
- 每个逗号后留一个空格。
- 长行在运算符之后换行(except 和 and 或除外)。
- 当前行匹配的右括号后留一个空格。
- 垂直对齐对应的项目。
- 只要不影响清晰度,就省略冗余标点符号。
以下是一些其他更重要的风格问题需要考虑:仅仅因为您可以以某种特定方式做某事并不意味着您应该那样做。Perl 旨在为您提供多种方法来完成任何事情,因此请考虑选择最易读的一种。例如:
open(FOO,$foo) || die "Can't open $foo: $!";
比这更好:
die "Can't open $foo: $!" unless open(FOO,$foo);
因为第二种方法将语句的主要要点隐藏在修饰符中。另一方面,
print "Starting analysis\n" if $verbose;
比这更好:
$verbose && print "Starting analysis\n";
因为主要问题不在于用户是否键入了 -v。
当 Perl 提供 last 运算符以便您可以在中间退出时,不要费力地尝试在循环顶部或底部退出循环。只需稍微“缩进”一下,使其更清晰:
LINE: for (;;) { statements; last LINE if $foo; next LINE if /^#/; statements; }
让我们看看更多重要的点:
不要害怕使用循环标签——它们既可以增强可读性,也可以允许多级循环中断。参见前面的示例。
避免在空上下文中使用 grep()(或 map())或反引号,也就是说,当您只是丢弃它们的返回值时。这些函数都有返回值,因此请使用它们。否则,请改用 foreach() 循环或 system() 函数。
为了可移植性,当使用可能并非在每台机器上都实现的功能时,请在 eval 中测试该构造以查看它是否失败。如果您知道某个特定功能是在哪个版本或补丁级别实现的,您可以测试 $](英文中的 $PERL_VERSION)以查看它是否存在。Config 模块还可以让您查询 Perl 安装时 Configure 程序确定的值。
选择助记符标识符。如果您不记得助记符是什么意思,那么您就遇到问题了。
虽然像 $gotit 这样的短标识符可能没问题,但请使用下划线分隔较长标识符中的单词。阅读 $var_names_like_this 通常比 $VarNamesLikeThis 更容易,尤其对于非英语母语人士而言。这也是一个适用于 VAR_NAMES_LIKE_THIS 的简单且一致的规则。
包名称有时是此规则的例外。Perl 非正式地为像 integer 和 strict 这样的“pragma”模块保留小写模块名称。其他模块应该以大写字母开头并使用混合大小写,但可能由于原始文件系统将模块名称表示为必须适合少数稀疏字节的文件的限制而没有下划线。
如果您有一个非常复杂的正则表达式,请使用 /x 修饰符并添加一些空格,使其看起来不那么像噪音。当您的正则表达式包含斜杠或反斜杠时,请不要使用斜杠作为分隔符。
始终检查系统调用的返回代码。良好的错误消息应该发送到 STDERR,包括导致问题的程序、失败的系统调用和参数是什么,以及(非常重要)应该包含导致错误的标准系统错误消息。这是一个简单但足够的示例:
opendir(D, $dir) or die "can't opendir $dir: $!";
考虑可重用性。当您可能想要再次执行类似操作时,为什么要浪费脑力去完成一次性任务?考虑泛化您的代码。考虑编写模块或对象类。考虑让您的代码在使用 use strict 和 use warnings(或 -w)的情况下干净地运行。考虑分发您的代码。考虑改变你的整个世界观。考虑……哦,别介意。
保持一致。
友善待人。
Perl - 正则表达式
正则表达式是由字符组成的字符串,用于定义您正在查看的模式或模式。Perl 中正则表达式的语法与您在其他支持正则表达式的程序(如 **sed**、**grep** 和 **awk**)中看到的非常相似。
应用正则表达式的基本方法是使用模式绑定运算符 =~ 和 **!~**。第一个运算符是测试和赋值运算符。
Perl 中有三个正则表达式运算符。
- 匹配正则表达式 - m//
- 替换正则表达式 - s///
- 转换正则表达式 - tr///
在每种情况下,正斜杠都充当您指定的正则表达式 (regex) 的分隔符。如果您对任何其他分隔符感到满意,那么您可以使用它来代替正斜杠。
匹配运算符
匹配运算符 m// 用于将字符串或语句与正则表达式匹配。例如,要将字符序列“foo”与标量 $bar 匹配,您可以使用这样的语句:
#!/usr/bin/perl $bar = "This is foo and again foo"; if ($bar =~ /foo/) { print "First time is matching\n"; } else { print "First time is not matching\n"; } $bar = "foo"; if ($bar =~ /foo/) { print "Second time is matching\n"; } else { print "Second time is not matching\n"; }
执行上述程序时,将产生以下结果:
First time is matching Second time is matching
m// 的工作方式实际上与 q// 运算符系列相同。您可以使用任何自然匹配字符的组合来充当表达式的分隔符。例如,m{}、m() 和 m>< 都是有效的。因此,上面的例子可以改写如下:
#!/usr/bin/perl $bar = "This is foo and again foo"; if ($bar =~ m[foo]) { print "First time is matching\n"; } else { print "First time is not matching\n"; } $bar = "foo"; if ($bar =~ m{foo}) { print "Second time is matching\n"; } else { print "Second time is not matching\n"; }
如果分隔符是正斜杠,则可以省略 m// 中的 m,但对于所有其他分隔符,都必须使用 m 前缀。
请注意,整个匹配表达式(即 =~ 或 !~ 左侧的表达式和匹配运算符)在标量上下文中返回 true(如果表达式匹配)。因此,语句:
$true = ($foo =~ m/foo/);
如果 $foo 与正则表达式匹配,则将 $true 设置为 1,如果匹配失败则设置为 0。在列表上下文中,匹配返回任何分组表达式的內容。例如,当从时间字符串中提取小时、分钟和秒时,我们可以使用:
my ($hours, $minutes, $seconds) = ($time =~ m/(\d+):(\d+):(\d+)/);
匹配运算符修饰符
匹配运算符支持它自己的一组修饰符。/g 修饰符允许全局匹配。/i 修饰符将使匹配不区分大小写。以下是修饰符的完整列表:
序号 | 修饰符和说明 |
---|---|
1 | i 使匹配不区分大小写。 |
2 | m 指定如果字符串包含换行符或回车符,则 ^ 和 $ 运算符现在将与换行符边界匹配,而不是字符串边界。 |
3 | o 仅评估表达式一次。 |
4 | s 允许使用 . 来匹配换行符。 |
5 | x 允许您在表达式中使用空格以提高清晰度。 |
6 | g 全局查找所有匹配项。 |
7 | cg 允许搜索即使在全局匹配失败后也能继续。 |
仅匹配一次
匹配运算符还有一个更简单的版本 - ?PATTERN? 运算符。这基本上与 m// 运算符相同,只是它在每次调用重置之间只匹配字符串中的一次。
例如,您可以使用它来获取列表中的第一个和最后一个元素:
#!/usr/bin/perl @list = qw/food foosball subeo footnote terfoot canic footbrdige/; foreach (@list) { $first = $1 if /(foo.*?)/; $last = $1 if /(foo.*)/; } print "First: $first, Last: $last\n";
执行上述程序时,将产生以下结果:
First: foo, Last: footbrdige
正则表达式变量
正则表达式变量包括 **$**,它包含上次分组匹配匹配到的内容;**$&**,它包含整个匹配字符串;**$`**,它包含匹配字符串之前的所有内容;以及 **$'**,它包含匹配字符串之后的所有内容。下面的代码演示了结果:
#!/usr/bin/perl $string = "The food is in the salad bar"; $string =~ m/foo/; print "Before: $`\n"; print "Matched: $&\n"; print "After: $'\n";
执行上述程序时,将产生以下结果:
Before: The Matched: foo After: d is in the salad bar
替换运算符
替换运算符 s/// 实际上只是匹配运算符的扩展,允许您将匹配的文本替换为一些新文本。运算符的基本形式是:
s/PATTERN/REPLACEMENT/;
PATTERN 是我们正在寻找的文本的正则表达式。REPLACEMENT 是我们想要用来替换找到的文本的文本或正则表达式的规范。例如,我们可以使用以下正则表达式将所有出现的 **dog** 替换为 **cat**:
#/user/bin/perl $string = "The cat sat on the mat"; $string =~ s/cat/dog/; print "$string\n";
执行上述程序时,将产生以下结果:
The dog sat on the mat
替换运算符修饰符
以下是与替换运算符一起使用的所有修饰符的列表。
序号 | 修饰符和说明 |
---|---|
1 | i 使匹配不区分大小写。 |
2 | m 指定如果字符串包含换行符或回车符,则 ^ 和 $ 运算符现在将与换行符边界匹配,而不是字符串边界。 |
3 | o 仅评估表达式一次。 |
4 | s 允许使用 . 来匹配换行符。 |
5 | x 允许您在表达式中使用空格以提高清晰度。 |
6 | g 将所有找到的表达式的出现替换为替换文本。 |
7 | e 将替换作为 Perl 语句进行评估,并将其返回值用作替换文本。 |
转换运算符
翻译类似于,但并非完全等同于替换原则,但与替换不同的是,翻译(或音译)不使用正则表达式进行搜索和替换值。翻译运算符为:
tr/SEARCHLIST/REPLACEMENTLIST/cds y/SEARCHLIST/REPLACEMENTLIST/cds
翻译将SEARCHLIST中所有出现的字符替换为REPLACEMENTLIST中对应的字符。例如,使用我们在本章中一直在使用的字符串“The cat sat on the mat.”:
#/user/bin/perl $string = 'The cat sat on the mat'; $string =~ tr/a/o/; print "$string\n";
执行上述程序时,将产生以下结果:
The cot sot on the mot.
也可以使用标准的Perl范围,允许您按字母或数值指定字符范围。要更改字符串的大小写,您可以使用以下语法代替uc函数。
$string =~ tr/a-z/A-Z/;
翻译运算符修饰符
以下是与翻译相关的运算符列表。
序号 | 修饰符和说明 |
---|---|
1 | c 补充SEARCHLIST。 |
2 | d 删除找到但未替换的字符。 |
3 | s 压缩重复的替换字符。 |
/d修饰符删除与SEARCHLIST匹配但REPLACEMENTLIST中没有对应条目的字符。例如:
#!/usr/bin/perl $string = 'the cat sat on the mat.'; $string =~ tr/a-z/b/d; print "$string\n";
执行上述程序时,将产生以下结果:
b b b.
最后一个修饰符/s删除被替换的重复字符序列,因此:
#!/usr/bin/perl $string = 'food'; $string = 'food'; $string =~ tr/a-z/a-z/s; print "$string\n";
执行上述程序时,将产生以下结果:
fod
更复杂的正则表达式
您不必只匹配固定字符串。事实上,通过使用更复杂的正则表达式,您可以匹配几乎任何您能想到的东西。这是一个快速备忘单:
下表列出了Python中可用的正则表达式语法。
序号 | 模式与描述 |
---|---|
1 | ^ 匹配行首。 |
2 | $ 匹配行尾。 |
3 | . 匹配除换行符之外的任何单个字符。使用m选项也可以匹配换行符。 |
4 | [...] 匹配括号中任何单个字符。 |
5 | [^...] 匹配括号中不存在的任何单个字符。 |
6 | * 匹配前面表达式的0次或多次出现。 |
7 | + 匹配前面表达式的1次或多次出现。 |
8 | ? 匹配前面表达式的0次或1次出现。 |
9 | { n} 匹配前面表达式恰好n次出现。 |
10 | { n,} 匹配前面表达式n次或多次出现。 |
11 | { n, m} 匹配前面表达式至少n次,至多m次出现。 |
12 | a| b 匹配a或b。 |
13 | \w 匹配单词字符。 |
14 | \W 匹配非单词字符。 |
15 | \s 匹配空格。等同于[\t\n\r\f]。 |
16 | \S 匹配非空格。 |
17 | \d 匹配数字。等同于[0-9]。 |
18 | \D 匹配非数字。 |
19 | \A 匹配字符串开头。 |
20 | \Z 匹配字符串结尾。如果存在换行符,则匹配换行符之前的字符。 |
21 | \z 匹配字符串结尾。 |
22 | \G 匹配上次匹配结束的位置。 |
23 | \b 在括号外匹配单词边界。在括号内匹配退格键 (0x08)。 |
24 | \B 匹配非单词边界。 |
25 | \n, \t, etc. 匹配换行符、回车符、制表符等。 |
26 | \1...\9 匹配第n个分组子表达式。 |
27 | \10 如果第n个分组子表达式已匹配,则匹配它。否则引用字符代码的八进制表示。 |
28 | [aeiou] 匹配给定集合中的单个字符 |
29 | [^aeiou] 匹配给定集合之外的单个字符 |
^元字符匹配字符串的开头,$元符号匹配字符串的结尾。以下是一些简短的示例。
# nothing in the string (start and end are adjacent) /^$/ # a three digits, each followed by a whitespace # character (eg "3 4 5 ") /(\d\s) {3}/ # matches a string in which every # odd-numbered letter is a (eg "abacadaf") /(a.)+/ # string starts with one or more digits /^\d+/ # string that ends with one or more digits /\d+$/
让我们看看另一个例子。
#!/usr/bin/perl $string = "Cats go Catatonic\nWhen given Catnip"; ($start) = ($string =~ /\A(.*?) /); @lines = $string =~ /^(.*?) /gm; print "First word: $start\n","Line starts: @lines\n";
执行上述程序时,将产生以下结果:
First word: Cats Line starts: Cats When
匹配边界
\b 匹配任何单词边界,由 \w 类和 \W 类之间的差异定义。因为 \w 包括单词的字符,而 \W 包括相反的字符,所以这通常意味着单词的终止。\B 断言匹配任何不是单词边界的位置。例如:
/\bcat\b/ # Matches 'the cat sat' but not 'cat on the mat' /\Bcat\B/ # Matches 'verification' but not 'the cat on the mat' /\bcat\B/ # Matches 'catatonic' but not 'polecat' /\Bcat\b/ # Matches 'polecat' but not 'catatonic'
选择备选方案
| 字符就像 Perl 中的标准或按位 OR 一样。它指定正则表达式或组中的备选匹配。例如,要在表达式中匹配“cat”或“dog”,您可以使用:
if ($string =~ /cat|dog/)
您可以将表达式的各个元素组合在一起,以支持复杂的匹配。搜索两个人的姓名可以通过两个单独的测试来实现,如下所示:
if (($string =~ /Martin Brown/) || ($string =~ /Sharon Brown/)) This could be written as follows if ($string =~ /(Martin|Sharon) Brown/)
分组匹配
从正则表达式的角度来看,两者之间没有区别,也许除了前者稍微清晰一些。
$string =~ /(\S+)\s+(\S+)/; and $string =~ /\S+\s+\S+/;
但是,分组的好处是它允许我们从正则表达式中提取序列。分组按其在原始表达式中出现的顺序返回为列表。例如,在下面的片段中,我们从字符串中提取了小时、分钟和秒。
my ($hours, $minutes, $seconds) = ($time =~ m/(\d+):(\d+):(\d+)/);
除了这种直接方法外,匹配的组也可用在特殊的$x变量中,其中x是正则表达式中组的编号。因此,我们可以将前面的示例改写如下:
#!/usr/bin/perl $time = "12:05:30"; $time =~ m/(\d+):(\d+):(\d+)/; my ($hours, $minutes, $seconds) = ($1, $2, $3); print "Hours : $hours, Minutes: $minutes, Second: $seconds\n";
执行上述程序时,将产生以下结果:
Hours : 12, Minutes: 05, Second: 30
当在替换表达式中使用组时,$x语法可以在替换文本中使用。因此,我们可以使用以下方法重新格式化日期字符串:
#!/usr/bin/perl $date = '03/26/1999'; $date =~ s#(\d+)/(\d+)/(\d+)#$3/$1/$2#; print "$date\n";
执行上述程序时,将产生以下结果:
1999/03/26
\G 断言
\G 断言允许您从上次匹配发生的位置继续搜索。例如,在下面的代码中,我们使用了 \G,以便我们可以搜索到正确的位置,然后提取一些信息,而无需创建更复杂的单个正则表达式:
#!/usr/bin/perl $string = "The time is: 12:31:02 on 4/12/00"; $string =~ /:\s+/g; ($time) = ($string =~ /\G(\d+:\d+:\d+)/); $string =~ /.+\s+/g; ($date) = ($string =~ m{\G(\d+/\d+/\d+)}); print "Time: $time, Date: $date\n";
执行上述程序时,将产生以下结果:
Time: 12:31:02, Date: 4/12/00
\G 断言实际上只是 pos 函数的元符号等价物,因此在正则表达式调用之间,您可以继续使用 pos,甚至可以通过使用 pos 作为 lvalue 子例程来修改 pos(因此是 \G)的值。
正则表达式示例
字面字符
序号 | 示例与描述 |
---|---|
1 | Perl 匹配“Perl”。 |
字符类
序号 | 示例与描述 |
---|---|
1 | [Pp]ython 匹配“Python”或“python”。 |
2 | rub[ye] 匹配“ruby”或“rube”。 |
3 | [aeiou] 匹配任何一个小写元音。 |
4 | [0-9] 匹配任何数字;与 [0123456789] 相同。 |
5 | [a-z] 匹配任何小写 ASCII 字母。 |
6 | [A-Z] 匹配任何大写 ASCII 字母。 |
7 | [a-zA-Z0-9] 匹配以上任何一个。 |
8 | [^aeiou] 匹配任何非小写元音。 |
9 | [^0-9] 匹配任何非数字。 |
特殊字符类
序号 | 示例与描述 |
---|---|
1 | . 匹配除换行符之外的任何字符。 |
2 | \d 匹配数字:[0-9] |
3 | \D 匹配非数字:[0-9] |
4 | \s 匹配空格字符:[ \t\r\n\f] |
5 | \S 匹配非空格:[ \t\r\n\f] |
6 | \w 匹配单个单词字符:[A-Za-z0-9_] |
7 | \W 匹配非单词字符:[A-Za-z0-9_] |
重复情况
序号 | 示例与描述 |
---|---|
1 | ruby? 匹配“rub”或“ruby”:y 是可选的。 |
2 | ruby* 匹配“rub”加上 0 个或多个 y。 |
3 | ruby+ 匹配“rub”加上 1 个或多个 y。 |
4 | \d{3} 匹配正好 3 个数字。 |
5 | \d{3,} 匹配 3 个或更多数字。 |
6. | \d{3,5} 匹配 3、4 或 5 个数字。 |
非贪婪重复
这匹配最少的重复次数:
序号 | 示例与描述 |
---|---|
1 | <.*> 贪婪重复:匹配“<python>perl>”。 |
2 | <.*?> 非贪婪:在“<python>perl>”中匹配“<python>”。 |
使用括号进行分组
序号 | 示例与描述 |
---|---|
1 | \D\d+ 无分组:+ 重复 \d。 |
2 | (\D\d)+ 分组:+ 重复 \D\d 对。 |
3 | ([Pp]ython(, )?)+ 匹配“Python”、“Python, python, python”等。 |
反向引用
这再次匹配先前匹配的组:
序号 | 示例与描述 |
---|---|
1 | ([Pp])ython&\1ails 匹配 python&pails 或 Python&Pails。 |
2 | (['"])[^\1]*\1 单引号或双引号字符串。\1 匹配第一个组匹配的任何内容。\2 匹配第二个组匹配的任何内容,依此类推。 |
备选方案
序号 | 示例与描述 |
---|---|
1 | python|perl 匹配“python”或“perl”。 |
2 | rub(y|le)) 匹配“ruby”或“ruble”。 |
3 | Python(!+|\?) “Python”后跟一个或多个 ! 或一个 ?。 |
锚点
这需要指定匹配位置。
序号 | 示例与描述 |
---|---|
1 | ^Python 匹配字符串或内部行的开头的“Python”。 |
2 | Python$ 匹配字符串或行结尾的“Python”。 |
3 | \APython 匹配字符串开头的“Python”。 |
4 | Python\Z 匹配字符串结尾的“Python”。 |
5 | \bPython\b 匹配单词边界的“Python”。 |
6 | \brub\B \B是非单词边界:在“rube”和“ruby”中匹配“rub”,但不能单独匹配。 |
7 | Python(?=!) 如果后跟感叹号,则匹配“Python”。 |
8 | Python(?!!) 如果后不跟感叹号,则匹配“Python”。 |
带括号的特殊语法
序号 | 示例与描述 |
---|---|
1 | R(?#comment) 匹配“R”。其余都是注释。 |
2 | R(?i)uby 匹配“uby”时不区分大小写。 |
3 | R(?i:uby) 与上面相同。 |
4 | rub(?:y|le)) 仅分组而不创建 \1 反向引用。 |
Perl - 发送邮件
使用sendmail实用程序
发送纯文本邮件
如果您在Linux/Unix机器上工作,那么您可以简单地在Perl程序中使用sendmail实用程序来发送电子邮件。这是一个可以将电子邮件发送到给定电子邮件ID的示例脚本。只需确保sendmail实用程序的给定路径正确即可。对于您的Linux/Unix机器,这可能有所不同。
#!/usr/bin/perl $to = '[email protected]'; $from = '[email protected]'; $subject = 'Test Email'; $message = 'This is test email sent by Perl Script'; open(MAIL, "|/usr/sbin/sendmail -t"); # Email Header print MAIL "To: $to\n"; print MAIL "From: $from\n"; print MAIL "Subject: $subject\n\n"; # Email Body print MAIL $message; close(MAIL); print "Email Sent Successfully\n";
实际上,上面的脚本是一个客户端电子邮件脚本,它将起草电子邮件并提交到您的Linux/Unix机器上本地运行的服务器。此脚本不负责将电子邮件发送到实际目的地。因此,您必须确保电子邮件服务器已正确配置并在您的机器上运行,才能将电子邮件发送到给定的电子邮件ID。
发送HTML邮件
如果您想使用sendmail发送HTML格式的电子邮件,那么您只需要在电子邮件的标题部分添加Content-type: text/html\n,如下所示:
#!/usr/bin/perl $to = '[email protected]'; $from = '[email protected]'; $subject = 'Test Email'; $message = '<h1>This is test email sent by Perl Script</h1>'; open(MAIL, "|/usr/sbin/sendmail -t"); # Email Header print MAIL "To: $to\n"; print MAIL "From: $from\n"; print MAIL "Subject: $subject\n\n"; print MAIL "Content-type: text/html\n"; # Email Body print MAIL $message; close(MAIL); print "Email Sent Successfully\n";
使用MIME::Lite模块
如果您在Windows机器上工作,那么您将无法访问sendmail实用程序。但是,您可以使用MIME:Lite perl模块编写您自己的电子邮件客户端作为替代。您可以从此处下载此模块 MIME-Lite-3.01.tar.gz 并将其安装在您的Windows或Linux/Unix机器上。要安装它,请按照以下简单步骤操作:
$tar xvfz MIME-Lite-3.01.tar.gz $cd MIME-Lite-3.01 $perl Makefile.PL $make $make install
就是这样,您的机器上将安装MIME::Lite模块。现在您可以使用下面解释的简单脚本来发送电子邮件了。
发送纯文本邮件
下面是一个负责向指定邮箱发送邮件的脚本:
#!/usr/bin/perl use MIME::Lite; $to = '[email protected]'; $cc = '[email protected]'; $from = '[email protected]'; $subject = 'Test Email'; $message = 'This is test email sent by Perl Script'; $msg = MIME::Lite->new( From => $from, To => $to, Cc => $cc, Subject => $subject, Data => $message ); $msg->send; print "Email Sent Successfully\n";
发送HTML邮件
如果您想使用sendmail发送HTML格式的邮件,只需在邮件的头部添加Content-type: text/html\n即可。下面是发送HTML格式邮件的脚本:
#!/usr/bin/perl use MIME::Lite; $to = '[email protected]'; $cc = '[email protected]'; $from = '[email protected]'; $subject = 'Test Email'; $message = '<h1>This is test email sent by Perl Script</h1>'; $msg = MIME::Lite->new( From => $from, To => $to, Cc => $cc, Subject => $subject, Data => $message ); $msg->attr("content-type" => "text/html"); $msg->send; print "Email Sent Successfully\n";
发送附件
如果您想发送附件,可以使用以下脚本:
#!/usr/bin/perl use MIME::Lite; $to = '[email protected]'; $cc = '[email protected]'; $from = '[email protected]'; $subject = 'Test Email'; $message = 'This is test email sent by Perl Script'; $msg = MIME::Lite->new( From => $from, To => $to, Cc => $cc, Subject => $subject, Type => 'multipart/mixed' ); # Add your text message. $msg->attach(Type => 'text', Data => $message ); # Specify your file as attachement. $msg->attach(Type => 'image/gif', Path => '/tmp/logo.gif', Filename => 'logo.gif', Disposition => 'attachment' ); $msg->send; print "Email Sent Successfully\n";
您可以使用attach()方法在邮件中添加任意数量的附件。
使用SMTP服务器
如果您的机器没有运行邮件服务器,您可以使用远程位置的任何其他邮件服务器。但是,要使用其他邮件服务器,您需要拥有用户名、密码、URL等信息。一旦您拥有所有必要的信息,只需在send()方法中提供这些信息,如下所示:
$msg->send('smtp', "smtp.myisp.net", AuthUser=>"id", AuthPass=>"password" );
您可以联系您的邮件服务器管理员获取上述信息,如果用户名和密码尚不存在,您的管理员可以在几分钟内创建它们。
Perl - 套接字编程
什么是套接字?
套接字是伯克利UNIX中的一种机制,用于在不同的进程之间创建虚拟双工连接。后来,它被移植到所有已知的操作系统上,从而实现了在运行不同操作系统软件的不同地理位置的系统之间的通信。如果没有套接字,大多数系统之间的网络通信将永远不会发生。
更仔细地观察一下;网络上的典型计算机系统会根据其上运行的各种应用程序的要求接收和发送信息。由于为其指定了唯一的IP地址,因此此信息被路由到系统。在系统上,此信息将被提供给相关的应用程序,这些应用程序监听不同的端口。例如,互联网浏览器监听端口80以接收来自Web服务器的信息。我们还可以编写自定义应用程序,这些应用程序可以监听并在特定端口号上发送/接收信息。
现在,让我们总结一下,套接字是一个IP地址和一个端口,可以启用连接以通过网络发送和接收数据。
为了解释上述套接字概念,我们将以使用Perl的客户端-服务器编程为例。要完成客户端服务器架构,我们将需要执行以下步骤:
创建服务器
使用socket调用创建套接字。
使用bind调用将套接字绑定到端口地址。
使用listen调用监听端口地址上的套接字。
使用accept调用接受客户端连接。
创建客户端
使用socket调用创建套接字。
使用connect调用连接(套接字)到服务器。
下图显示了客户端和服务器用于相互通信的调用的完整序列:
服务器端套接字调用
socket() 调用
socket()调用是建立网络连接的第一步,它创建了一个套接字。此调用的语法如下:
socket( SOCKET, DOMAIN, TYPE, PROTOCOL );
上述调用创建一个SOCKET,其他三个参数是整数,对于TCP/IP连接应具有以下值。
DOMAIN应为PF_INET。在您的计算机上可能是2。
TYPE对于TCP/IP连接应为SOCK_STREAM。
PROTOCOL应为(getprotobyname('tcp'))[2]。它是通过套接字使用的特定协议,例如TCP。
因此,服务器发出的socket函数调用将类似于:
use Socket # This defines PF_INET and SOCK_STREAM socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2]);
bind() 调用
由socket()调用创建的套接字在绑定到主机名和端口号之前是无用的。服务器使用以下bind()函数来指定它将接受客户端连接的端口。
bind( SOCKET, ADDRESS );
这里SOCKET是由socket()调用返回的描述符,ADDRESS是套接字地址(对于TCP/IP),包含三个元素:
地址族(对于TCP/IP,是AF_INET,在您的系统上可能是2)。
端口号(例如21)。
计算机的互联网地址(例如10.12.12.168)。
由于bind()由服务器使用,服务器不需要知道它自己的地址,因此参数列表如下所示:
use Socket # This defines PF_INET and SOCK_STREAM $port = 12345; # The unique port used by the sever to listen requests $server_ip_address = "10.12.12.168"; bind( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address))) or die "Can't bind to port $port! \n";
or die子句非常重要,因为如果服务器在没有未完成连接的情况下死亡,除非您使用setsockopt()函数使用选项SO_REUSEADDR,否则端口将不会立即可重用。这里使用pack_sockaddr_in()函数将端口和IP地址打包成二进制格式。
listen() 调用
如果这是一个服务器程序,则需要在指定的端口上发出对listen()的调用以进行监听,即等待传入请求。此调用的语法如下:
listen( SOCKET, QUEUESIZE );
上述调用使用socket()调用返回的SOCKET描述符,QUEUESIZE是允许同时存在的最大未完成连接请求数。
accept() 调用
如果这是一个服务器程序,则需要发出对access()函数的调用以接受传入的连接。此调用的语法如下:
accept( NEW_SOCKET, SOCKET );
accept调用接收socket()函数返回的SOCKET描述符,成功完成时,将返回一个新的套接字描述符NEW_SOCKET,用于客户端和服务器之间的所有未来通信。如果access()调用失败,则返回我们在初始阶段使用的Socket模块中定义的FLASE。
通常,accept()用于无限循环中。一旦一个连接到达,服务器要么创建一个子进程来处理它,要么自己服务它,然后返回去监听更多连接。
while(1) { accept( NEW_SOCKET, SOCKT ); ....... }
现在所有与服务器相关的调用都已完成,让我们看看客户端将需要的调用。
客户端套接字调用
connect() 调用
如果您要准备客户端程序,那么首先您将使用socket()调用创建套接字,然后您必须使用connect()调用连接到服务器。您已经看到了socket()调用的语法,它与服务器socket()调用类似,但这里是connect()调用的语法:
connect( SOCKET, ADDRESS );
这里SCOKET是客户端发出的socket()调用返回的套接字描述符,ADDRESS是与bind调用类似的套接字地址,不同之处在于它包含远程服务器的IP地址。
$port = 21; # For example, the ftp port $server_ip_address = "10.12.12.168"; connect( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address))) or die "Can't connect to port $port! \n";
如果您成功连接到服务器,则可以使用SOCKET描述符开始向服务器发送命令,否则您的客户端将通过显示错误消息退出。
客户端-服务器示例
以下是用Perl套接字实现简单的客户端-服务器程序的Perl代码。这里服务器监听传入请求,一旦建立连接,它就简单地回复Smile from the server。客户端读取该消息并打印到屏幕上。让我们看看它是如何完成的,假设我们的服务器和客户端在同一台机器上。
创建服务器的脚本
#!/usr/bin/perl -w # Filename : server.pl use strict; use Socket; # use port 7890 as default my $port = shift || 7890; my $proto = getprotobyname('tcp'); my $server = "localhost"; # Host IP running the server # create a socket, make it reusable socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die "Can't open socket $!\n"; setsockopt(SOCKET, SOL_SOCKET, SO_REUSEADDR, 1) or die "Can't set socket option to SO_REUSEADDR $!\n"; # bind to a port, then listen bind( SOCKET, pack_sockaddr_in($port, inet_aton($server))) or die "Can't bind to port $port! \n"; listen(SOCKET, 5) or die "listen: $!"; print "SERVER started on port $port\n"; # accepting a connection my $client_addr; while ($client_addr = accept(NEW_SOCKET, SOCKET)) { # send them a message, close connection my $name = gethostbyaddr($client_addr, AF_INET ); print NEW_SOCKET "Smile from the server"; print "Connection recieved from $name\n"; close NEW_SOCKET; }
要在后台模式下运行服务器,请在Unix提示符下发出以下命令:
$perl sever.pl&
创建客户端的脚本
!/usr/bin/perl -w # Filename : client.pl use strict; use Socket; # initialize host and port my $host = shift || 'localhost'; my $port = shift || 7890; my $server = "localhost"; # Host IP running the server # create the socket, connect to the port socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2]) or die "Can't create a socket $!\n"; connect( SOCKET, pack_sockaddr_in($port, inet_aton($server))) or die "Can't connect to port $port! \n"; my $line; while ($line = <SOCKET>) { print "$line\n"; } close SOCKET or die "close: $!";
现在让我们在命令提示符下启动我们的客户端,它将连接到服务器并读取服务器发送的消息,并在屏幕上显示如下:
$perl client.pl Smile from the server
注意 - 如果您使用点分十进制表示法提供实际IP地址,则建议在客户端和服务器中都使用相同的格式提供IP地址,以避免任何混淆。
Perl中的面向对象编程
我们已经学习了Perl中的引用以及Perl匿名数组和哈希表。Perl中的面向对象概念很大程度上基于引用以及匿名数组和哈希表。让我们开始学习Perl面向对象的其本概念。
对象基础
从Perl如何处理对象的角度来看,有三个主要术语。这些术语是对象、类和方法。
Perl中的对象仅仅是对知道其所属类的某种数据类型的引用。对象作为引用存储在标量变量中。因为标量只包含对对象的引用,所以同一个标量可以在不同的类中保存不同的对象。
Perl中的类是一个包含创建和操作对象所需相应方法的包。
Perl中的方法是一个子例程,它使用包进行定义。方法的第一个参数是对象引用或包名称,这取决于方法是影响当前对象还是类。
Perl提供了一个bless()函数,用于返回最终成为对象的引用。
定义类
在Perl中定义类非常简单。在最简单的形式中,类对应于Perl包。要在Perl中创建类,我们首先构建一个包。
包是用户定义变量和子例程的自包含单元,可以重复使用。
Perl包在Perl程序中提供一个单独的命名空间,它使子例程和变量独立于与其他包中的那些变量冲突。
要在Perl中声明名为Person的类,我们这样做:
package Person;
包定义的作用域扩展到文件的末尾,或者直到遇到另一个package关键字。
创建和使用对象
要创建类的实例(对象),我们需要一个对象构造函数。此构造函数是在包中定义的方法。大多数程序员选择将此对象构造函数方法命名为new,但在Perl中您可以使用任何名称。
您可以在Perl中使用任何类型的Perl变量作为对象。大多数Perl程序员选择数组或哈希的引用。
让我们使用Perl哈希引用为Person类创建构造函数。创建对象时,需要提供构造函数,它是包中的一个子例程,它返回对象引用。对象引用是通过将对包类的引用进行祝福来创建的。例如:
package Person; sub new { my $class = shift; my $self = { _firstName => shift, _lastName => shift, _ssn => shift, }; # Print all the values just for clarification. print "First Name is $self->{_firstName}\n"; print "Last Name is $self->{_lastName}\n"; print "SSN is $self->{_ssn}\n"; bless $self, $class; return $self; }
现在让我们看看如何创建对象。
$object = new Person( "Mohammad", "Saleem", 23234345);
如果您不想为任何类变量赋值,可以在构造函数中使用简单的哈希。例如:
package Person; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; }
定义方法
其他面向对象的语言具有数据安全性的概念,以防止程序员直接更改对象数据,并且它们提供访问器方法来修改对象数据。Perl没有私有变量,但我们仍然可以使用辅助方法的概念来操作对象数据。
让我们定义一个辅助方法来获取人的名字:
sub getFirstName { return $self->{_firstName}; }
另一个设置人名的辅助函数:
sub setFirstName { my ( $self, $firstName ) = @_; $self->{_firstName} = $firstName if defined($firstName); return $self->{_firstName}; }
现在让我们看看完整的示例:将Person包和辅助函数放入Person.pm文件中。
#!/usr/bin/perl package Person; sub new { my $class = shift; my $self = { _firstName => shift, _lastName => shift, _ssn => shift, }; # Print all the values just for clarification. print "First Name is $self->{_firstName}\n"; print "Last Name is $self->{_lastName}\n"; print "SSN is $self->{_ssn}\n"; bless $self, $class; return $self; } sub setFirstName { my ( $self, $firstName ) = @_; $self->{_firstName} = $firstName if defined($firstName); return $self->{_firstName}; } sub getFirstName { my( $self ) = @_; return $self->{_firstName}; } 1;
现在让我们在employee.pl文件中使用Person对象,如下所示:
#!/usr/bin/perl use Person; $object = new Person( "Mohammad", "Saleem", 23234345); # Get first name which is set using constructor. $firstName = $object->getFirstName(); print "Before Setting First Name is : $firstName\n"; # Now Set first name using helper function. $object->setFirstName( "Mohd." ); # Now get first name set by helper function. $firstName = $object->getFirstName(); print "Before Setting First Name is : $firstName\n";
当我们执行上述程序时,它会产生以下结果:
First Name is Mohammad Last Name is Saleem SSN is 23234345 Before Setting First Name is : Mohammad Before Setting First Name is : Mohd.
继承
面向对象编程有一个非常好用的概念叫做继承。继承简单来说就是父类的属性和方法将可用于子类。因此,您不必一遍遍地编写相同的代码,您可以直接继承父类。
例如,我们可以有一个Employee类,它继承自Person类。这被称为“isa”关系,因为员工是一个人。Perl有一个特殊的变量@ISA来帮助实现这一点。@ISA控制(方法)继承。
使用继承时,需要考虑以下重要事项:
Perl在指定对象的类中搜索给定的方法或属性,即变量。
Perl搜索在对象类的@ISA数组中定义的类。
如果在步骤1或2中找不到方法,则Perl使用AUTOLOAD子例程(如果在@ISA树中找到一个)。
如果仍然找不到匹配的方法,则Perl将在作为标准Perl库一部分的UNIVERSAL类(包)中搜索该方法。
如果仍然找不到该方法,则Perl将放弃并引发运行时异常。
因此,要创建一个新的 Employee 类,继承 Person 类的所有方法和属性,只需编写如下代码:将此代码保存到 Employee.pm 文件中。
#!/usr/bin/perl package Employee; use Person; use strict; our @ISA = qw(Person); # inherits from Person
现在 Employee 类继承了 Person 类所有的方法和属性,您可以按如下方式使用它们:使用 main.pl 文件进行测试 -
#!/usr/bin/perl use Employee; $object = new Employee( "Mohammad", "Saleem", 23234345); # Get first name which is set using constructor. $firstName = $object->getFirstName(); print "Before Setting First Name is : $firstName\n"; # Now Set first name using helper function. $object->setFirstName( "Mohd." ); # Now get first name set by helper function. $firstName = $object->getFirstName(); print "After Setting First Name is : $firstName\n";
当我们执行上述程序时,它会产生以下结果:
First Name is Mohammad Last Name is Saleem SSN is 23234345 Before Setting First Name is : Mohammad Before Setting First Name is : Mohd.
方法重写
子类 Employee 继承了父类 Person 的所有方法。但是,如果您想在子类中重写这些方法,可以通过提供您自己的实现来做到这一点。您可以在子类中添加额外的函数,或者添加或修改父类中现有方法的功能。操作方法如下:修改 Employee.pm 文件。
#!/usr/bin/perl package Employee; use Person; use strict; our @ISA = qw(Person); # inherits from Person # Override constructor sub new { my ($class) = @_; # Call the constructor of the parent class, Person. my $self = $class->SUPER::new( $_[1], $_[2], $_[3] ); # Add few more attributes $self->{_id} = undef; $self->{_title} = undef; bless $self, $class; return $self; } # Override helper function sub getFirstName { my( $self ) = @_; # This is child class function. print "This is child class helper function\n"; return $self->{_firstName}; } # Add more methods sub setLastName{ my ( $self, $lastName ) = @_; $self->{_lastName} = $lastName if defined($lastName); return $self->{_lastName}; } sub getLastName { my( $self ) = @_; return $self->{_lastName}; } 1;
现在让我们再次尝试在 main.pl 文件中使用 Employee 对象并执行它。
#!/usr/bin/perl use Employee; $object = new Employee( "Mohammad", "Saleem", 23234345); # Get first name which is set using constructor. $firstName = $object->getFirstName(); print "Before Setting First Name is : $firstName\n"; # Now Set first name using helper function. $object->setFirstName( "Mohd." ); # Now get first name set by helper function. $firstName = $object->getFirstName(); print "After Setting First Name is : $firstName\n";
当我们执行上述程序时,它会产生以下结果:
First Name is Mohammad Last Name is Saleem SSN is 23234345 This is child class helper function Before Setting First Name is : Mohammad This is child class helper function After Setting First Name is : Mohd.
默认自动加载
Perl 提供了一个在其他编程语言中找不到的功能:默认子程序。这意味着,如果您定义了一个名为 AUTOLOAD() 的函数,则对未定义子程序的任何调用都将自动调用 AUTOLOAD() 函数。丢失的子程序的名称在此子程序中作为 $AUTOLOAD 可访问。
默认自动加载功能对于错误处理非常有用。这是一个实现 AUTOLOAD 的示例,您可以根据自己的方式实现此函数。
sub AUTOLOAD { my $self = shift; my $type = ref ($self) || croak "$self is not an object"; my $field = $AUTOLOAD; $field =~ s/.*://; unless (exists $self->{$field}) { croak "$field does not exist in object/class $type"; } if (@_) { return $self->($name) = shift; } else { return $self->($name); } }
析构函数和垃圾回收
如果您之前使用过面向对象的编程,那么您就会意识到需要创建一个 析构函数 来释放使用完对象后分配给对象的内存。Perl 会在对象超出作用域后自动执行此操作。
如果您想实现自己的析构函数,它应该负责关闭文件或执行一些额外的处理,那么您需要定义一个名为 DESTROY 的特殊方法。此方法将在 Perl 释放分配给它的内存之前立即在对象上调用。在所有其他方面,DESTROY 方法与任何其他方法一样,您可以在此方法内实现任何您想要的逻辑。
析构函数只是一个名为 DESTROY 的成员函数(子程序),它将在以下情况下自动调用 -
- 当对象引用的变量超出作用域时。
- 当对象引用的变量被 undef 时。
- 当脚本终止时
- 当 Perl 解释器终止时
例如,您可以简单地将以下 DESTROY 方法放入您的类中 -
package MyClass; ... sub DESTROY { print "MyClass::DESTROY called\n"; }
面向对象 Perl 示例
这是一个很好的例子,它将帮助您理解 Perl 的面向对象概念。将此源代码放入任何 Perl 文件中并执行它。
#!/usr/bin/perl # Following is the implementation of simple Class. package MyClass; sub new { print "MyClass::new called\n"; my $type = shift; # The package/type name my $self = {}; # Reference to empty hash return bless $self, $type; } sub DESTROY { print "MyClass::DESTROY called\n"; } sub MyMethod { print "MyClass::MyMethod called!\n"; } # Following is the implemnetation of Inheritance. package MySubClass; @ISA = qw( MyClass ); sub new { print "MySubClass::new called\n"; my $type = shift; # The package/type name my $self = MyClass->new; # Reference to empty hash return bless $self, $type; } sub DESTROY { print "MySubClass::DESTROY called\n"; } sub MyMethod { my $self = shift; $self->SUPER::MyMethod(); print " MySubClass::MyMethod called!\n"; } # Here is the main program using above classes. package main; print "Invoke MyClass method\n"; $myObject = MyClass->new(); $myObject->MyMethod(); print "Invoke MySubClass method\n"; $myObject2 = MySubClass->new(); $myObject2->MyMethod(); print "Create a scoped object\n"; { my $myObject2 = MyClass->new(); } # Destructor is called automatically here print "Create and undef an object\n"; $myObject3 = MyClass->new(); undef $myObject3; print "Fall off the end of the script...\n"; # Remaining destructors are called automatically here
当我们执行上述程序时,它会产生以下结果:
Invoke MyClass method MyClass::new called MyClass::MyMethod called! Invoke MySubClass method MySubClass::new called MyClass::new called MyClass::MyMethod called! MySubClass::MyMethod called! Create a scoped object MyClass::new called MyClass::DESTROY called Create and undef an object MyClass::new called MyClass::DESTROY called Fall off the end of the script... MyClass::DESTROY called MySubClass::DESTROY called
Perl - 数据库访问
本章将教您如何在 Perl 脚本中访问数据库。从 Perl 5 开始,使用 DBI 模块编写数据库应用程序变得非常容易。DBI 代表 Perl 的 数据库独立接口,这意味着 DBI 在 Perl 代码和底层数据库之间提供了一个抽象层,允许您非常轻松地切换数据库实现。
DBI 是 Perl 编程语言的数据库访问模块。它提供了一组方法、变量和约定,这些方法、变量和约定提供了一致的数据库接口,独立于正在使用的实际数据库。
DBI 应用程序的架构
DBI 独立于后端可用的任何数据库。无论您使用的是 Oracle、MySQL 还是 Informix 等,都可以使用 DBI。从以下架构图可以清楚地看出这一点。
这里 DBI 负责通过 API(即应用程序编程接口)获取所有 SQL 命令,并将它们分派给相应的驱动程序以进行实际执行。最后,DBI 负责从驱动程序获取结果并将其返回给调用脚本。
符号和约定
在本章中,将使用以下符号,建议您也遵循相同的约定。
$dsn Database source name $dbh Database handle object $sth Statement handle object $h Any of the handle types above ($dbh, $sth, or $drh) $rc General Return Code (boolean: true=ok, false=error) $rv General Return Value (typically an integer) @ary List of values returned from the database. $rows Number of rows processed (if available, else -1) $fh A filehandle undef NULL values are represented by undefined values in Perl \%attr Reference to a hash of attribute values passed to methods
数据库连接
假设我们将使用 MySQL 数据库。连接到数据库之前,请确保以下事项。如果您不了解如何在 MySQL 数据库中创建数据库和表,可以参考我们的 MySQL 教程。
您已创建了一个名为 TESTDB 的数据库。
您已在 TESTDB 中创建了一个名为 TEST_TABLE 的表。
此表具有字段 FIRST_NAME、LAST_NAME、AGE、SEX 和 INCOME。
已设置用户 ID“testuser”和密码“test123”以访问 TESTDB。
您的机器上已正确安装 Perl 模块 DBI。
您已阅读 MySQL 教程以了解 MySQL 基础知识。
以下是连接 MySQL 数据库“TESTDB”的示例 -
#!/usr/bin/perl use DBI use strict; my $driver = "mysql"; my $database = "TESTDB"; my $dsn = "DBI:$driver:database=$database"; my $userid = "testuser"; my $password = "test123"; my $dbh = DBI->connect($dsn, $userid, $password ) or die $DBI::errstr;
如果与数据源建立连接,则返回数据库句柄并将其保存到 $dbh 以供进一步使用,否则 $dbh 设置为 undef 值,而 $DBI::errstr 返回错误字符串。
INSERT 操作
当您想要在表中创建一些记录时,需要进行 INSERT 操作。这里我们使用 TEST_TABLE 表来创建我们的记录。因此,一旦我们的数据库连接建立,我们就可以准备在 TEST_TABLE 中创建记录了。以下是创建单个记录到 TEST_TABLE 的过程。您可以使用相同的概念创建任意数量的记录。
记录创建包含以下步骤 -
使用 INSERT 语句准备 SQL 语句。这将使用 prepare() API 完成。
执行 SQL 查询以从数据库中选择所有结果。这将使用 execute() API 完成。
释放语句句柄。这将使用 finish() API 完成。
如果一切顺利,则 提交此操作,否则您可以 回滚整个事务。提交和回滚将在下一节中解释。
my $sth = $dbh->prepare("INSERT INTO TEST_TABLE (FIRST_NAME, LAST_NAME, SEX, AGE, INCOME ) values ('john', 'poul', 'M', 30, 13000)"); $sth->execute() or die $DBI::errstr; $sth->finish(); $dbh->commit or die $DBI::errstr;
使用绑定值
可能存在需要输入的值事先未给出的情况。因此,您可以使用绑定变量,这些变量将在运行时获取所需的值。Perl DBI 模块使用问号代替实际值,然后实际值在运行时通过 execute() API 传递。示例如下 -
my $first_name = "john"; my $last_name = "poul"; my $sex = "M"; my $income = 13000; my $age = 30; my $sth = $dbh->prepare("INSERT INTO TEST_TABLE (FIRST_NAME, LAST_NAME, SEX, AGE, INCOME ) values (?,?,?,?)"); $sth->execute($first_name,$last_name,$sex, $age, $income) or die $DBI::errstr; $sth->finish(); $dbh->commit or die $DBI::errstr;
READ 操作
任何数据库上的 READ 操作都意味着从数据库中提取一些有用的信息,即一个或多个表中的一条或多条记录。因此,一旦我们的数据库连接建立,我们就可以准备对这个数据库进行查询了。以下是查询所有年龄大于 20 的记录的过程。这将包含四个步骤 -
根据所需条件准备 SQL SELECT 查询。这将使用 prepare() API 完成。
执行 SQL 查询以从数据库中选择所有结果。这将使用 execute() API 完成。
逐一获取所有结果并打印这些结果。这将使用 fetchrow_array() API 完成。
释放语句句柄。这将使用 finish() API 完成。
my $sth = $dbh->prepare("SELECT FIRST_NAME, LAST_NAME FROM TEST_TABLE WHERE AGE > 20"); $sth->execute() or die $DBI::errstr; print "Number of rows found :" + $sth->rows; while (my @row = $sth->fetchrow_array()) { my ($first_name, $last_name ) = @row; print "First Name = $first_name, Last Name = $last_name\n"; } $sth->finish();
使用绑定值
可能存在条件事先未给出的情况。因此,您可以使用绑定变量,这些变量将在运行时获取所需的值。Perl DBI 模块使用问号代替实际值,然后实际值在运行时通过 execute() API 传递。示例如下 -
$age = 20; my $sth = $dbh->prepare("SELECT FIRST_NAME, LAST_NAME FROM TEST_TABLE WHERE AGE > ?"); $sth->execute( $age ) or die $DBI::errstr; print "Number of rows found :" + $sth->rows; while (my @row = $sth->fetchrow_array()) { my ($first_name, $last_name ) = @row; print "First Name = $first_name, Last Name = $last_name\n"; } $sth->finish();
UPDATE 操作
任何数据库上的 UPDATE 操作都意味着更新数据库表中已存在的记录。以下是更新所有 SEX 为“M”的记录的过程。这里我们将所有男性的 AGE 增加一年。这将包含三个步骤 -
根据所需条件准备 SQL 查询。这将使用 prepare() API 完成。
执行 SQL 查询以从数据库中选择所有结果。这将使用 execute() API 完成。
释放语句句柄。这将使用 finish() API 完成。
如果一切顺利,则 提交此操作,否则您可以 回滚整个事务。有关提交和回滚 API,请参阅下一节。
my $sth = $dbh->prepare("UPDATE TEST_TABLE SET AGE = AGE + 1 WHERE SEX = 'M'"); $sth->execute() or die $DBI::errstr; print "Number of rows updated :" + $sth->rows; $sth->finish(); $dbh->commit or die $DBI::errstr;
使用绑定值
可能存在条件事先未给出的情况。因此,您可以使用绑定变量,这些变量将在运行时获取所需的值。Perl DBI 模块使用问号代替实际值,然后实际值在运行时通过 execute() API 传递。示例如下 -
$sex = 'M'; my $sth = $dbh->prepare("UPDATE TEST_TABLE SET AGE = AGE + 1 WHERE SEX = ?"); $sth->execute('$sex') or die $DBI::errstr; print "Number of rows updated :" + $sth->rows; $sth->finish(); $dbh->commit or die $DBI::errstr;
在某些情况下,您可能希望设置一个事先未给出的值,因此您可以按如下方式使用绑定值。在此示例中,所有男性的收入将设置为 10000。
$sex = 'M'; $income = 10000; my $sth = $dbh->prepare("UPDATE TEST_TABLE SET INCOME = ? WHERE SEX = ?"); $sth->execute( $income, '$sex') or die $DBI::errstr; print "Number of rows updated :" + $sth->rows; $sth->finish();
DELETE 操作
当您想要从数据库中删除某些记录时,需要进行 DELETE 操作。以下是删除 TEST_TABLE 中 AGE 等于 30 的所有记录的过程。此操作将包含以下步骤。
根据所需条件准备 SQL 查询。这将使用 prepare() API 完成。
执行 SQL 查询以从数据库中删除所需记录。这将使用 execute() API 完成。
释放语句句柄。这将使用 finish() API 完成。
如果一切顺利,则 提交此操作,否则您可以 回滚整个事务。
$age = 30; my $sth = $dbh->prepare("DELETE FROM TEST_TABLE WHERE AGE = ?"); $sth->execute( $age ) or die $DBI::errstr; print "Number of rows deleted :" + $sth->rows; $sth->finish(); $dbh->commit or die $DBI::errstr;
使用 do 语句
如果您要执行 UPDATE、INSERT 或 DELETE 操作,则不会从数据库返回任何数据,因此可以使用快捷方式执行此操作。您可以使用 do 语句执行任何命令,如下所示。
$dbh->do('DELETE FROM TEST_TABLE WHERE age =30');
如果 do 成功,则返回真值;如果失败,则返回假值。实际上,如果成功,它将返回受影响的行数。在示例中,它将返回实际删除的行数。
COMMIT 操作
Commit 是一个向数据库发出信号以完成更改的操作,此操作后,任何更改都无法恢复到其原始位置。
这是一个调用 commit API 的简单示例。
$dbh->commit or die $dbh->errstr;
ROLLBACK 操作
如果您对所有更改不满意,或者在任何操作过程中遇到错误,您可以使用 rollback API 来恢复这些更改。
这是一个调用 rollback API 的简单示例。
$dbh->rollback or die $dbh->errstr;
开始事务
许多数据库支持事务。这意味着您可以进行许多修改数据库的查询,但没有任何更改实际生效。然后,最后,您发出特殊的 SQL 查询 COMMIT,所有更改将同时生效。或者,您可以发出查询 ROLLBACK,在这种情况下,所有更改都将被丢弃,数据库保持不变。
Perl DBI 模块提供了 begin_work API,它启用事务(通过关闭 AutoCommit),直到下次调用 commit 或 rollback。在下一次提交或回滚后,AutoCommit 将自动再次打开。
$rc = $dbh->begin_work or die $dbh->errstr;
AutoCommit 选项
如果您的事务很简单,您可以避免发出大量提交的麻烦。进行连接调用时,您可以指定一个 AutoCommit 选项,该选项将在每次成功查询后执行自动提交操作。如下所示 -
my $dbh = DBI->connect($dsn, $userid, $password, {AutoCommit => 1}) or die $DBI::errstr;
这里 AutoCommit 可以取值 1 或 0,其中 1 表示 AutoCommit 已打开,0 表示 AutoCommit 已关闭。
自动错误处理
进行连接调用时,您可以指定一个 RaiseErrors 选项,该选项将自动为您处理错误。发生错误时,DBI 将中止您的程序,而不是返回失败代码。如果您只想在发生错误时中止程序,这可能会很方便。如下所示 -
my $dbh = DBI->connect($dsn, $userid, $password, {RaiseError => 1}) or die $DBI::errstr;
这里 RaiseError 可以取值 1 或 0。
断开数据库连接
要断开数据库连接,请使用 disconnect API,如下所示 -
$rc = $dbh->disconnect or warn $dbh->errstr;
disconnect 方法的事务行为很遗憾是未定义的。某些数据库系统(如 Oracle 和 Ingres)将自动提交任何未完成的更改,但其他系统(如 Informix)将回滚任何未完成的更改。不使用 AutoCommit 的应用程序应在调用 disconnect 之前显式调用 commit 或 rollback。
使用 NULL 值
未定义值,或 undef,用于表示 NULL 值。您可以像插入和更新非 NULL 值一样插入和更新具有 NULL 值的列。以下示例使用 NULL 值插入和更新 age 列:
$sth = $dbh->prepare(qq { INSERT INTO TEST_TABLE (FIRST_NAME, AGE) VALUES (?, ?) }); $sth->execute("Joe", undef);
这里使用qq{}将带引号的字符串返回到prepare API。但是,在 WHERE 子句中使用 NULL 值时必须小心。请考虑:
SELECT FIRST_NAME FROM TEST_TABLE WHERE age = ?
将 undef (NULL) 绑定到占位符不会选择 age 为 NULL 的行!至少对于符合 SQL 标准的数据库引擎是这样。有关原因,请参阅您的数据库引擎的 SQL 手册或任何 SQL 书籍。要显式选择 NULL 值,您必须使用“WHERE age IS NULL”。
一个常见的问题是让代码片段处理运行时可能已定义或未定义(非 NULL 或 NULL)的值。一个简单的技术是根据需要准备相应的语句,并在非 NULL 的情况下替换占位符:
$sql_clause = defined $age? "age = ?" : "age IS NULL"; $sth = $dbh->prepare(qq { SELECT FIRST_NAME FROM TEST_TABLE WHERE $sql_clause }); $sth->execute(defined $age ? $age : ());
其他一些 DBI 函数
available_drivers
@ary = DBI->available_drivers; @ary = DBI->available_drivers($quiet);
通过搜索 @INC 中目录下的 DBD::* 模块来返回所有可用驱动程序的列表。默认情况下,如果某些驱动程序被同一名称的早期目录中的其他驱动程序隐藏,则会发出警告。将 $quiet 的值传递为 true 将抑制警告。
installed_drivers
%drivers = DBI->installed_drivers();
返回当前进程中所有“已安装”(已加载)驱动程序的驱动程序名称和驱动程序句柄对列表。驱动程序名称不包含“DBD::”前缀。
data_sources
@ary = DBI->data_sources($driver);
返回通过指定驱动程序可用的数据源(数据库)列表。如果 $driver 为空或未定义,则使用 DBI_DRIVER 环境变量的值。
quote
$sql = $dbh->quote($value); $sql = $dbh->quote($value, $data_type);
为在 SQL 语句中用作文字值而引用字符串文字,方法是转义字符串中包含的任何特殊字符(例如引号),并添加所需的外部引号类型。
$sql = sprintf "SELECT foo FROM bar WHERE baz = %s", $dbh->quote("Don't");
对于大多数数据库类型,quote 将返回 'Don''t'(包括外部引号)。quote() 方法返回计算结果为所需字符串的 SQL 表达式是有效的。例如:
$quoted = $dbh->quote("one\ntwo\0three") may produce results which will be equivalent to CONCAT('one', CHAR(12), 'two', CHAR(0), 'three')
所有句柄通用的方法
err
$rv = $h->err; or $rv = $DBI::err or $rv = $h->err
返回上次调用的驱动程序方法的本机数据库引擎错误代码。该代码通常是整数,但您不应假设它是整数。这等效于 $DBI::err 或 $h->err。
errstr
$str = $h->errstr; or $str = $DBI::errstr or $str = $h->errstr
返回上次调用的 DBI 方法的本机数据库引擎错误消息。这与上面描述的“err”方法具有相同的生命周期问题。这等效于 $DBI::errstr 或 $h->errstr。
rows
$rv = $h->rows; or $rv = $DBI::rows
这返回前一个 SQL 语句影响的行数,等效于 $DBI::rows。
trace
$h->trace($trace_settings);
DBI 具有一个非常有用的功能,可以生成其正在执行的操作的运行时跟踪信息,这在尝试跟踪 DBI 程序中奇怪问题的过程中可以节省大量时间。您可以使用不同的值来设置跟踪级别。这些值从 0 到 4 不等。值为 0 表示禁用跟踪,值为 4 表示生成完整的跟踪。
禁止使用内插语句
强烈建议不要使用如下所示的内插语句:
while ($first_name = <>) { my $sth = $dbh->prepare("SELECT * FROM TEST_TABLE WHERE FIRST_NAME = '$first_name'"); $sth->execute(); # and so on ... }
因此,不要使用内插语句,而是使用绑定值来准备动态 SQL 语句。
Perl - CGI 编程
什么是 CGI?
公共网关接口 (CGI) 是一组标准,定义了如何在 Web 服务器和自定义脚本之间交换信息。
CGI 规范目前由 NCSA 维护,NCSA 对 CGI 的定义如下:
公共网关接口 (CGI) 是外部网关程序与信息服务器(例如 HTTP 服务器)接口的标准。
当前版本为 CGI/1.1,CGI/1.2 正在开发中。
网页浏览
为了理解 CGI 的概念,让我们看看当我们点击网页上提供的超链接来浏览特定网页或 URL 时会发生什么。
您的浏览器使用 HTTP 协议联系 Web 服务器并请求 URL,即网页文件名。
Web 服务器将检查 URL 并查找请求的文件名。如果 Web 服务器找到该文件,则它会将文件发送回浏览器,无需进一步执行;否则,它会发送一条错误消息,指示您请求的文件错误。
Web 浏览器接收来自 Web 服务器的响应,并显示接收到的文件内容或在找不到文件的情况下显示错误消息。
但是,可以以这样一种方式设置 HTTP 服务器:每当请求某个目录中的文件时,该文件不会被发送回来;相反,它作为程序执行,该程序输出的结果将被发送回浏览器以显示。这可以通过使用 Web 服务器中提供的特殊功能来实现,该功能称为公共网关接口或 CGI,并且服务器执行以产生最终结果的此类程序称为 CGI 脚本。这些 CGI 程序可以是 PERL 脚本、Shell 脚本、C 或 C++ 程序等。
CGI 架构图
Web 服务器支持和配置
在继续进行 CGI 编程之前,请确保您的 Web 服务器支持 CGI 功能,并且已配置为处理 CGI 程序。所有要由 Web 服务器执行的 CGI 程序都保存在预配置的目录中。此目录称为 CGI 目录,按照惯例,其名称为 /cgi-bin。按照惯例,Perl CGI 文件的扩展名为.cgi。
第一个 CGI 程序
这是一个简单的链接,它链接到名为 hello.cgi 的 CGI 脚本。此文件已保存在/cgi-bin/目录中,其内容如下。在运行 CGI 程序之前,请确保已使用chmod 755 hello.cgi UNIX 命令更改文件的模式。
#!/usr/bin/perl print "Content-type:text/html\r\n\r\n"; print '<html>'; print '<head>'; print '<title>Hello Word - First CGI Program</title>'; print '</head>'; print '<body>'; print '<h2>Hello Word! This is my first CGI program</h2>'; print '</body>'; print '</html>'; 1;
现在,如果您单击hello.cgi链接,则请求将转到 Web 服务器,Web 服务器在 /cgi-bin 目录中搜索 hello.cgi,执行它,并将生成的任何结果发送回 Web 浏览器,如下所示:
Hello Word! This is my first CGI program
这个 hello.cgi 脚本是一个简单的 Perl 脚本,它将其输出写入 STDOUT 文件(即屏幕)。有一个重要的额外功能,即要打印的第一行Content-type:text/html\r\n\r\n。此行被发送回浏览器,并指定要在浏览器屏幕上显示的内容类型。现在您一定已经理解了 CGI 的基本概念,您可以使用 Perl 编写许多复杂的 CGI 程序。此脚本还可以与任何其他外部系统交互以交换信息,例如数据库、Web 服务或任何其他复杂的接口。
理解 HTTP 头
第一行Content-type:text/html\r\n\r\n是 HTTP 头的一部分,它被发送到浏览器,以便浏览器可以理解来自服务器端的传入内容。所有 HTTP 头都将采用以下形式:
HTTP Field Name: Field Content
例如:
Content-type:text/html\r\n\r\n
还有一些其他重要的 HTTP 头,您将在 CGI 编程中经常使用它们。
序号 | 头 & 描述 |
---|---|
1 | Content-type: 字符串 定义返回内容格式的 MIME 字符串。例如 Content-type:text/html |
2 | Expires: 日期字符串 信息变得无效的日期。浏览器应该使用它来决定何时需要刷新页面。有效的日期字符串应采用 01 Jan 1998 12:00:00 GMT 格式。 |
3 | Location: URL 字符串 应该返回的 URL,而不是请求的 URL。您可以使用此字段将请求重定向到任何其他位置。 |
4 | Last-modified: 字符串 文件的上次修改日期。 |
5 | Content-length: 字符串 返回数据的长度(以字节为单位)。浏览器使用此值来报告文件的估计下载时间。 |
6 | Set-Cookie: 字符串 设置通过字符串传递的 cookie |
CGI 环境变量
所有 CGI 程序都可以访问以下环境变量。这些变量在编写任何 CGI 程序时都起着重要作用。
序号 | 变量名称 & 描述 |
---|---|
1 | CONTENT_TYPE 内容的数据类型。当客户端向服务器发送附加内容时使用。例如文件上传等。 |
2 | CONTENT_LENGTH 查询信息的长度。仅对 POST 请求可用 |
3 | HTTP_COOKIE 以键值对的形式返回设置的 cookie。 |
4 | HTTP_USER_AGENT User-Agent 请求头字段包含有关发起请求的用户代理的信息。这是 Web 浏览器的名称。 |
5 | PATH_INFO CGI 脚本的路径。 |
6 | QUERY_STRING 使用 GET 方法请求发送的 URL 编码信息。 |
7 | REMOTE_ADDR 发出请求的远程主机的 IP 地址。这对于记录或身份验证目的很有用。 |
8 | REMOTE_HOST 发出请求的主机的完全限定名称。如果此信息不可用,则可以使用 REMOTE_ADDR 获取 IR 地址。 |
9 | REQUEST_METHOD 用于发出请求的方法。最常用的方法是 GET 和 POST。 |
10 | SCRIPT_FILENAME CGI 脚本的完整路径。 |
11 | SCRIPT_NAME CGI 脚本的名称。 |
12 | SERVER_NAME 服务器的主机名或 IP 地址。 |
13 | SERVER_SOFTWARE 服务器运行的软件的名称和版本。 |
这是一个小的 CGI 程序,用于列出 Web 服务器支持的所有 CGI 变量。单击此链接查看结果 获取环境变量
#!/usr/bin/perl print "Content-type: text/html\n\n"; print "<font size=+1>Environment</font>\n"; foreach (sort keys %ENV) { print "<b>$_</b>: $ENV{$_}<br>\n"; } 1;
如何弹出“文件下载”对话框?
有时您希望提供一个选项,用户单击链接后会弹出一个“文件下载”对话框,而不是显示实际内容。这很容易实现,并且可以通过 HTTP 头实现。
此 HTTP 头将与上一节中提到的头不同。例如,如果您想使FileName文件可从给定链接下载,则其语法如下:
#!/usr/bin/perl # HTTP Header print "Content-Type:application/octet-stream; name = \"FileName\"\r\n"; print "Content-Disposition: attachment; filename = \"FileName\"\r\n\n"; # Actual File Content will go hear. open( FILE, "<FileName" ); while(read(FILE, $buffer, 100) ) { print("$buffer"); }
GET 和 POST 方法
您肯定遇到过许多需要将信息从浏览器传递到 Web 服务器,最终传递到处理您请求的 CGI 程序的情况。浏览器最常用两种方法将信息传递到 Web 服务器。这两种方法是 **GET** 方法和 **POST** 方法。让我们逐一检查它们。
使用 GET 方法传递信息
GET 方法将编码后的用户信息附加到页面 URL 本身。页面和编码后的信息以 ? 字符分隔,如下所示:
http://www.test.com/cgi-bin/hello.cgi?key1=value1&key2=value2
GET 方法是将信息从浏览器传递到 Web 服务器的默认方法,它会生成一个长字符串,该字符串显示在浏览器的“位置”框中。如果您需要将密码或其他敏感信息传递到服务器,则绝不应使用 GET 方法。GET 方法有大小限制:请求字符串中只能传递 1024 个字符。
此信息使用 **QUERY_STRING** 标头传递,并且可以通过 QUERY_STRING 环境变量在您的 CGI 程序中访问,您可以解析此变量并在 CGI 程序中使用它。
您可以通过简单地连接键值对以及任何 URL 来传递信息,也可以使用 HTML <FORM> 标签使用 GET 方法传递信息。
简单的 URL 示例:GET 方法
这是一个简单的 URL,它将使用 GET 方法将两个值传递给 hello_get.cgi 程序。
https://tutorialspoint.com/cgi-bin/hello_get.cgi?first_name=ZARA&last_name=ALI以下是处理 Web 浏览器给出的输入的 **hello_get.cgi** 脚本。
#!/usr/bin/perl local ($buffer, @pairs, $pair, $name, $value, %FORM); # Read in text $ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/; if ($ENV{'REQUEST_METHOD'} eq "GET") { $buffer = $ENV{'QUERY_STRING'}; } # Split information into name/value pairs @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%(..)/pack("C", hex($1))/eg; $FORM{$name} = $value; } $first_name = $FORM{first_name}; $last_name = $FORM{last_name}; print "Content-type:text/html\r\n\r\n"; print "<html>"; print "<head>"; print "<title>Hello - Second CGI Program</title>"; print "</head>"; print "<body>"; print "<h2>Hello $first_name $last_name - Second CGI Program</h2>"; print "</body>"; print "</html>"; 1;
简单的表单示例:GET 方法
这是一个简单的示例,它使用 HTML 表单和提交按钮传递两个值。我们将使用相同的 CGI 脚本 hello_get.cgi 来处理此输入。
<FORM action = "/cgi-bin/hello_get.cgi" method = "GET"> First Name: <input type = "text" name = "first_name"> <br> Last Name: <input type = "text" name = "last_name"> <input type = "submit" value = "Submit"> </FORM>
这是上述表单代码的实际输出。现在您可以输入名字和姓氏,然后单击提交按钮查看结果。
使用 POST 方法传递信息
将信息传递到 CGI 程序的一种更可靠的方法是 **POST** 方法。此方法以与 GET 方法完全相同的方式打包信息,但它不是在 URL 中的 **?** 后将其作为文本字符串发送,而是将其作为 HTTP 标头的一部分作为单独的消息发送。Web 服务器以标准输入的形式将此消息提供给 CGI 脚本。
以下是修改后的 **hello_post.cgi** 脚本,用于处理 Web 浏览器给出的输入。此脚本将处理 GET 和 POST 方法。
#!/usr/bin/perl local ($buffer, @pairs, $pair, $name, $value, %FORM); # Read in text $ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/; if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $buffer = $ENV{'QUERY_STRING'}; } # Split information into name/value pairs @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%(..)/pack("C", hex($1))/eg; $FORM{$name} = $value; } $first_name = $FORM{first_name}; $last_name = $FORM{last_name}; print "Content-type:text/html\r\n\r\n"; print "<html>"; print "<head>"; print "<title>Hello - Second CGI Program</title>"; print "</head>"; print "<body>"; print "<h2>Hello $first_name $last_name - Second CGI Program</h2>"; print "</body>"; print "</html>"; 1;
让我们再次采用与上述相同的示例,该示例使用 HTML 表单和提交按钮传递两个值。我们将使用 CGI 脚本 hello_post.cgi 来处理此输入。
<FORM action = "/cgi-bin/hello_post.cgi" method = "POST"> First Name: <input type = "text" name = "first_name"> <br> Last Name: <input type = "text" name = "last_name"> <input type = "submit" value = "Submit"> </FORM>
这是上述表单代码的实际输出,您可以输入名字和姓氏,然后单击提交按钮查看结果。
将复选框数据传递到 CGI 程序
当需要选择多个选项时,使用复选框。这是一个带有两个复选框的表单的 HTML 代码示例。
<form action = "/cgi-bin/checkbox.cgi" method = "POST" target = "_blank"> <input type = "checkbox" name = "maths" value = "on"> Maths <input type = "checkbox" name = "physics" value = "on"> Physics <input type = "submit" value = "Select Subject"> </form>
此代码的结果是以下表单:
以下是处理 Web 浏览器为单选按钮提供的输入的 **checkbox.cgi** 脚本。
#!/usr/bin/perl local ($buffer, @pairs, $pair, $name, $value, %FORM); # Read in text $ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/; if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $buffer = $ENV{'QUERY_STRING'}; } # Split information into name/value pairs @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%(..)/pack("C", hex($1))/eg; $FORM{$name} = $value; } if( $FORM{maths} ) { $maths_flag ="ON"; } else { $maths_flag ="OFF"; } if( $FORM{physics} ) { $physics_flag ="ON"; } else { $physics_flag ="OFF"; } print "Content-type:text/html\r\n\r\n"; print "<html>"; print "<head>"; print "<title>Checkbox - Third CGI Program</title>"; print "</head>"; print "<body>"; print "<h2> CheckBox Maths is : $maths_flag</h2>"; print "<h2> CheckBox Physics is : $physics_flag</h2>"; print "</body>"; print "</html>"; 1;
将单选按钮数据传递到 CGI 程序
当只需要选择一个选项时,使用单选按钮。这是一个带有两个单选按钮的表单的 HTML 代码示例:
<form action = "/cgi-bin/radiobutton.cgi" method = "POST" target = "_blank"> <input type = "radio" name = "subject" value = "maths"> Maths <input type = "radio" name = "subject" value = "physics"> Physics <input type = "submit" value = "Select Subject"> </form>
此代码的结果是以下表单:
以下是处理 Web 浏览器为单选按钮提供的输入的 **radiobutton.cgi** 脚本。
#!/usr/bin/perl local ($buffer, @pairs, $pair, $name, $value, %FORM); # Read in text $ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/; if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $buffer = $ENV{'QUERY_STRING'}; } # Split information into name/value pairs @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%(..)/pack("C", hex($1))/eg; $FORM{$name} = $value; } $subject = $FORM{subject}; print "Content-type:text/html\r\n\r\n"; print "<html>"; print "<head>"; print "<title>Radio - Fourth CGI Program</title>"; print "</head>"; print "<body>"; print "<h2> Selected Subject is $subject</h2>"; print "</body>"; print "</html>"; 1;
将文本区域数据传递到 CGI 程序
当必须将多行文本传递到 CGI 程序时,使用文本区域元素。这是一个带有文本区域框的表单的 HTML 代码示例:
<form action = "/cgi-bin/textarea.cgi" method = "POST" target = "_blank"> <textarea name = "textcontent" cols = 40 rows = 4> Type your text here... </textarea> <input type = "submit" value = "Submit"> </form>
此代码的结果是以下表单:
以下是处理 Web 浏览器提供的输入的 **textarea.cgi** 脚本。
#!/usr/bin/perl local ($buffer, @pairs, $pair, $name, $value, %FORM); # Read in text $ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/; if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $buffer = $ENV{'QUERY_STRING'}; } # Split information into name/value pairs @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%(..)/pack("C", hex($1))/eg; $FORM{$name} = $value; } $text_content = $FORM{textcontent}; print "Content-type:text/html\r\n\r\n"; print "<html>"; print "<head>"; print "<title>Text Area - Fifth CGI Program</title>"; print "</head>"; print "<body>"; print "<h2> Entered Text Content is $text_content</h2>"; print "</body>"; print "</html>"; 1;
将下拉框数据传递到 CGI 程序
当有很多选项可用但只选择一两个时,使用下拉框。这是一个带有下拉框的表单的 HTML 代码示例
<form action = "/cgi-bin/dropdown.cgi" method = "POST" target = "_blank"> <select name = "dropdown"> <option value = "Maths" selected>Maths</option> <option value = "Physics">Physics</option> </select> <input type = "submit" value = "Submit"> </form>
此代码的结果是以下表单:
以下是处理 Web 浏览器提供的输入的 **dropdown.cgi** 脚本。
#!/usr/bin/perl local ($buffer, @pairs, $pair, $name, $value, %FORM); # Read in text $ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/; if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $buffer = $ENV{'QUERY_STRING'}; } # Split information into name/value pairs @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%(..)/pack("C", hex($1))/eg; $FORM{$name} = $value; } $subject = $FORM{dropdown}; print "Content-type:text/html\r\n\r\n"; print "<html>"; print "<head>"; print "<title>Dropdown Box - Sixth CGI Program</title>"; print "</head>"; print "<body>"; print "<h2> Selected Subject is $subject</h2>"; print "</body>"; print "</html>"; 1;
在 CGI 中使用 Cookie
HTTP 协议是一种无状态协议。但是对于商业网站来说,需要在不同的页面之间维护会话信息。例如,一个用户注册在跨越多个页面的事务之后结束。但是如何跨所有网页维护用户的会话信息呢?
在许多情况下,使用 Cookie 是记住和跟踪偏好、购买、佣金以及其他需要改善访客体验或网站统计信息的信息的最有效方法。
工作原理
您的服务器以 Cookie 的形式向访问者的浏览器发送一些数据。浏览器可能会接受 Cookie。如果接受,它将作为纯文本记录存储在访问者的硬盘驱动器上。现在,当访问者到达您网站上的另一个页面时,Cookie 可供检索。检索后,您的服务器就知道/记住存储的内容。
Cookie 是 5 个变长字段的纯文本数据记录:
**Expires** - Cookie 将过期的日期。如果为空,则 Cookie 将在访问者退出浏览器时过期。
**Domain** - 您网站的域名。
**Path** - 设置 Cookie 的目录或网页的路径。如果您想从任何目录或页面检索 Cookie,则可以留空。
**Secure** - 如果此字段包含单词“secure”,则只能使用安全服务器检索 Cookie。如果此字段为空,则不存在此类限制。
**Name = Value** - Cookie 以键值对的形式设置和检索。
设置 Cookie
向浏览器发送 Cookie 非常容易。这些 Cookie 将与 HTTP 标头一起发送。假设您想将 UserID 和 Password 设置为 Cookie。因此,操作步骤如下:
#!/usr/bin/perl print "Set-Cookie:UserID = XYZ;\n"; print "Set-Cookie:Password = XYZ123;\n"; print "Set-Cookie:Expires = Tuesday, 31-Dec-2007 23:12:40 GMT";\n"; print "Set-Cookie:Domain = www.tutorialspoint.com;\n"; print "Set-Cookie:Path = /perl;\n"; print "Content-type:text/html\r\n\r\n"; ...........Rest of the HTML Content goes here....
我们在这里使用 **Set-Cookie** HTTP 标头来设置 Cookie。可选设置 Cookie 属性,如 Expires、Domain 和 Path。重要的是要注意,Cookie 是在发送魔法行 **"Content-type:text/html\r\n\r\n"** 之前设置的。
检索 Cookie
检索所有已设置的 Cookie 非常容易。Cookie 存储在 CGI 环境变量 HTTP_COOKIE 中,它们将具有以下形式。
key1 = value1;key2 = value2;key3 = value3....
这是一个关于如何检索 Cookie 的示例。
#!/usr/bin/perl $rcvd_cookies = $ENV{'HTTP_COOKIE'}; @cookies = split /;/, $rcvd_cookies; foreach $cookie ( @cookies ) { ($key, $val) = split(/=/, $cookie); # splits on the first =. $key =~ s/^\s+//; $val =~ s/^\s+//; $key =~ s/\s+$//; $val =~ s/\s+$//; if( $key eq "UserID" ) { $user_id = $val; } elsif($key eq "Password") { $password = $val; } } print "User ID = $user_id\n"; print "Password = $password\n";
如果在调用检索 Cookie 脚本之前已设置上述 Cookie,则将产生以下结果。
User ID = XYZ Password = XYZ123
CGI 模块和库
您会在互联网上找到许多内置模块,这些模块为您提供可在 CGI 程序中直接使用的函数。以下是重要的模块。
Perl - 包和模块
什么是包?
**package** 语句将当前命名上下文切换到指定的命名空间(符号表)。因此:
包是存在于其自身命名空间中的一组代码。
命名空间是唯一变量名称的命名集合(也称为符号表)。
命名空间可防止包之间发生变量名称冲突。
包支持构建模块,使用这些模块时不会破坏模块自身命名空间之外的变量和函数。
包一直有效,直到调用另一个 package 语句,或者直到当前块或文件的结尾。
您可以使用 **::** 包限定符显式引用包中的变量。
以下示例在一个文件中包含 main 和 Foo 包。这里使用了特殊变量 __PACKAGE__ 来打印包名。
#!/usr/bin/perl # This is main package $i = 1; print "Package name : " , __PACKAGE__ , " $i\n"; package Foo; # This is Foo package $i = 10; print "Package name : " , __PACKAGE__ , " $i\n"; package main; # This is again main package $i = 100; print "Package name : " , __PACKAGE__ , " $i\n"; print "Package name : " , __PACKAGE__ , " $Foo::i\n"; 1;
执行上述代码时,会产生以下结果:
Package name : main 1 Package name : Foo 10 Package name : main 100 Package name : main 10
BEGIN 和 END 块
您可以定义任意数量名为 BEGIN 和 END 的代码块,它们分别充当构造函数和析构函数。
BEGIN { ... } END { ... } BEGIN { ... } END { ... }
每个 **BEGIN** 块在 Perl 脚本加载和编译后但在执行任何其他语句之前执行。
每个 END 块在 Perl 解释器退出之前执行。
BEGIN 和 END 块在创建 Perl 模块时特别有用。
以下示例显示了它的用法:
#!/usr/bin/perl package Foo; print "Begin and Block Demo\n"; BEGIN { print "This is BEGIN Block\n" } END { print "This is END Block\n" } 1;
执行上述代码时,会产生以下结果:
This is BEGIN Block Begin and Block Demo This is END Block
什么是 Perl 模块?
Perl 模块是在库文件中定义的可重用包,其名称与包的名称相同,扩展名为 .pm。
名为 **Foo.pm** 的 Perl 模块文件可能包含如下语句。
#!/usr/bin/perl package Foo; sub bar { print "Hello $_[0]\n" } sub blat { print "World $_[0]\n" } 1;
关于 Perl 模块的一些重要点
函数 **require** 和 **use** 将加载模块。
两者都使用 **@INC** 中的搜索路径列表来查找模块。
这两个函数 **require** 和 **use** 都调用 **eval** 函数来处理代码。
底部的 **1;** 使 eval 的计算结果为 TRUE(因此不会失败)。
Require 函数
可以通过调用 **require** 函数来加载模块,如下所示:
#!/usr/bin/perl require Foo; Foo::bar( "a" ); Foo::blat( "b" );
您可能已经注意到,必须完全限定子程序名称才能调用它们。最好启用子程序 **bar** 和 **blat** 导入到我们自己的命名空间中,这样我们就不必使用 Foo:: 限定符。
Use 函数
可以通过调用 **use** 函数来加载模块。
#!/usr/bin/perl use Foo; bar( "a" ); blat( "b" );
请注意,我们不必完全限定包的函数名。**use** 函数将在模块中添加一些语句后,从模块导出符号列表。
require Exporter; @ISA = qw(Exporter);
然后,通过填充名为 **@EXPORT** 的列表变量来提供符号列表(标量、列表、哈希、子程序等):例如:
package Module; require Exporter; @ISA = qw(Exporter); @EXPORT = qw(bar blat); sub bar { print "Hello $_[0]\n" } sub blat { print "World $_[0]\n" } sub splat { print "Not $_[0]\n" } # Not exported! 1;
创建 Perl 模块树
当您准备好发布 Perl 模块时,有一种创建 Perl 模块树的标准方法。这是使用 **h2xs** 实用程序完成的。此实用程序随 Perl 一起提供。以下是使用 h2xs 的语法:
$h2xs -AX -n ModuleName
例如,如果您的模块位于 **Person.pm** 文件中,则只需发出以下命令:
$h2xs -AX -n Person
这将产生以下结果:
Writing Person/lib/Person.pm Writing Person/Makefile.PL Writing Person/README Writing Person/t/Person.t Writing Person/Changes Writing Person/MANIFEST
以下是这些选项的描述:
**-A** 省略自动加载器代码(最适合定义大量不常用子程序的模块)。
-X 忽略 XS 元素(eXternal Subroutine,其中 eXternal 指 Perl 之外的外部程序,即 C 程序)。
-n 指定模块名称。
因此,上述命令会在 Person 目录内创建如下结构。实际结果如上所示。
- 变更
- Makefile.PL
- MANIFEST(包含包中所有文件的列表)
- README
- t/(测试文件)
- lib/(实际源代码位于此处)
最后,您需要将此目录结构打包成 Person.tar.gz 文件,然后即可发布。您需要更新 README 文件,添加正确的安装说明。您也可以在 t 目录中提供一些测试示例文件。
安装 Perl 模块
下载 tar.gz 格式的 Perl 模块。使用以下步骤安装任何已下载为 Person.tar.gz 文件的 Perl 模块 Person.pm。
tar xvfz Person.tar.gz cd Person perl Makefile.PL make make install
Perl 解释器有一个目录列表,用于搜索模块(全局数组 @INC)。
Perl - 进程管理
您可以使用 Perl 以各种方式根据您的需求创建新进程。本教程将列出创建和管理 Perl 进程的一些重要且最常用的方法。
您可以使用特殊变量 $$ 或 $PROCESS_ID 获取当前进程 ID。
使用任何上述方法创建的每个进程,都维护其自身的虚拟环境,位于 %ENV 变量中。
exit() 函数始终只退出执行此函数的子进程,除非所有正在运行的子进程都已退出,否则主进程不会整体退出。
所有打开的句柄在子进程中都会被 dup(),因此在一个进程中关闭任何句柄都不会影响其他进程。
反引号操作符
执行任何 Unix 命令最简单的方法是使用反引号操作符。您只需将命令放在反引号操作符内,这将导致命令执行并返回其结果,该结果可以按如下方式存储:
#!/usr/bin/perl @files = `ls -l`; foreach $file (@files) { print $file; } 1;
执行上述代码时,它将列出当前目录中所有可用文件和目录。
drwxr-xr-x 3 root root 4096 Sep 14 06:46 9-14 drwxr-xr-x 4 root root 4096 Sep 13 07:54 android -rw-r--r-- 1 root root 574 Sep 17 15:16 index.htm drwxr-xr-x 3 544 401 4096 Jul 6 16:49 MIME-Lite-3.01 -rw-r--r-- 1 root root 71 Sep 17 15:16 test.pl drwx------ 2 root root 4096 Sep 17 15:11 vAtrJdy
system() 函数
您也可以使用 system() 函数执行任何 Unix 命令,其输出将转到 perl 脚本的输出。默认情况下,它是屏幕,即 STDOUT,但您可以使用重定向操作符 > 将其重定向到任何文件:
#!/usr/bin/perl system( "ls -l") 1;
执行上述代码时,它将列出当前目录中所有可用文件和目录。
drwxr-xr-x 3 root root 4096 Sep 14 06:46 9-14 drwxr-xr-x 4 root root 4096 Sep 13 07:54 android -rw-r--r-- 1 root root 574 Sep 17 15:16 index.htm drwxr-xr-x 3 544 401 4096 Jul 6 16:49 MIME-Lite-3.01 -rw-r--r-- 1 root root 71 Sep 17 15:16 test.pl drwx------ 2 root root 4096 Sep 17 15:11 vAtrJdy
如果您的命令包含 shell 环境变量(如 $PATH 或 $HOME),请务必小心。尝试以下三种情况:
#!/usr/bin/perl $PATH = "I am Perl Variable"; system('echo $PATH'); # Treats $PATH as shell variable system("echo $PATH"); # Treats $PATH as Perl variable system("echo \$PATH"); # Escaping $ works. 1;
执行上述代码时,产生的结果取决于 shell 变量 $PATH 中设置的内容。
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin I am Perl Variable /usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin
fork() 函数
Perl 提供一个 fork() 函数,它对应于同名的 Unix 系统调用。在大多数支持 fork() 系统调用的类 Unix 平台上,Perl 的 fork() 只是简单地调用它。在某些平台(如 Windows)上,fork() 系统调用不可用,Perl 可以构建为在解释器级别模拟 fork()。
fork() 函数用于克隆当前进程。此调用创建一个新的进程,运行相同的程序,并在同一位置运行。它将子进程 pid 返回给父进程,将 0 返回给子进程,如果 fork 不成功则返回 undef。
您可以在进程中使用 exec() 函数启动请求的可执行文件,该文件将在单独的进程区域中执行,exec() 将等待其完成,然后使用与该进程相同的退出状态退出。
#!/usr/bin/perl if(!defined($pid = fork())) { # fork returned undef, so unsuccessful die "Cannot fork a child: $!"; } elsif ($pid == 0) { print "Printed by child process\n"; exec("date") || die "can't exec date: $!"; } else { # fork returned 0 nor undef # so this branch is parent print "Printed by parent process\n"; $ret = waitpid($pid, 0); print "Completed process id: $ret\n"; } 1;
执行上述代码时,会产生以下结果:
Printed by parent process Printed by child process Tue Sep 17 15:41:08 CDT 2013 Completed process id: 17777
wait() 和 waitpid() 可以作为 fork() 返回的伪进程 ID 传递。这些调用将正确等待伪进程的终止并返回其状态。如果您使用 fork() 但从未使用 waitpid() 函数等待子进程,则会累积僵尸进程。在 Unix 系统上,您可以通过将 $SIG{CHLD} 设置为 "IGNORE" 来避免这种情况,如下所示:
#!/usr/bin/perl local $SIG{CHLD} = "IGNORE"; if(!defined($pid = fork())) { # fork returned undef, so unsuccessful die "Cannot fork a child: $!"; } elsif ($pid == 0) { print "Printed by child process\n"; exec("date") || die "can't exec date: $!"; } else { # fork returned 0 nor undef # so this branch is parent print "Printed by parent process\n"; $ret = waitpid($pid, 0); print "Completed process id: $ret\n"; } 1;
执行上述代码时,会产生以下结果:
Printed by parent process Printed by child process Tue Sep 17 15:44:07 CDT 2013 Completed process id: -1
kill() 函数
Perl kill('KILL', (Process List)) 函数可用于通过向其传递 fork() 返回的 ID 来终止伪进程。
请注意,在伪进程上使用 kill('KILL', (Process List)) 通常会导致内存泄漏,因为实现伪进程的线程没有机会清理其资源。
您可以使用 kill() 函数向目标进程发送任何其他信号,例如,以下操作将向进程 ID 104 和 102 发送 SIGINT:
#!/usr/bin/perl kill('INT', 104, 102); 1;
Perl - 内嵌文档
您可以在 Perl 模块和脚本中嵌入 Pod(Plain Old Documentation)文档。以下是使用 Perl 代码中嵌入式文档的规则:
以空行开头,以 =head1 命令开头,以 =cut 命令结尾。
Perl 将忽略您在代码中输入的 Pod 文本。以下是使用 Perl 代码中嵌入式文档的简单示例:
#!/usr/bin/perl print "Hello, World\n"; =head1 Hello, World Example This example demonstrate very basic syntax of Perl. =cut print "Hello, Universe\n";
执行上述代码时,会产生以下结果:
Hello, World Hello, Universe
如果您要将 Pod 放在文件的末尾,并且正在使用 __END__ 或 __DATA__ 分隔符,请确保在第一个 Pod 命令之前添加一个空行,否则如果没有在 =head1 之前添加空行,许多转换器将无法识别 =head1 作为 Pod 块的起始标记。
#!/usr/bin/perl print "Hello, World\n"; while(<DATA>) { print $_; } __END__ =head1 Hello, World Example This example demonstrate very basic syntax of Perl. print "Hello, Universe\n";
执行上述代码时,会产生以下结果:
Hello, World =head1 Hello, World Example This example demonstrate very basic syntax of Perl. print "Hello, Universe\n";
让我们再举一个不读取 DATA 部分的相同代码的例子:
#!/usr/bin/perl print "Hello, World\n"; __END__ =head1 Hello, World Example This example demonstrate very basic syntax of Perl. print "Hello, Universe\n";
执行上述代码时,会产生以下结果:
Hello, World
什么是 POD?
Pod 是一种易于使用的标记语言,用于编写 Perl、Perl 程序和 Perl 模块的文档。有各种转换器可用于将 Pod 转换为各种格式,如纯文本、HTML、手册页等等。Pod 标记由三种基本类型的段落组成:
普通段落 - 您可以在普通段落中使用格式代码,用于粗体、斜体、代码样式、超链接等等。
逐字段落 - 逐字段落通常用于呈现代码块或不需要任何特殊解析或格式化的其他文本,并且不应换行。
命令段落 - 命令段落用于对整块文本进行特殊处理,通常作为标题或列表的一部分。所有命令段落都以 = 开头,后跟一个标识符,然后是命令可以随意使用的任意文本。当前识别的命令是:
=pod =head1 Heading Text =head2 Heading Text =head3 Heading Text =head4 Heading Text =over indentlevel =item stuff =back =begin format =end format =for format text... =encoding type =cut
POD 示例
考虑以下 POD:
=head1 SYNOPSIS Copyright 2005 [TUTORIALSOPOINT]. =cut
您可以使用 Linux 上可用的 pod2html 实用程序将上述 POD 转换为 HTML,因此它将产生以下结果:
版权所有 2005 [TUTORIALSOPOINT]。
接下来,考虑以下示例:
=head2 An Example List =over 4 =item * This is a bulleted list. =item * Here's another item. =back =begin html <p> Here's some embedded HTML. In this block I can include images, apply <span style="color: green"> styles</span>, or do anything else I can do with HTML. pod parsers that aren't outputting HTML will completely ignore it. </p> =end html
当您使用 pod2html 将上述 POD 转换为 HTML 时,它将产生以下结果:
An Example List This is a bulleted list. Here's another item. Here's some embedded HTML. In this block I can include images, apply styles, or do anything else I can do with HTML. pod parsers that aren't outputting HTML will completely ignore it.
Perl - 函数引用
以下是标准 Perl 支持的所有重要函数的列表。
abs - 绝对值函数
accept - 接受传入的套接字连接
alarm - 安排 SIGALRM
atan2 - Y/X 的反正切值,范围为 -PI 到 PI
bind - 将地址绑定到套接字
binmode - 为 I/O 准备二进制文件
bless - 创建对象
caller - 获取当前子例程调用的上下文
chdir - 更改当前工作目录
chmod - 更改文件列表的权限
chomp - 从字符串中删除尾随记录分隔符
chop - 从字符串中删除最后一个字符
chown - 更改文件列表的所有权
chr - 获取此数字表示的字符
chroot - 使目录成为路径查找的新根目录
close - 关闭文件(或管道或套接字)句柄
closedir - 关闭目录句柄
connect - 连接到远程套接字
continue - while 或 foreach 中可选的尾随块
cos - 余弦函数
crypt - 单向密码式加密
dbmclose - 断开与绑定到 dbm 文件的连接
dbmopen - 创建与绑定到 dbm 文件的连接
defined - 测试值、变量或函数是否已定义
delete - 从哈希中删除值
die - 抛出异常或退出
do - 将 BLOCK 转换为 TERM
dump - 创建立即核心转储
each - 从哈希中检索下一个键/值对
endgrent - 完成使用组文件
endhostent - 完成使用主机文件
endnetent - 完成使用网络文件
endprotoent - 完成使用协议文件
endpwent - 完成使用 passwd 文件
endservent - 完成使用服务文件
eof - 测试文件句柄是否已结束
eval - 捕获异常或编译和运行代码
exec - 放弃此程序以运行另一个程序
exists - 测试哈希键是否存在
exit - 终止此程序
exp - 将 I
提升到幂 fcntl - 文件控制系统调用
fileno - 从文件句柄返回文件描述符
flock - 使用建议锁锁定整个文件
fork - 创建一个新的进程,与当前进程相同
format - 声明一个图片格式,供 write() 函数使用
formline - 用于格式的内部函数
getc - 从文件句柄获取下一个字符
getgrent - 获取下一个组记录
getgrgid - 给定组用户 ID 获取组记录
getgrnam - 给定组名称获取组记录
gethostbyaddr - 给定地址获取主机记录
gethostbyname - 给定名称获取主机记录
gethostent - 获取下一个主机记录
getlogin - 返回在此 tty 上登录的用户
getnetbyaddr - 给定地址获取网络记录
getnetbyname - 给定名称获取网络记录
getnetent - 获取下一个网络记录
getpeername - 查找套接字连接的另一端
getpgrp - 获取进程组
getppid - 获取父进程 ID
getpriority - 获取当前的 nice 值
getprotobyname - 给定名称获取协议记录
getprotobynumber - 给定数字协议获取协议记录
getprotoent - 获取下一个协议记录
getpwent - 获取下一个 passwd 记录
getpwnam - 给定用户名获取 passwd 记录
getpwuid - 给定用户 ID 获取 passwd 记录
getservbyname - 给定名称获取服务记录
getservbyport - 给定数字端口获取服务记录
getservent - 获取下一个服务记录
getsockname - 检索给定套接字的 sockaddr
getsockopt - 获取给定套接字的套接字选项
glob - 使用通配符展开文件名
gmtime - 使用格林威治时间格式将 UNIX 时间转换为记录或字符串。
goto - 创建意大利面条式代码
grep - 在列表中查找满足给定条件的元素
hex - 将字符串转换为十六进制数
import - 将模块的命名空间导入到自己的命名空间中
index - 在字符串中查找子字符串
int - 获取数字的整数部分
ioctl - 系统相关的设备控制系统调用
join - 使用分隔符将列表连接成字符串
keys - 从哈希中检索索引列表
kill - 向进程或进程组发送信号
last - 提早退出代码块
lc - 返回字符串的小写版本
lcfirst - 返回仅将第一个字母转换为小写的字符串
length - 返回字符串的字节数
link - 在文件系统中创建一个硬链接
listen - 将套接字注册为服务器
local - 为全局变量创建临时值(动态作用域)
localtime - 使用本地时间将UNIX时间转换为记录或字符串
lock - 获取变量、子程序或方法的线程锁
log - 获取数字的自然对数
lstat - 获取符号链接的状态信息
m - 使用正则表达式模式匹配字符串
map - 将更改应用于列表,以获取包含更改的新列表
mkdir - 创建目录
msgctl - SysV IPC 消息控制操作
msgget - 获取 SysV IPC 消息队列
msgrcv - 从消息队列接收 SysV IPC 消息
msgsnd - 向消息队列发送 SysV IPC 消息
my - 声明和赋值局部变量(词法作用域)
next - 提早迭代代码块
no - 在编译时取消导入某些模块符号或语义
oct - 将字符串转换为八进制数
open - 打开文件、管道或描述符
opendir - 打开目录
ord - 查找字符的数字表示
our - 声明和赋值包变量(词法作用域)
pack - 将列表转换为二进制表示
package - 声明单独的全局命名空间
pipe - 打开一对连接的文件句柄
pop - 从数组中移除最后一个元素并返回它
pos - 查找或设置最后/下一个 m//g 搜索的偏移量
print - 将列表输出到文件句柄
printf - 将格式化的列表输出到文件句柄
prototype - 获取子程序的原型(如果有)
push - 将一个或多个元素附加到数组
q - 单引号字符串
qq - 双引号字符串
qr - 编译模式
quotemeta - 转义正则表达式特殊字符
qw - 引用单词列表
qx - 反引号引用字符串
rand - 获取下一个伪随机数
read - 从文件句柄进行固定长度的缓冲输入
readdir - 从目录句柄获取目录项
readline - 从文件中获取记录
readlink - 确定符号链接指向的位置
readpipe - 执行系统命令并收集标准输出
recv - 通过套接字接收消息
redo - 重新开始本次循环迭代
ref - 找出被引用的对象的类型
rename - 更改文件名
require - 在运行时加载库中的外部函数
reset - 清除所有给定名称的变量
return - 提早退出函数
reverse - 反转字符串或列表
rewinddir - 重置目录句柄
rindex - 从右到左的子字符串搜索
rmdir - 删除目录
s - 将模式替换为字符串
scalar - 强制标量上下文
seek - 为随机访问I/O重新定位文件指针
seekdir - 重新定位目录指针
select - 重置默认输出或进行I/O多路复用
semctl - SysV 信号量控制操作
semget - 获取 SysV 信号量集
semop - SysV 信号量操作
send - 通过套接字发送消息
setgrent - 准备使用组文件
sethostent - 准备使用主机文件
setnetent - 准备使用网络文件
setpgrp - 设置进程的进程组
setpriority - 设置进程的 nice 值
setprotoent - 准备使用协议文件
setpwent - 准备使用 passwd 文件
setservent - 准备使用服务文件
setsockopt - 设置套接字选项
shift - 从数组中移除第一个元素并返回它
shmctl - SysV 共享内存操作
shmget - 获取 SysV 共享内存段标识符
shmread - 读取 SysV 共享内存
shmwrite - 写入 SysV 共享内存
shutdown - 关闭套接字连接的一半
sin - 返回数字的正弦值
sleep - 阻塞一段时间
socket - 创建套接字
socketpair - 创建一对套接字
sort - 对值列表进行排序
splice - 在数组中的任何位置添加或删除元素
split - 使用正则表达式分隔符分割字符串
sprintf - 将格式化的输出写入字符串
sqrt - 平方根函数
srand - 为随机数生成器设置种子
stat - 获取文件的状态信息
study - 优化输入数据以进行重复搜索
sub - 声明子程序,可能匿名
substr - 获取或更改字符串的一部分
symlink - 创建指向文件的符号链接
syscall - 执行任意系统调用
sysopen - 打开文件、管道或描述符
sysread - 从文件句柄进行固定长度的无缓冲输入
sysseek - 定位与 sysread 和 syswrite 一起使用的句柄上的 I/O 指针
system - 运行单独的程序
syswrite - 向文件句柄进行固定长度的无缓冲输出
tell - 获取文件句柄上的当前查找指针
telldir - 获取目录句柄上的当前查找指针
tie - 将变量绑定到对象类
tied - 获取对绑定变量底层对象的引用
time - 返回自 1970 年以来的秒数
times - 返回自身和子进程的已用时间
tr - 转化字符串
truncate - 截断文件
uc - 返回字符串的大写版本
ucfirst - 返回仅将第一个字母转换为大写的字符串
umask - 设置文件创建模式掩码
undef - 删除变量或函数定义
unlink - 删除指向文件的链接
unpack - 将二进制结构转换为普通的 Perl 变量
unshift - 在列表的开头添加元素
untie - 解除与变量的绑定
use - 在编译时加载模块
utime - 设置文件的最后访问和修改时间
values - 返回哈希中值的列表
vec - 测试或设置字符串中的特定位
wait - 等待任何子进程结束
waitpid - 等待特定子进程结束
wantarray - 获取当前子程序调用的 void、标量或列表上下文
warn - 打印调试信息
write - 打印图片记录
-X - 文件测试 (-r, -x 等)
y - 转化字符串