《Perl语言入门》学习笔记

0.前言

本文主要是记录perl学习中的一些关键知识点,尽可能帮助平日的脚本开发需要

1.标量数据

a 数字

perl内部对数字,统一都是按照双精度浮点数double类型来存储的,

b 字符串

最短的字符串是不含任何字符的空串,最长的字符串没有限制
字符串相关操作
  • 分割:使用的是split函数,split /,/ $str 等价于split(/,/, $str),返回的是数组
  • 连接:字符串的连接使用的是符号.来进行,例如 $newStr=$a . " " . $b;
  • 重复:例如需要得到abcabcabc,那么可以根据字符串进行这样的操作 "abc" x 3
  • 子串:substr()获取子串,substr(字符串,开始的索引位置,长度,替换的字符串)
  • 长度:length()返回
  • 比较:字符串的比较用的是eq ne

perl中的字符串和数字会自动转换,具体情况是看使用了什么操作符

数字的飞碟操作符

a <=> b 表示对数字a和数字b进行比较,返回-1,0,1三种结果

2.变量

变量的类型主要是有这么几种

  • 标量:$a = 1
  • 数组: @a = (1, 2, 3)
  • hash: %a = ('a' => 0, 'b' => 1, 'c' => 2)

perl的三种变量是存在不同的命名空间里,所以 $a, @a, %a 三者是不冲突的。但我们应该避免这样命名,可读性还是很糟糕的。

字符串标量内插

被双引号包裹的字符串,可以使用变量内插,这个和linux shell中的一样,例
"hello $name" 等价于 'hello' . $name

3.控制结构

if

这个与java中的一样,唯一不同的是,逻辑判断可以使用 and, not, or, xor, 还有 ne, eq 等等
值得注意的是,与js一样,if对不同类型的变量的布尔值处理也是有规律的

  • 0 false, 其他数值都是true
  • 值为字符串,空字符串是假,其他有长度的为真
  • 既不是数字,也不是字符串,就会先试图转换为数字或者字符串
unless

unless 等于 if ( not xxxx) 这个样子

unless (a == 1) {
	# do something
}
elsif

等于java中的else if

获取行输入

获取用户的行输入,使用的是句柄<STDIN>,返回一个字符串,并且一般最后带有一个换行符。

$ilne=<STDIN>;
if ($line eq "\n") {
	print "That was just a blank line!\n";
} else {
	print "That line of input was $line";
}
chomp

只能用于字符串变量上,仅把换行符去掉

while

与java一样,真假值得判断和if中的一样

循环体中的几种控制符
last

等于break

next

等于continue

带标签的块

perl中建议使用大写来作为标签名,标签的使用时 标签名+冒号

LINE: while(<>) {
	foreach (split) {
		last LINE if /__END__/;
	}
}

三目运算符

my $size = 
	($width <10 ) ? "small" :
	($width <20 ) ? "medium" :
	($width <30 ) ? "large" :
		"extra-large";			#default
undef

如果没有赋值某个变量,那这个变量的的初始值是undef
- 当被作为数字使用时,为0
- 当被当做是字符串使用时,是空串

defined函数

该函数可以区分undef和空串,例如在读取文件的时候,当eof了,则会返回undef回来,这个时候就可以通过这个来判断了

$madona=<STDIN>;
if (defined($madona)) {
	print "the input is $madona";
} else {
	print "the input is empty\n";
}

4.列表与数组

数组下标从0开始,以此类推,最后一个元素的下标是-1。

列表直接量

需要引入列表的时候可以写成数组的样子,在圆括号中用逗号隔开的一系列值

(1,2,3)
("a","b",1)
(1..100)
qw

如果只是建立简单的单词列表,那么许多乏味的双引号和逗号会惹人讨厌。perl提供了qw的写法来简化
qw( a b c d e )

qw=quoted word,译为加上引号的单词,不论如何,perl会将里面当做是字符串来处理,qw的边界符不仅仅是括号,基本上成对出现的字符就行

qw ! a b c !
qw / a b c /
qw # a b c #
qw < a b c >

以上的几种写法都是合法的

列表赋值

( $a, $b, $c ) = ("111", "222", "333")
左侧列表中的三个变量,依次会赋予右侧列表的值

push pop shift unshift

push和pop对对数组的最后一个元素来进行操作

@arr=1..3
@brr=5..7
push(@arr, "1")
push @arr,@brr;  # @arr得到@brr数组的所有元素
pop(@arr)

shift与unshift刚好相反,是对第一个元素来进行操作的

shift(@arr)			# 弹出第一个元素
unshift(@arr,"1")	# 将1添加到第一个位置

foreach

示例,每次循环迭代的时候,里面的控制变量是数组中的元素本身,任何修改都可以反映到本身。

@rocks=qw / 1 2 3 4 /
foreach $rock (@rocks) {
	$rock = "\t$rock";  
	$rock .= "\n";
}

如果我们没有指定控制变量,那么会被默认存在$_

