perl的grep用法
grep operator接受一个列表和一个“测试表达式”作为参数。
它从list中一个个的取出元素,然后放到$_变量中。接着在标量环境中,对测试表达式进行估值。如果表达式的结果是true,grep就把$_放到结果list中。
在一个列表环境中,grep operator返回一个列表。在标量环境中,返回列表元素的个数。
my @results = grep EXPR, @input_list;
my $count = grep EXPR, @input_list;
这里,EXPR代表任何显式或隐式地引用$_的标量表达式。
例如,要找出list中所有大于10的数(显式的引用$_):
my @input_numbers = (1, 2, 4, 8, 16, 32, 64);
my @bigger_than_10 = grep $_ > 10, @input_numbers;
隐式的调用$_(找出所有以4结尾的数字):
my @end_in_4 = grep /4$/, @input_numbers;
$_不是列表元素的copy,而是他们的别名,就像foreach中一样。
如果测试表达式逻辑比较复杂,可以封装为函数:
my @odd_digit_sum = grep digit_sum_is_odd($_), @input_numbers;
sub digit_sum_is_odd {
my $input = shift;
my @digits = split //, $input; # Assume no nondigit characters
my $sum;
$sum += $_ for @digits;
return $sum % 2;}
除了上面的表达式形式,还有另一种块形式。不是显式的定义一个函数,我们可以只把函数的主体部分放在grep中。
注意:块后面没有逗号。
my @odd_digit_sum = grep {
my $input = $_;
my @digits = split //, $input; # Assume no nondigit characters
my $sum;
$sum += $_ for @digits;
$sum % 2;
} @input_numbers;
注意这个块和上面函数形式的区别。
再从http://blog.chinaunix.net/u/10363/showart.php?id=51326参照一些例子。
♣剔除list中重复的元素:
my @arr_str = qw(a ba c a a e f c e);
my @unique_str = grep {++$count_hash{$_} < 2} @arr_str;
print Dumper(@unique_str);
♣找出list中出现两次的元素:
应用两次grep
my @arr_twice = grep {$count_hash{$_} == 2} grep {++$count_hash{$_} > 1} @arr_str;
♣找出当前目录的文本文件:
my @files = grep { -f and -T } glob '* .*';
glob返回的是一个文件列表, -f and -T匹配列表的元素(先判断-f效率高一些)。
♣选择数组元素并消除重复:
my @array = qw(To be or not to be that is the question);
my @found_words =
grep { $_ =~ /b|o/i and ++$counts{$_} < 2; } @array;
正则表达式的历史
只能大概的把书上内容列一下了。
最初可以追溯到20世纪40年代,有两个神经生理学家建立了一个神经系统的模型。到了多年以后,数学家Stephen Kleene 用代数方法描述了这个模型,并称之为regular sets。并且他设计了一种简单的记号方法来表达regular sets,叫他们为regular expressions。
之后正则表达式被广泛的研究应用。例如Ken Thompson 的qed。
Grep
- 支持*,但是不支持+和?。
- capturing metacharacters是\(⋯\)
- 不支持alternation。
- 括号只能用来capturing匹配的文本。
EGrep
- 不仅支持+和?,还可以应用到被括号围着的表达式。
把各种不同的正则表达式flavor归结为两种,Basic Regular Expressions (BREs), and Extended Regular Expressions (EREs)。
| Regex feature | BREs | EREs |
| dot, ^, $, [⋯], [^⋯] | √ | √ |
| “any number” quantifier | * | * |
| + and ? quantifiers | + ? | |
| range quantifier | \{min,max\} | {min,max} |
| grouping | \(⋯\) | (⋯) |
| can apply quantifiers to parentheses | √ | √ |
| backreferences | \1 through \9 | |
| alternation | √ |
1986年,Henry Spencer 用C写了正则表达式库。接着Larry Wall发明了Perl,一版又一版的改进了正则表达式,加了一坨特性。
还要注意,每种语言提供的正则表达式有不同的flavor。
每种不同语言有不同的方式来实现正则表达式:集成的(integrated)、过程式的(procedural)和面向对象式的(object-oriented)。
集成的代表是Perl。
优点是简化了程序员的工作,因为它隐藏了一些机制,例如准备正则表达式,建立匹配,应用正则表达式,然后得到结果。
隐藏这些细节也有不利之处,在一些情况下,导致效率低下,处理不灵活。
Vim中的列编辑
http://bbs.chinaunix.net/viewthread.php?tid=157540
- 定位光标
- CTRL+v #进入Visual Mode。(Windows下用CTRL+Q)
- j #选择要在哪些行加入?
- I #一定是大写!
- 输入要插入的文本
- ESC
Watir是个不错的东东
想写一个程序,模拟IE的各种动作。曾经用过VBA实现过类似的功能,访问一个网页,模拟点击“印刷”,把网页的内容保存为Pdf文件。当然,使用了PDFCreator。
但是总觉得VBA太丑了,不想用。然后想到了Python,因为最近开始学习Python的缘故。但是Python3.0对pywin32什么的没什么支持。然后想到了Perl,上网搜索了一下,用了use Win32::OLE。
在公司的机器(Windows XP SP3, activeperl5.8.9)跑的是好好的,就差回去找找匹配中文正则表达式的问题。
但是在家里的机器,始终出问题。
试了几次,发现问题出在
$IE->{visible} = 1;
这个语句上。在家里的机器(Vista Ultimate SP1,activeperl5.8.9)上,会打开两个IE窗口,一个是指定的URL,一个是空白的IE,在试图打开什么网页,但是始终打不开的样子。然后导致后面的语句执行出问题。
如果注释掉这个语句,就只打开制定的网页,但是似乎还报别的错误(记不清楚了)。
补:终于找到原因了。在家里面下面的Watir的代码也不能运行,错误和上面的一样。查了一下,找到原因了。
Try one of these:
- Change your ruby permissions to “run as administrator”; or
- Disable User Access Control; or
- Turn off Internet Explorer Protected Mode
我就“以管理员身份运行”cmd.exe,然后在运行script。
早上想到Ruby似乎也有类似Perl 的Win32::OLE 的库,搜索了一下,发现了更好的Watir。似乎更多的是作为Web测试的,比如,这篇,这篇。哎,刚刚大概看了一下,真是一个好东东啊,哈哈:)。这里还有很多例子。
文档和示例都不错,以后可以Web的测试就能自动化一点了?
下面是一段小代码,自动打开IE访问google,用高级搜索设置搜索关键字,每页显式20条结果:
require 'watir'
ie = Watir::IE.new
ie.goto(‘http://www.google.com/’)
#click the ‘Advanced Search’ link
ie.link(:text, ‘Advanced Search’).click
#set search value, textbox ‘all these words:’
ie.text_field(:name, ‘as_q’).set ‘ruby’
#set ‘Results per page’ to 20
ie.select_list(:name, ‘num’).select ’20 results’
#submit the form
ie.form(:name, ‘f’).submit
要注意的是select列表设值的时候,select后面的值是option的text值。
正则表达式之lookaround
正则表达式中的lookaround,类似形如\b、^、或者$这样的metacharacters,它们不匹配文本,而是匹配文本中的位置。
lookaround又分为lookahead和lookbehind。
(>=Jeffrey)匹配下面的标记的位置:
by
Jeffrey Friedl
书中有个例子,把Jeffs替换为Jeff’s。
| 方法 | 解释 |
| s/\bJeffs\b/Jeff’s/g | 最直接的方法 |
| s/\b(Jeff)(s)\b/$1′$2/g | 杀鸡用牛刀 |
| s/\bJeff(?=s\b)/Jeff’/g | 按照作者的意思,只是为了说明lookahead,实际用处不大 |
| s/(?<=\bJeff)(?=s\b)/’/g | 这个表达式没有consume任何文本,它找到这样的一个位置——前面是一个单词起始,紧接着Jeff,后面是字符s,紧接着单词的起始。然后把这个位置替换为引号’ |
| s/(?=s\b)(?<=\bJeff)/’/g | 和上面的类似,只不过两个字表达式的顺序颠倒了一下,因为他们并不consume文本,所以顺序是无关的。 |
接着是一个实际的例子,把数字转化为特定的格式:#,###,###。
思路就是:
找到这样的一个位置——它的左边有数字,右边是n个3位一组的数字(n>=1)。
$pop =~ s/(?<=\d)(?=(\d\d\d)+$)/,/g;
如果不加最后的$,会解释为:左边有一个数字,右边至少有3位数字。结果就如下:
1,2,3,4,5,678
当然,如果分组不对,同样有问题。
lookaround还有相应的“否定”形式:(?<!……)和(?!......)
TODO
undef $/;
正则表达式之匹配和修改
示例均为Perl代码
测试匹配的用法:
$variable =~ m/regex/
每个用括号包围着的,匹配成功的部分,会被分别保存到变量$1, $2, $3…中。
如果不想分组,就用(?:⋯)
修改匹配的用法:
$var =~ s/regex/replacement/
书上的一个例子
$price =~ s/(\.\d\d[1-9]?)\d*/$1/
这会把12.25618替换为12.256。但是如果写成
$price =~ s/\.(\d\d[1-9]?)\d*/$1/
则结果是12256(这不是废话吗,呃,我开始就是这么写错的。。。)
在命令行运行Perl程序
详细的可以参考perlrun
书上给出的例子如下
% perl -p -i -e 's/sysread/read/g' file
- -e: 在命令行执行一个单行的程序。一旦指定的这个Switch,perl会在参数列表中寻找一个文件名。
- -p: 对参数列表中的文件每行进行操作。
- -i: 对参数列表的文件进行in-place的编辑。
书上的例子似乎有问题,至少在Windows下要指定备份文件的名(或者仅仅是后缀?)
Switch可以连着写。-p -i.bak等价于 -pi.bak。
例如,删除文件中的空白行:
类似于sed,会在终端回显结果
perl -p -e "s/^\s*$//g" bl.txt
in-place的编辑
perl -pi.bak -e "s/^\s*$//g" bl.txt
原先的文件名加了后缀.bak
adsl的自动拨号
如果需要快速的切换IP,而不手动的关闭连接,重新连接,可以用下面的方法:
@rasdial 宽带连接 /DISCONNECT
@rasdial 宽带连接 account pwd
保存为bat文件执行即可。
其中的“宽带连接”是自己的宽带连接名字,我这里是就是下图红色的部分。

开始正则表达式
就用JeffreyE.F.Friedl的Mastering Regular Expressions开始系统的学学正则表达式吧。
第一章用egrep作为工具介绍了正则表达式的概念。
主要的概念就是MetaCharacter,characterClass。详细介绍了几种MetaCharacter。例如通配符(?+*)。
另外需要注意的就是字符是否是MetaCharacter,取决于它所使用的地方。例如dot(.)就是一个MetaCharacter,匹配任意一个字符(多数情况下除了换行符)。但是如果在characterClass中,他就是一个普通的字符,仅仅代表字符.。
另外纠正一个自己长久以来的错误观念。如果要查找匹配字符串abc的行,我原以为必须:
grep -E '^.*abc.*$' filename
原来只需要:grep -E 'abc' filename。
鄙视自己一下,真是白痴