正则表达式两篇:

本文是对Perl正则的一点扩展,主要内容是使用qr//创建正则对象,以及一些其它的技巧。

qr//创建正则对象

因为可以在正则模式中使用变量替换,所以我们可以将正则中的一部分表达式事先保存在变量中。例如:

$str="hello worlds gaoxiaofang";
$pattern="w.*d";
$str =~ /$pattern/;
print "$&\n";

但是,这样缺陷很大,在保存正则表达式的变量中存放的特殊字符要防止有特殊意义。例如,当使用m//的方式做匹配分隔符时,不能在变量中保存/,除非转义。

perl提供了qr/pattern/的功能,它把pattern部分构建成一个正则表达式对象,然后就可以:

  • 在正则表达式中直接引用这个对象
  • 可以将这个对象保存到变量中,通过引用变量的方式来引用这个已保存好的正则对象
  • 将引用变量插入到其它模式中构建更复杂的正则表达式

其中:

  • qr//的定界符斜线可以替换为其它符号,例如对称的括号类qr() qr{} qr<> qr[],一致的符号类qr%% qr## qr!! qr$$ qr"" qr''等。
  • 但是使用单引号作为定界符时比较特殊(即qr'pattern'),它会将pattern部分使用单引号的方式去解析,例如变量$var无法替换,而是表示4个字符。但是正则表达式的元字符仍然起作用,例如$仍然表示行尾。
$str="hello worlds gaoxiaofang";

# 直接作为正则表达式
$str =~ qr/w.*d/;
print "$&\n"; # 保存为变量,再作为正则表达式
$pattern=qr/w.*d/;
$str =~ $pattern; # (1)
$str =~ /$pattern/; # (2)
print "$&\n"; # 保存为变量,作为正则表达式的一部分
$pattern=qr/w.*d/;
$str =~ /hel.* $pattern/;
print "$&\n";

还允许为这个正则对象设置修饰符,比如忽略大小写的匹配修饰符为i,这样在真正匹配的时候,就只有这一部分正则对象会忽略大小写,其余部分仍然区分大小写。

$str="HELLO wORLDs gaoxiaofang";

$pattern=qr/w.*d/i;         # 忽略大小写

$str =~ /HEL.* $pattern/;   # 匹配成功,$pattern部分忽略大小写
$str =~ /hel.* $pattern/; # 匹配失败
$str =~ /hel.* $pattern/i; # 匹配成功,所有都忽略大小写

qr如何构建正则对象

输出qr构建的正则引用,看看是怎样的结构:

$patt1=qr/w.*d/;
print "$patt1\n"; $patt2=qr/w.*d/i; # 加上修饰符i
print "$patt2\n"; $patt3=qr/w.*d/img; # 加上修饰符img
print "$patt3\n";

上面的print将输出如下结果:

(?^:w.*d)
(?^i:w.*d)
(?^mi:w.*d)

qr的作用实际上就是在我们给定的正则pattern基础上加上(?^:)并带上一些修饰符,得到的结果总是(?^FLAGS:pattern)

但是上面patt3的修饰符g不见了。先可以看看(?^:)的作用:非捕获分组,并重置修饰符。重置为哪些修饰符?对于(?^FLAGS:)来说,只有这些修饰符"alupimsx"是可用的,即(?^alupimsx:)

  • 如果给定的修饰符不在这些修饰符内,则不被识别,有时候会报错
  • 如果给定的修饰符属于这几个修饰符,那么没有给定的修饰符部分将采用默认值(不同版本可能默认是否开启的值不同)

所以上面的g会被丢弃,甚至在进一步操作这个正则引用时,会报错。

既然qr给pattern部分加上了(?^:),那么当它们插入到其它正则中的时候,就能保证这一段是独立的,不受全局修饰符影响的模式。

$patt1=qr/w.*d/im;
$patt2=qr/hel.*d $patt1/i;
print "$patt2\n"; # 输出:(?^i:hel.*d (?^mi:w.*d))

正则引用作为标量的用法

既然qr//创建的正则对象引用是一个标量,那么标量可以出现的地方,正则引用就可以出现。例如,放进hash结构,数组结构。

例如,放进数组中形成一个正则表达式列表,然后给定一个待匹配目标,依次用列表中的这些模式去匹配。

