Perl 自学笔记

37 views
Skip to first unread message

marc...@gmail.com

unread,
Jul 26, 2005, 11:30:51 AM7/26/05
to mar...@googlegroups.com
Perl 语言编程:

前言:
该阅读的书籍:
《Mastering Regular Expressions》, Jeffrey Friedl
《Perl Cookbook》,Tom Christiansen和Nathan Torkington

第一部分:概述
第一章:Perl概述
赋值:双引号可以用来变量内插和反斜扛内插,比如把\n解释成换行符;单引号取消内插;反引号用来执行外部程序并返回程序的输出。
举例:
$pi=3.14159265; #一个实数
$avocados=6.02e23; #科学计数法
$sign="I love my $pet"; #带内插的字符串
$thence=$whence; #另一个变量的值
$exit system("vi $file"); #一条命令的数字状态
$cwd=`pwd`; #从一个命令输出的字符串
标量也可以保存对其他数据结构的引用,包括子例程和对象。
$ary=\@myarray; #引用一个命名数组
$hah=\%myhash; #引用一个命名散列
$sub=\%mysub; #引用一个命名子例程
$ary=[1,2,3,4,5]; #引用一个未命名的数组
$fido=new Camel "Amelia"; #引用一个对象
如果使用的是未赋值的变量,perl会自动根据上下文的环境来确定变量的类型和初值:
$camels='123';
print $camels+1, "\n";
#最初变量camels是个字符串变量,但是遇到+1,perl会先转换成数值型变量,然后再转换成字符串显示出来。
数组赋值:
@home = ("couch", "chair", "table", "stove");
从数组给4个变量赋值:
($potato, $lift, $tennis, $pipe) = @home;
交换两个变量不需要中间值:
($alpha,$omega) = ($omega,$alpha);
数组是从0开始计数的,如果你想一次对一个数组元素进行赋值:
$home[0] = "couch";
$home[1] = "chair";
$home[2] = "table";
$home[3] = "stove";
散列是无序的,不象数组类似于一个堆栈,有开始也有结尾。因此构建散列时必须同时指定值和健。
%longday = ("Sun", "Sunday", "Mon", "Monday", "Tue", "Tuesday",
"Wed", "Wednesday", "Thu", "Thursday", "Fri",
"Friday", "Sat", "Saturday"); #非常难读
%longday = (
"Sun" => "Sunday",
"Mon" => "Monday",
"Tue" => "Tuesday",
"Wed" => "Wednesday",
"Thu" => "Thursday",
"Fri" => "Friday",
"Sat" => "Saturday",
); #非常清晰,"健" => "值"
散列单个赋值:
$wife{"Adam"} = "Eve";
复杂的数据结构:
$wife{"Jacob"} = ["Leah", "Rachel", "Bilhah", "Zilpah"]; #
等于下面4行
$wife{"Jacob"}[0] = "Leah";
$wife{"Jacob"}[1] = "Rachel";
$wife{"Jacob"}[2] = "Bilhah";
$wife{"Jacob"}[3] = "Zilpah";

$kids_of_wife{"Jacob"} = {
"Leah" => ["Reuben", "Simeon", "Levi", "Judah", "Issachar",
"Zebulun"],
"Rachel" => ["Joseph", "Benjamin"],
"Bilhah" => ["Dan", "Naphtali"],
"Zilpah" => ["Gad", "Asher"],
};
$kids_of_wife{"Jacob"}{"Leah"}[0] = "Reuben";
$kids_of_wife{"Jacob"}{"Leah"}[1] = "Simeon";
$kids_of_wife{"Jacob"}{"Leah"}[2] = "Levi";
$kids_of_wife{"Jacob"}{"Leah"}[3] = "Judah";
$kids_of_wife{"Jacob"}{"Leah"}[4] = "Issachar";
$kids_of_wife{"Jacob"}{"Leah"}[5] = "Zebulun";
$kids_of_wife{"Jacob"}{"Rachel"}[0] = "Joseph";
$kids_of_wife{"Jacob"}{"Rachel"}[1] = "Benjamin";
$kids_of_wife{"Jacob"}{"Bilhah"}[0] = "Dan";
$kids_of_wife{"Jacob"}{"Bilhah"}[1] = "Naphtali";
$kids_of_wife{"Jacob"}{"Zilpah"}[0] = "Gad";
$kids_of_wife{"Jacob"}{"Zilpah"}[1] = "Asher";

