题记:把这篇文章发到c++专区,没有哗众取宠的意思,这里我想套用阿里巴巴一个资深CTO的话:程序员的知识结构应该是T形的,要精通一到两门编程语言作为主干,而在顶端则是要多种语言结合使用,也就是我们常说的深度和广度,那么为什么要学习Perl,我相信有过Perl经验的程序员一定会对这种脚本语言语义的丰富和强大的字符处理能力有深刻的印象。的确,对于习惯了静态语言的我们,Perl是个全新的世界,没有变量声明,没有main函数,比起c++, 我觉得perl更向是一个不羁的顽童,perl的语法中充满了freedom的思想
假设有以下一个任务,编一个函数将一个文件中所有在尖括号中间的字符由小写转换为大写,这个工作如果用c++来完成的话可能得费一番心思,来看看Perl的解决方案吧
1#! perl -w
2&replaceString($ARGV[0];);
3
4sub replaceString
5{
6 open FILE,"< $_[0]"
7 or die "can' open file:$!";
8 while(<FILE>)
9 {
10 s/\w*(<[^>]+>)/\U$1/g;
11 print ;
12 }
13}
以上是perl的解决方案,第一句话是perl的调用语句,对于windows系统意义不大,第二句话是将读入的第一个参数传入子例程replaceString,数组ARGV用来存放命令行传入的参数,接下来就是子例程,其中的核心语句就是s/\w*(<[^>]+>)/\U$1/g;向天书一样,不过如果你对正则表达式熟悉的,也可以看出一些门道来,这也正是perl语言的强大之处,对于正则表达式的原生支持
学习perl,首先要忘掉C++古板的作风:
1,这里没有main函数,perl不会生成所谓的可执行文件,源文件就是可执行文件,这句话是说给没有脚本经验的朋友们的听的,解释型语言编译之后走哪算哪,没有所谓的入口。
2.perl中常用的只有3种常量类型,标量、数组和散列
标量包括常用的字符串,数字,类型是不确定的,perl会根据你的上下文情景做“自然的”转换,声明标量使用$前缀
例如你可以写出以下语句,轻松得到结果
$num1=5;
$num2=5**2;
print "the square of $num1 is $num2";
(结果你可以自己试试看)
而数组则是perl另一个灵活强大的类型,声明数组使用@前缀,还是以一个例子说明吧
1#! perl -w
2 $sentence = "I love c++ and perl";
3 @words = split " ",$sentence;
4 print "the sentence \"@words\" has ".@words." words\n";
输出是the sentence "I love c++ and perl" has 5 words,是的,你也许意识到,perl自动根据你需要做了转换,这部分涉及标量上下文和列表上下文(超出本文讨论范围,有兴趣可以深入研究),是perl的一个重要特性。然而perl 数组还有许多强大特性,例如,将以上例子稍作修改如下:
1#! perl -w
2 $sentence = "I love c++ and perl";
3 @words = split " ",$sentence;
4 @words = @words[0,1,4];
5 print "the sentence \"@words\" has ".@words." words\n";
有兴趣的朋友可以试试看结果
3, 函数参数列表的括号可加可不加,就像上面调用open函数,正规写法应该是open(FILE,"$_[0]"),原因就在于perl觉得挪动两根手指去输入括号,是很费时的,而大多数情况下不加括号并不会引起歧义
4,函数的返回值为默认的最后一个表达式的值,请注意,perl的函数没有void的类型,任何函数都有返回值,且不用你去费事的写return,而return在perl中又叫“多余的7个字母”
5,子例程参数列表 @_的使用,你也许会对第一个例子中的子例程replaceString有的意外,没有参数列表,是的,perl的子例程没有参数列表,不去规定每个函数可以接受什么参数,多少个参数,所有传入的参数都会在函数调用的时候自动存入@_这个特殊数组(诸如此类的特殊符号perl中还有许多),而数组的第一个元素可以像这样应用$_[0],第二个$_[1]...依次类推,所以,我可以将第一个例程稍作修改,使他可以适用于更多的输入参数,达到一次处理多个文件的效果
1&replaceString(@ARGV);
2
3sub replaceString
4{
5 foreach $id (0..@_-1)
6 {
7 open FILE,"< $_[$id]"
8 or die "can' open file $_[$id]:$!";
9 while(<FILE>)
10 {
11 s/\w*(<[^>]+>)/\U$1/g;
12 print ;
13 }
14 close FILE;
15 }
16}
6,以上的例子还可以进一步简化
1&replaceString(@ARGV);
2
3sub replaceString
4{
5 foreach (@_)
6 {
7 open FILE,"< $_"
8 or die "can' open file $_[$id]:$!";
9 while(<FILE>)
10 {
11 s/\w*(<[^>]+>)/\U$1/g;
12 print ;
13 }
14 close FILE;
15 }
16}
注意第5行和第7行的变化,出现了一个新的面孔$_,它成为默认变量,那它默认指代谁呢?在循环语句中,它默认指代循环变量,注意到foreach中省略了他原有的循环变量$id,那这时$_就指向了它,也许你会觉得这回令程序产生歧义,其实这些担心是多余的,事实上它在perl中很好用,可以使写出来的程序简洁优美,perl中还有许多诸如此类的变量:
$_ 默认变量,多用于循环语句指代循环变量
$! 错误信息包含变量,当调用系统API出错的时候,系统的错误信息会自动写入这个变量
$` 正则表达式匹配前置变量
$& 正则表达式匹配变量
$' 正则表达式匹配后置变量
$1,$2,$3... 正则表达式匹配临时变量
前面两个我们已经见过了,后面四个都是关于正则表达式的,还是以一个例子说明吧
#! perl -w
use strict;
sub readMappingFile
{
my ($fileName)=@_;
my %mapping;
open MAPFILE,"< $fileName"
or die "can't open file $fileName:$!";
while(<MAPFILE>)
{
chomp;
if(/^(\w+)\s+/)
{
$mapping{$1}=$';
}
}
%mapping;
}
no strict;
print $ARGV[0]." is open\n";
%mapping=readMappingFile $ARGV[0];
while(($key,$value)= each %mapping)
{
print "$key=>$value\n"
}
这个例子需要传入一个命令行参数,该参数是个文本文件的文件名,程序将读入文本文件的内容,将它存入散列%mapping中(以%为前缀的变量声明未散列,相当于C++ STL中的map类型),最后将其打印出来
例如:文本内容为:
1 cnblog
2 cppblog
csdb http://blog.csdn.net/dawnbreak/
cppblog http://www.cppblog.com/dawnbreak/
将会输出:
1.txt is open
1=>cnblog
cppblog=>http://www.cppblog.com/dawnbreak/
2=>cppblog
csdb=>http://blog.csdn.net/dawnbreak/
注意到各行每两个健值之间的空格或制表符并不一样,但是输出格式确是一致
这篇文章前前后后写了很长时间,决定还是先发出来,慢慢更新