use v5.10.1;
my @patterns = (
qr/(?:Willie )?Gilligan/,
qr/Mary Ann/,
qr/Ginger/,
qr/(?:The )?Professor/,
qr/Skipper/,
qr/Mrs?. Howell/,
); my $name = 'Ginger';
foreach my $pattern ( @patterns ) {
if( $name =~ /$pattern/ ) {
say "Match!";
print "$pattern";
last;
}
}

还可以将这些正则引用放进hash中,为每个pattern都使用key来标识一下,例如pattern1是用来匹配什么的:

use v5.10.1;
my %patterns = (
Gilligan => qr/(?:Willie )?Gilligan/,
'Mary Ann' => qr/Mary Ann/,
Ginger => qr/Ginger/,
Professor => qr/(?:The )?Professor/,
Skipper => qr/Skipper/,
'A Howell' => qr/Mrs?. Howell/,
);
my $name = 'Ginger';
my( $match ) = grep { $name =~ $patterns{$_} } keys %patterns;
say "Matched $match" if $match;

上面将grep语句的结果赋值给了一个标量,所以如果有多个Pattern能匹配$name,多次执行,$match的值将可能会不一样。

构建复杂的正则表达式

有了qr,就可以将正则表达式细化成一小片一小片,然后组合起来。例如:

my $howells = qr/Thurston|Mrs/;
my $tagalongs = qr/Ginger|Mary Ann/;
my $passengers = qr/$howells|$tagalongs/;
my $crew = qr/Gilligan|Skipper/;
my $everyone = qr/$crew|$passengers/;

就像RFC 1738中对URL各个部分的解剖,如果转换成Perl正则,大概是这样的(了解即可):