名字空间:建立自己的名字空间使得程序中使用的变量不会被混淆,比如:
package Camel;
$fido = &fetch(); # $fido的真实名字应该时$Camel::fido
&Camel::fetch
package Dog;
$fido = &fetch(); # $fido代表$Dog:fido &Dog:fetch
两个名字空间中的变量不会被混淆,但是同一时间只能有一个名字空间。
引用已经写好的模块:CPAN中有大量的已经写好的模块供使用。
use Camel;
use strict; #
严格的约束Perl中的一些规则,一般代码超过一屏最好使用它
执行单行perl程序:
perl -e 'print "Hello, world!\n";'
文件句柄:
open(SESAME, "filename") # read from existing file
open(SESAME, "<filename") # 同上,显式指定
open(SESAME, ">filename") # create file and write to it
open(SESAME, ">>filename") # append to existing file
open(SESAME, "| output-pipe-command") # set up an output filter
open(SESAME, "input-pipe-command |") # set up an input filter
钻石操作符:<STDIN>
这本书里面没有这么说,不过在入门的那本书确实这么形容的。
print STDOUT "Enter a number: "; # ask for a number
$number = <STDIN>; # input the number
print STDOUT "The number is $number.\n"; # print the number
chomp和chop:我认为只要记住chomp就可以了,因为chomp会自动去除结束标记,例如换行,而chop不管最后一个是什么字符,都会去掉。
chomp($number = <STDIN>); #输入数字并删除换行符

marc...@gmail.com

unread,
Jul 27, 2005, 11:55:15 AM7/27/05
to mar...@googlegroups.com
二元操作符:幂先于乘法,乘法先于加法,但是最好还是使用括号,如果不想给自己找麻烦的话
$a + $b #加法,$a和$b相加
$a * $b #乘法,$a和$b的积
$a % $b #模,$a被$b除的余数
$a ** $b #幂,取$a的$b次幂
字符串操作符:
$a = 123;
$b = 456;
print $a + $b; # prints 579
print $a . $b; # prints 123456
字符串的“乘法”,应该称做“重复”
$a = 123;
$b = 3;
print $a * $b; # prints 369
print $a x $b; # prints
123123123,这里perl自动做了转换的动作,将$a转换成字符串
x的作用举例:设$scrwid是屏幕的宽度,程序会在屏幕上画一条线
print "-" x $scrwid, "\n";
特别的赋值操作符:
$a *= 3; #等于 $a = $a * 3;
$line .= "\n"; # Append newline to $line.
$fill x= 80; # Make string $fill into 80 repeats of itself.
$val ||= "2"; # Set $val to 2 if it isn't already "true".
$a = $b = $c = 0; #3个变量清0.
($temp -= 32) *= 5/9; #将华氏温度转换成摄氏温度
一元算数操作符:和C语言一样
++$a, $a++
--$a, $a--
逻辑操作符:有2组,优先级不同
$a && $b #与,$a假,则为$a,否则为$b
$a || $b #或,$a为真,则为$a,否则为$b
! $a #非,如果$a为假,则为真
$a and $b #与
$a or $b #或
not $a #非
$a xor
$b #异或,如果$a或$b为真,但不同时为真,则为真
open(GRADES, "grades") or die "Can't open file grades: $!\n";
#如果成功打开文件,则跳到下一行继续执行,如果不能打开文件,程序将打印一个错误小溪并停止执行
比较操作符:有2种,一种针对数字,另外一种针对字符串,和shell正好相反
比较 数字 字符串 返回值
等于 == eq 如果$a等于$b,返回真
不等于 != ne 如果$a不等于$b,返回真
小于 < lt 如果$a小于$b,返回真
大于 > gt 如果$a大于$b,返回真
小于等于 <= le 如果$a不大于$b,返回真
大于等于 >= gt 如果$a不小于$b,返回真
比较 <=> cmp 相等时为0,如果$a大为1,如果$b大为-1
文件测试操作符:非常好用。。。
例子 名字 结果
-e $a 存在 如果$a中命名的文件存在,则为真
-r $a 可读 如果$a中命名的文件可读,则为真
-w $a 可写 如果$a中命名的文件可写,则为真
-d $a 目录 如果$a中命名的是个目录,则为真
-f $a 文件 如果$a中命名的是个普通文件,则为真
-T
$a 文本文件 如果$a中命名的文件是文本文件,则为真
控制结构:
搞清楚“真”的概念,有4条规则:
除""和"0"外,所有字符串为真
除0外,所有数字为真
所有引用为真
所有未定义的值为假
0 # would become the string "0", so false.
1 # would become the string "1", so true.
10 - 10 # 10 minus 10 is 0, would convert to string "0", so false.
0.00 # equals 0, would convert to string "0", so false.
"0" # is the string "0", so false.
"" # is a null string, so false.
"0.00" # is the string "0.00", neither "" nor "0", so true!
"0.00" + 0 # would become the number 0 (coerced by the +), so false.
\$a # is a reference to $a, so true, even if $a is false.
undef() # is a function returning the undefined value, so false.