foreach (1..10) {
	print "current number $_"
}

reverse sort

sort可以指定比较规则,比较规则是一个函数

sub by_ascii {
	$a cmp $b;
}
my @strings = sort by_ascii @any_strings;

5.子程序

子程序更像是我们在其他语言中见到的函数,关键词是sub 开头的。子程序可以定义在任何位置,而不必要先声明。

参数,传给perl的子程序的参数,会被放入到参数数组@_ 中,你可以通过$_[0]这种来进行访问。

子程序可以通过检查@_的长度,来检查是否传入的参数是否正确。

sub max {
	if (@_ != 2) {
		print "WARNNING...."
	}
}

注意注意@_$_是完全不同的,一个是数组长度,一个是在for遍历中代表当前元素的变量

私有变量

在没有特殊说明情况下,perl中定义的变量都是全局的,这显然不是一件好事,我们可以通过my来指定变量的作用范围。

6.输入与输出

钻石输入符号<> , 我们假设有如下的一个脚本my.pl

#!/usr/bin/perl

while (defined($line = <>)) {	
	chomp($line);
	print "It was $line that I saw\n"
}

考虑到上面这段代码,钻石操作符是从文件或者标准输入流来读取文件的内容,我们创建一个临时文件,文件的内容如下

#/tmp/demo1
hahah
fdsafdsa
fdasfdsa
feiwqrf

然后我们执行命令perl /tmp/my.pl /tmp/demo1 得到的输出内容如下

It was hahah that I saw
It was fdsafdsa that I saw
It was fdasfdsa that I saw
It was feiwqrf that I saw

命令修改为perl /tmp/my.pl - 就变为了从标准输入来获取内容了

hahaha
It was hahaha that I saw
mymy
It was mymy that I saw
^C

注意,如果在使用钻石操作符去读取多个文件时,其实是这几个文件先被合并为一个大文件了,然后从头开始被顺序的读取下去。而当前正在处理的文件,会在变量$ARGV

#my.pl
#!/usr/bin/perl

while (defined($line = <>)) {	
	chomp($line);
	print "FILE: $ARGV It was $line that I saw\n"
}

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

FILE: /tmp/demo1 It was hahah that I saw
FILE: /tmp/demo1 It was fdsafdsa that I saw
FILE: /tmp/demo1 It was fdasfdsa that I saw
FILE: /tmp/demo1 It was feiwqrf that I saw
FILE: /tmp/demo2 It was 1111 that I saw
FILE: /tmp/demo2 It was 2222 that I saw
FILE: /tmp/demo2 It was 3333 that I saw

命令行参数列表@ARGV

文件句柄

my $fp;
open($fp, "</tmp/1.txt");
while (my $line = <$fp>) {
	....
}
close($fp);
改变默认的文件输出
select SOME_FILE;
print "123\n";

一旦切换了默认的输出文件句柄,程序就会一直往那里进行输出。因此比较好的方式是,对不同的输出,进行指定。

select LOG;
$|=1; #这行的意思是清除缓冲区的数据
select STDOUT;
print LOG "I'm a log"  # 指定输出的句柄

7.哈希

哈希就像是java语言中的map结构一样。

访问哈希

$hash{ $someKey }

哈希赋值

在访问哈希的基础上,右边添加一个值,就是已完成赋值了

访问整个hash

想要指代整个hash,就需要使用百分号作为前缀,hash可以被转换为列表,是一些简单的键值对列表

#!/usr/bin/perl

%demo_hash = ( 
"a" => 1, 
"b" => 2, 
"c" => 3, 
"d" => 4
);


@demo_array = %demo_hash; # 这里会将hash转换为键值对
my $l = @demo_array;
print "@demo_array\n";
print "size=$l\n"

------ output --------
c 3 a 1 b 2 d 4
size=8

胖箭头

上面的方式来书写hash显然不是很方便和直观,于是就有了使用胖箭头的方式来进行

my %hash = (
	"a" => "1",
	"aa" => "2",
	"aaa" => "3"
)

哈希函数

keys 和values
my @k = keys %hash; # 注意这里是要访问整个hash,不用$
my @v = values %hash;
each

如果需要罗列键值对,则可以使用each函数

while ( ($key, $value) = eash %hash ) {
	print "$key => $value";
}
嵌套hash
$some_hash{a}{b}{c} = 1
exists函数

检查hash中是否有某个键

if (exists($hash{"a"})) {
	# do something
}
delete函数

从hash中删除指定key的值,如果没有key,则程序会直接结束,而不会有任何的信息输出

%ENV

程序运行的环境变量,会在%ENV可以获取到,print "PATH is $ENV{PATH}\n"

8.模块

使用命令来查询本地是否已经安装了某个模块,例如perldoc CGI

使用模块

use File::Basename
#....

导入模块与自定义函数冲突

有可能你导入的模块与自己定义的函数有着相同的名字,这个时候就需要指定导入的列表
例如你有定义一个函数dirname,而File::Basename里面也有这个函数。