# 可复用的基本符号类
my $alpha = qr/[a-z]/;
my $digit = qr/\d/;
my $alphadigit = qr/(?i:$alpha|$digit)/;
my $safe = qr/[\$_.+-]/;
my $extra = qr/[!*'\(\),]/;
my $national = qr/[{}|\\^~\[\]`]/;
my $reserved = qr|[;/?:@&=]|;
my $hex = qr/(?i:$digit|[A-F])/;
my $escape = qr/%$hex$hex/;
my $unreserved = qr/$alpha|$digit|$safe|$extra/;
my $uchar = qr/$unreserved|$escape/;
my $xchar = qr/$unreserved|$reserved|$escape/;
my $ucharplus = qr/(?:$uchar|[;?&=])*/;
my $digits = qr/(?:$digit){1,}/; # 可复用的URL组成元素
my $hsegment = $ucharplus;
my $hpath = qr|$hsegment(?:/$hsegment)*|;
my $search = $ucharplus;
my $scheme = qr|(?i:https?://)|;
my $port = qr/$digits/;
my $password = $ucharplus;
my $user = $ucharplus; my $toplevel = qr/$alpha|$alpha(?:$alphadigit|-)*$alphadigit/;
my $domainlabel = qr/$alphadigit|$alphadigit(?:$alphadigit|-)*$alphadigit/x;
my $hostname = qr/(?:$domainlabel\.)*$toplevel/;
my $hostnumber = qr/$digits\.$digits\.$digits\.$digits/;
my $host = qr/$hostname|$hostnumber/;
my $hostport = qr/$host(?::$port)?/;
my $login = qr/(?:$user(?::$password)\@)?/; my $urlpath = qr/(?:(?:$xchar)*)/;

然后我们就可以用上面看上去无比复杂的正则表达式去匹配一个路径是否是合格的http url:

use v5.10.1;
my $httpurl = qr|$scheme$hostport(?:/$hpath(?:\?$search)?)?|;
while( <> ) {
say if /$httpurl/;
}

正则表达式模块

上面构建的正则太复杂了,很多常用的正则表达式别人已经造好了轮子,我们直接拿来用就行了。例如,Regexp::Common模块,提供了很多种已经构建好的正则表达式。

首先安装这个模块:

sudo cpan -i Regexp::Common

以下是CPAN上提供的Regexp::Common已造好的轮子,可参考:https://metacpan.org/release/Regexp-Common

Regexp::Common - Provide commonly requested regular expressions
Regexp::Common::CC - provide patterns for credit card numbers.
Regexp::Common::SEN - provide regexes for Social-Economical Numbers.
Regexp::Common::URI - provide patterns for URIs.
Regexp::Common::URI::RFC1035 - Definitions from RFC1035;
Regexp::Common::URI::RFC1738 - Definitions from RFC1738;
Regexp::Common::URI::RFC1808 - Definitions from RFC1808;
Regexp::Common::URI::RFC2384 - Definitions from RFC2384;
Regexp::Common::URI::RFC2396 - Definitions from RFC2396;
Regexp::Common::URI::RFC2806 - Definitions from RFC2806;
Regexp::Common::URI::fax - Returns a pattern for fax URIs.
Regexp::Common::URI::file - Returns a pattern for file URIs.
Regexp::Common::URI::ftp - Returns a pattern for FTP URIs.
Regexp::Common::URI::gopher - Returns a pattern for gopher URIs.
Regexp::Common::URI::http - Returns a pattern for HTTP URIs.
Regexp::Common::URI::news - Returns a pattern for file URIs.
Regexp::Common::URI::pop - Returns a pattern for POP URIs.
Regexp::Common::URI::prospero - Returns a pattern for prospero URIs.
Regexp::Common::URI::tel - Returns a pattern for telephone URIs.
Regexp::Common::URI::telnet - Returns a pattern for telnet URIs.
Regexp::Common::URI::tv - Returns a pattern for tv URIs.
Regexp::Common::URI::wais - Returns a pattern for WAIS URIs.
Regexp::Common::_support - Support functions for Regexp::Common.
Regexp::Common::balanced - provide regexes for strings with balanced parenthesized delimiters or arbitrary delimiters.
Regexp::Common::comment - provide regexes for comments.
Regexp::Common::delimited - provides a regex for delimited strings
Regexp::Common::lingua - provide regexes for language related stuff.
Regexp::Common::list - provide regexes for lists
Regexp::Common::net - provide regexes for IPv4, IPv6, and MAC addresses.
Regexp::Common::number - provide regexes for numbers
Regexp::Common::profanity - provide regexes for profanity
Regexp::Common::whitespace - provides a regex for leading or trailing whitescape
Regexp::Common::zip - provide regexes for postal codes.

这些正则表达式是通过hash进行嵌套的,hash的名称为%RE。例如模块Regexp::Common::URI::http,它提供的是HTTP URI的正则表达式,它嵌套了两层,第一层的key为URI,这个key对应的值是第二层hash,第二层hash的key为HTTP,于是可以通过$RE{URI}{HTTP}的方式获取这个正则。

例如,匹配一个http url是否合理:

use Regexp::Common qw(URI);
while( <> ) {
print if /$RE{URI}{HTTP}/;
}

在学习shell脚本的时候,经常有人写匹配IPV4的正则表达式,现在我们可用直接从Regexp::Common::net中获取:

use Regexp::Common qw(net);
$ipv4=$RE{net}{IPv4};
print $ipv4;

以下是结果:

(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))

只是需要注意的是,在真正匹配的时候应该将得到的引用锚定起来,否则对318.99.183.11进行匹配的时候也会返回true,因为18.99.183.11是符合匹配结果的。所以,对前后都加上锚定,例如:

$ipv4 =~ /^$RE{net}{IPv4}$/;

将上面的ipv4正则改造一下(去掉非捕获分组的功能),让它适用于shell工具中普遍支持的扩展正则:

(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}

默认情况下,Regexp::Common的各个模块是没有开启捕获功能的。如果要使用$1$N这种引用,需要使用{-keep}选项,至于每个分组捕获的是什么内容,需要参考帮助文档的说明。

例如:

use Regexp::Common qw(number);
while( <> ) {
say $1 if /$RE{num}{int}{ -base => 16 }{-keep}/;
}

Perl正则表达式引用的更多相关文章

  1. Perl正则表达式例子

    Perl正则表达式 一.介绍 正则表达式各语言都有自己的规范,但是基本都差不多,都是由元字符的组合来进行匹配:由于Nmap内嵌的服务与版本探测是使用的Perl正则规范,因此此篇博客记录一下Perl正则 ...

  2. Perl 正则表达式语法

    1. 概要 Perl正则表达式是Boost.regex 默认行为,也可以将perl传入basic_regex 构造. boost::regex  e1(my_expression); boost::r ...

  3. Perl正则表达式

    perl正则表达式就是通过一串特别设计的字符串,可以按照我们的需求匹配.替换.转化目标字符串.本文主要是对一些常用的正则表达以及语法的总结以及举例,供广大喜爱Perl的同学交流学习. 操作符: =~ ...

  4. Perl 正则表达式

    匹配:m/<regexp>;/ (还可以简写为 /<regexp>;/ ,略去 m)替换:s/<pattern>;/<replacement>;/转化: ...

  5. perl正则表达式第三周笔记

    正则引擎的分类 正则引擎的分类 正则引擎的分类主要分两种: DFA:egrep.awk.lex.flex NFA:.NET.PHP.Perl.Ruby.Python.GNU Emacs.ed.sec. ...

  6. perl 正则表达式之匹配

    一.用m//进行匹配 上篇用双斜线的写法表示模式,事实上是m//的简写,所谓简写,就是当用双斜线作为定界符的时候,可有省略开头的m. 不使用简写的时候,可以使用任何定界符表示模式,m().m<& ...

  7. posix,perl正则表达式区别

    1.正则表达式(Regular Expression,缩写为regexp,regex或regxp),又称正规表达式.正规表示式或常规表达式或正规化表示法或正规表示法,是指一个用来描述或者匹配一系列符合 ...

  8. Perl正则表达式超详细教程

    前言 想必学习perl的人,对基础正则表达式都已经熟悉,所以学习perl正则会很轻松.这里我不打算解释基础正则的内容,而是直接介绍基础正则中不具备的但perl支持的功能.关于基础正则表达式的内容,可参 ...

  9. <摘录>perl正则表达式中的元字符、转义字符、量词及匹配方式

    Linux平台上被广泛使用的正则表达式库PCRE - Perl-compatible regular expressions,从其名字即可知道,PCRE提供的是一套与Perl中相兼容的正则表达式. 元 ...

随机推荐

  1. java中的抽象类的作用

    抽象函数的作用有很多,我就举一个例子,(mars老师的例子): 打印机: 父类---Printer 子类---HBPrinter 子类----CanonPrinter 测试类----Test //定义 ...

  2. idea实现自动sql-generator的使用

    1.实现步骤:  9.1 加载插件 <!-- mybatis逆向工程jar包 --> <dependency> <groupId>org.mybatis.gener ...

  3. js-图片轮播(极简)

    <!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" ...

  4. 使用Spring+MySql实现读写分离(三)主从复制

    mysql  主(master)    从(slave)    复制的原理: 1. master将数据改变记录到二进制日志(binary log)中,也即是配置文件log-bin指定的文件(这些记录叫 ...

  5. SpringBoot条件注解@Conditional

    最近项目中使用到了关于@Conditional注解的一些特性,故写此文记录一下 @Conditional是啥呀? @Conditional注解是个什么东西呢,它可以根据代码中设置的条件装载不同的bea ...

  6. 第36节:Java当中的线程

    Java当中的线程 Java当中的线程,进程和线程的关系?进程就是线程吗?不是的.线程的运行,和方法. 多线程和多进程,多进程为在操作系统中同时进行多个应用程序,如你的电脑可以同时听音乐,同时上网,手 ...

  7. ruby-super用法

    ruby语法-super用法 本文主要介绍ruby中super方法的使用.super方法参数传递.method执行顺序. 下面主要通过实例来说明super方法的使用: 示例1: #!/usr/bin/ ...

  8. [Swift-2019力扣杯春季初赛]1. 易混淆数

    给定一个数字 N,当它满足以下条件的时候返回 true: 把原数字旋转180°以后得到新的数字. 如 0, 1, 6, 8, 9 旋转 180° 以后,得到了新的数字 0, 1, 9, 8, 6 . ...

  9. TX-LCN分布式事务Demo实战

    1. TX-LCN分布式事务Demo实战 1.1. 原理介绍 1.1.1. 事务控制原理 TX-LCN由两大模块组成, TxClient.TxManager,TxClient作为模块的依赖框架,提供T ...

  10. Java NIO工作机制简介

    前言 本博客只简单介绍NIO的原理实现和基本工作流程 I/O和NIO的本质区别 NIO将填充和提取缓冲区的I/O操作转移到了操作系统 I/O 以流的方式处理数据,而 NIO 以缓冲区的方式处理数据:I ...