if和unless语句
if ($debug_level > 0) {
# Something has gone wrong. Tell the user.
print "Debug: Danger, Will Robinson, danger!\n";
print "Debug: Answer was '54', expected '42'.\n";
}
在perl中大括号是必须的。
if ($city eq "New York") {
print "New York is northeast of Washington, D.C.\n";
}
elsif ($city eq "Chicago") {
print "Chicago is northwest of Washington, D.C.\n";
}
elsif ($city eq "Miami") {
print "Miami is south of Washington, D.C. And much warmer!\n";
}
else {
print "I don't know where $city is, sorry.\n";
}
对的,这里是elsif
如果这不是真的,就做某事:
unless ($destination eq $home) {
print "I'm not going home.\n";
}
循环结构:while, until , for和foreach
while和until语句类似于if和unless,如果条件满足(while语句为真,until是假)
while ($tickets_sold < 10000) {
$available = 10000 - $tickets_sold;
print "$available tickets are available. How many would you like:
";
$purchase = <STDIN>;
chomp($purchase);
$tickets_sold += $purchase;
}
while (@ARGV) {
process(shift @ARGV);
}
#每次循环,shift操作符都从参数数组中删除一个元素(同时返回这个元素),当数组@ARGV用完后循环自动推出,这时数组长度为0。

for语句:和C语言中非常类似
for ($sold = 0; $sold < 10000; $sold += $purchase) {
$available = 10000 - $sold;
print "$available tickets are available. How many would you like:
";
$purchase = <STDIN>;
chomp($purchase);
}

foreach语句:条件表达式是在列表环境,而不实标量环境中。循环变量直接指向元素本身,修改循环变量,就是修改原始数组,foreach比for用的多的多。
foreach $user (@users) {
if (-f "$home{$user}/.nexrc") {
print "$user is cool... they use a perl-aware vi!\n";
}
}

跳出控制结构:next和last
next操作符跳至本次循环的结束
last操作符跳至整个循环的结束
foreach $user (@users) {
if ($user eq "root" or $user eq "lp") {
next;
}
if ($user eq "special") {
print "Found the special account.\n";
# do some processing
last;
}
}

一些正则表达式的例子:
while ($line = <FILE>) {
if ($line =~ /http:/) {
print $line;
}
}
while (<FILE>) {
print if /http:/;
} #和上面的例子想同,但更简单
匹配最少7个数字,最多11个数字:\d{7,11}
匹配7个数字:\d{7}
匹配大于等于7的数字:\d{7, }
+代表{1, }
*代表{0, }
?代表{0,1}
/bam{2}/匹配"bamm"
/(bam){2}/匹配"bambam"
最小匹配:一般的匹配都会进行贪婪匹配,匹配到最右边,可以强制指定第一个匹配。
/.*?:/ 将停止在第一个冒号,而不是最后一个冒号
\b为单词边界,/\bFred\b/将匹配"The Great
Fred"但不匹配"Frederick"因为d后面的字符是单词字符
因此前面所说的/\d{7,11}/
并不排除11位后面的数字,因此需要在量词两头实用锚点,如\b
/<(.*?)>.*?<\/\1>/ 将匹配"<B>Blod</B>"
互换两个词: s/(\S+)\s+(\S+)/$2 $1/

marc...@gmail.com