use File::Basename qw / basename /; # 引入basename函数
# or
use File::Basename qw/ /; #不引入任何函数

9.文件

文件测试符

操作符 意义
-r 对目前用户或组来说是具有可读的
-w 对目前用户或组来说是具有可写的
-x 对目前用户或组来说是可执行的
-o 由目前用户拥有
-e 是存在的
-R -W -X -O 是前面4种的大写,表示对实际用户
-z 文件存在,并没有内容(对目录来说永远为假)
-s 文件或目录存在,且有内容
-f 是普通文件
-d 是普通目录
-l 是符号链接
-C 最后一次inode变更后到今天的天数
-M 最后一次修改到今天的天数
-A 最后一次访问到尽头的天数

虚拟文件句柄

如果我们需要判断一个文件是否是可读可写的,那么我们可能需要使用如下的代码

if ( -r $file and -w $file ) {
	# do something
}

但实际上这是个非常不划算的操作,因为在-r的时候,我们就其实已经拿到了文件的信息,第二次没有必要再获取一次。这个时候就可以使用perl独有的虚拟句柄来解决

if (-r $file and -w _ ) {
	# do something
}

虚拟文件句柄_指代的是最近一次文件查询,它可可以不写在一个表达式里

if (-r $file) {};
if (-w _ ) {};

栈式测试

除了使用虚拟句柄以外,perl还提供了栈式测试的方式

if (-r -w -x -o $file) {
	# do something
}

值得注意的是,栈式写法应该用于返回的都是boolean的测试符,而不要用于返回数字的。

stat

stat($file)返回的是一个数组,不同位置代表了不同的信息

localtime

获取当前的时间

目录句柄

如果想要从目录里获得文件列表,那么可以使用目录句柄。

opendir($dir, '/tmp/');
foreach $f (readdir $dir) {
	print "one file in /tmp/ is $f\n";
}
closedir($dir);

删除,重命名,软链接

unlink(file1,file2,file3...);
rename "oldname", "newname";   #必须属于一个盘,其他盘的话则不行
symlink "realFile" "linkFile"; # 软链接

10.进程管理

system函数

启动进程最简单的方式就是通过system函数,例如system "date";
注意,如果要使用到shell环境的变量,那么一定是要使用单引号,或者转义字符来表示
system 'ls $HOME' or system "ls \$HOME"

exec 函数

与system函数用法基本是一样的,但system函数是在perl空闲的时候去调用子程序,而exec是perl进程自己去执行子程序任务。exec一般会同fork一起使用,在吃不准要使用什么的情况下,system一般总是对的。

写在exec后面的代码是不会被执行的,除非是编程接管启动中的错误

exec "date"
die "date couldn't run: $!\n"

反引号获取输出

有的时候我们想要获取shell的执行结果,而system和exec都是输出到perl的标准输出,那我们就可以考虑使用反引号来获取

my $now = `date`;
print "the time is now $now\n";

奇淫巧技

智能匹配

考虑如下这种场景,我们想在哈希中找出包含Fred的key,并将其key打印出来。对此我们首先会想到的方法是,通过循环来遍历,然后对key判断是否包含,判定结果为true则打印出来。

my $flag = 0;
foreach my $key ( keys %names ) {
	next unless $key =~ /Fred/;
	$flag = $key;
	last;
}

上面写法的好处是,对于任何版本的perl都使用。在5.010版本以上,提供了智能操作符,我们可以将上面的需求写作一行。
say "i found a key matching 'Fred'" if %names ~~ /Fred/;

正则匹配提取

考虑到下面2个代码

#!/usr/bin/perl

my $line = "name: john, age: 18, birthday: 2003.10, height: 188";
printf("old message= $line\n");
if ( $line =~ /name: (\w+), age: (\d+), birthday: (.+), height: (\d+)/) {
	printf("1=%s 2=%s 3=%s 4=%s\n", $1,$2,$3,$4);
} else {
	printf("nothing\n");
}

if ( $line =~ /name: \w+, age: \d+, birthday: (.+), height: (\d+)/) {
	printf("1=%s 2=%s 3=%s 4=%s\n", $1,$2,$3,$4);
} else {
	printf("nothing\n");
}

第一个if条件中,所有的匹配像都被圆括号包裹了,第二个if条件,前面2个没有被包裹。我们运行一下看看结果。

old message= name: john, age: 18, birthday: 2003.10, height: 188
1=john 2=18 3=2003.10 4=188
1=2003.10 2=188 3= 4=

我们发现第二个的if可以匹配到内容,但是$3 $4 的内容都是空的,只有$1 $2有内容,并且内容是有带括号的birthday和height的。所以,perl 正则匹配会将被()包裹的内容存放到$1 $2 ... 的变量中去,这点在实际编程的时候十分的有用。

posted @ 2021-05-24 13:56  myCodeLikeShit  阅读(354)  评论(0编辑  收藏  举报