unread,
Jul 28, 2005, 9:09:25 PM7/28/05
to mar...@googlegroups.com
第二章:
语句的任何地方在以=开头的行都是合法的,Perl将忽略从这一行开始直到下一个由=cut开头的行,这些被认为是pod(plain
old documentation),可以当成多行注释的方法。
Perl只提供三种基本数据类型:标量、标量数组、标量散列
数组从0开始计数,也可以是负数计数,负数的话就是从后往前。
$days Simple scalar value $days
$days[28] 29th element of array @days
$days{'Feb'} "Feb" value from hash %days
${days} Same as $days but unambiguous before alphanumerics
$Dog::days Different $days variable, in the Dog package
$#days Last index of array @days
$days->[28] 29th element of array pointed to by reference $days
$days[0][2] Multidimensional array
$days{2000}{'Feb'} Multidimensional hash
$days{2000,'Feb'} Multidimensional hash emulation

@days Array containing ($days[0], $days[1],... $days[n])
@days[3, 4, 5] Array slice containing ($days[3], $days[4], $days[5])
@days[3..5] Array slice containing ($days[3], $days[4], $days[5])
@days{'Jan','Feb'} Hash slice containing ($days{'Jan'},$days{'Feb'})

%days (Jan => 31, Feb => $leap ? 29 : 28, ...)

名字查找,也就是变量名的搜索,perl会预先在词法作用域里查找,文件是最大的词法作用域,如果找不到则代表这个变量是一个package的声明。不管多大的perl都编译成至少一个词法作用域,一个包作用域。

数字直接量:可以用下划线提高可读性,另外可以表示8,16进制的数
$x = 12345; # integer
$x = 12345.67; # floating point
$x = 6.02e23; # scientific notation
$x = 4_294_967_296; # underline for legibility
$x = 0377; # octal
$x = 0xffff; # hexadecimal
$x = 0b1100_0000; # binary

marc...@gmail.com

unread,
Aug 1, 2005, 12:08:16 PM8/1/05
to Marco Lu's Group
字符串直接量:单引号不能用来转义(除了\'和\\意外)另外单引号之前必须有空格。双引号里面可以内插变量和转义。
反斜杠字符转义。
代码 含义
\n 换行符
\r 回车
\t 水平制表符
\f 进纸
\b 退格
\a 警报(响铃)
\e ESC字符
\033 八进制ESC
\x7f 十六进制的DEL
\cC Ctrl+C
\x{263a} Unicode(笑脸)
\N{NAME} 命名字符(只有和user charnames
pragma一起使用时才有效)
用来改变大小写或者随后的字符“以下皆同”的操作:
\u 强制下一个字符大写
\l 强制下一个字符小写
\U 强制后面所有字符为大写
\L 强制后面所有字符为小写
\Q 所有后面的非字母数字字符加反斜杠
\E 结束\U \L \Q

其他字符来替代'和''
常用 通用 含义 可否内插
' ' q/ / 直接量字符串 否
" " qq/ / 直接量字符串 是
` ` qx/ / 执行命令 是
( ) qw/ / 单词列表 否
/ / m/ / 模式匹配 是
s/ / / s/ / / 模式替换 是
y/ / / tr/ / / 字符转换 否
" " qr/ / 正则表达式 是
列表中通用的一列后面的/
/都可以用成对的其他非子字母数字来代替,非常方便。
例如:
$single = q!I said, "You said, 'she said it.'"!;
#这里就用了!来做为定界符替代/

使用use strict 'subs'; 来禁止bare
word,一旦出现,则编译出错。如
print hello, ' ', world, "\n"; #系统将会报错

here-document: 和shell中的用法查不多
print <<EOF;
The price is $Price.
EOF

print <<"EOF"; # 和上面的一样
The price is $Price.
EOF

print <<"dromedary", <<"camelid"; # 可以嵌套
I said bactrian.
dromedary
She said llama.
camelid

funkshun(<<"THIS", 23, <<'THAT'); # doesn't matter if they're in
parens
Here's a line
or two.
THIS
And here's another.
THAT

print << x 10; #
打印下面一行十次,注意后面要留空格或者空行
The camels are coming! Hurrah! Hurrah!

print <<"" x 10; #
比上面好的写法,注意后面要留空格或者空行
The camels are coming! Hurrah! Hurrah!

如果你的here-document在其他代码中是缩进的,可以收工从每行删除开头的空白;
($quote = <<'QUOTE') =~ s/^\s+//gm;
The Road goes ever on and on,
down from the door where it began.
QUOTE

Reply all
Reply to author
Forward
0 new messages