尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

正则指引——Ruby

正则指引——Ruby Ruby1、预备知识2、正则功能详解2.1、列表2.2、字符组2.3、Unicode属性2.4、字符组简记法2.5、单词边界2.6、行起始/结束位置2.7、环视2.8、匹配模式2.9、捕获分组的引用3、正则API简介3.1、Regexp3.2、Regexp.match(text)3.3、Regexp.quote(text)和Regexp.escape(text)3.4、String.index(Regexp)3.5、String.scan(Regexp)3.6、String.slice(Regexp)3.7、String.split(Regexp)3.8、String.sub(Regexp,Str)3.9、String.gsub(Regexp,String)4、常用操作示例4.1、验证4.2、提取4.3、替换4.4、切分5、Ruby 1.9的新变化Ruby内建了对正则表达式的支持提供了相对完备的正则表达式功能。但Ruby 1.8的正则表达式也存在不少限制最突出的是不支持逆序环视。Ruby 1.9采用全新的Oniguruma鬼车正则引擎补充了包括逆序环视在内的许多功能最重要的是全面支持Unicode但是也造成了一些奇怪的现象下面你会看到​。不过考虑到目前Ruby 1.9并没有完全取代Ruby 1.8所以我们会讲解Ruby 1.8和Ruby 1.9的差异末尾详细说明了Ruby 1.9中正则表达式的功能变化。​1、预备知识Ruby的正则表达式对象是Regexp。​最常见的生成方法类似/regex/即在首尾两个斜线/之间用正则文字给出正则表达式比如/\d/。为继续讲解方便先介绍进行正则表达式匹配的方法Ruby中的正则匹配非常简单直接使用操作符连接正则表达式与字符串即可。如果可以匹配则返回第一次匹配发生的起始位置否则返回nil。请注意正则表达式和字符串的左右顺序并不影响结果。Ruby中有两种字符串双引号字符串和单引号字符串。两者的主要区别在于双引号字符串需要进行字符串转义处理而单引号字符串不需要进行字符串转义处理。​所以如果要作为正则表达式的字符串最好使用单引号字符串这样就可以直接使用\d而不用写成\d作为普通文本的字符串则最好使用双引号字符串。2、正则功能详解2.1、列表下表列出了Ruby的正则功能。2.2、字符组Ruby中的正则引擎可以正确识别ASCII编码但无法正确识别GBK编码因此无法避免GBK编码的“错误匹配”问题​。解决的办法是使用Unicode编码环境指定程序使用Unicode编码的办法是在源文件的顶部写上这样一行Ruby 1.9之后版本的正则表达式才提供了相对完备的Unicode支持如果你使用的是Ruby 1.8正则表达式中又有非ASCII字符则最好在表达式末尾写上u显式指定Unicode模式Ruby1.9中正则表达式默认使用Unicode编码所以不用显式指定​。解决的办法是使用Unicode编码环境指定程序使用Unicode编码的办法是在源文件的顶部写上这样一行Ruby 1.9之后版本的正则表达式才提供了相对完备的Unicode支持如果你使用的是Ruby 1.8正则表达式中又有非ASCII字符则最好在表达式末尾写上u显式指定Unicode模式Ruby1.9中正则表达式默认使用Unicode编码所以不用显式指定​。另外要注意的是在Ruby 1.8中即便指定程序使用Unicode编码匹配位置仍然是按照字节计算的Ruby1.9中则是按照字符计算的。在Ruby中可以用字符组[\u{4E00}-\u{9FFF}]匹配任意中文字符需要注意的是这种写法只有Ruby 1.9以上版本才支持并且使用时必须显式指定Unicode模式。Ruby支持POSIX字符组。在Ruby 1.8中POSIX字符组只能匹配ASCII字符指定Unicode模式也不行​而且不可使用[:ascii:]和[:word:]而在Ruby 1.9中POSIX字符组可以匹配Unicode字符同时支持[:ascii:]和[:word:]。2.3、Unicode属性Ruby 1.9支持使用Unicode Script只是使用UnicodeScript时必须显式指定u模式否则会报错。所以如果要匹配中文字符则可以使用\p{Han}。2.4、字符组简记法Ruby的字符组简记法都采用ASCII编码匹配规则。如果指 定了Unicode模式则会换用Unicode匹配规则。​2.5、单词边界在Ruby 1.8中如果没有指定Unicode模式\w采用ASCII匹配规则单词边界\b也采用这种匹配规则​。如果指定了Unicode模式则\b也采用Unicode匹配规则。Ruby 1.9中的\w虽然采用ASCII匹配规则但\b的表现与Ruby 1.8中指定Unicode方式一样这一点值得特别注意。2.6、行起始/结束位置Ruby默认采用多行模式所以^不但可以匹配整个字符串的开始位置还可以匹配字符串内部的文本行的起始位置而\A无论在什么情况下都匹配整个字符串的起始位置。同样因为默认采用多行模式所以$可以匹配文本内部行的结束位置\Z、\z匹配的是整个字符串的结束位置如果结束位置有换行符则\Z匹配这个换行符之前的位置​。重复一次Ruby的m模式不等于其他语言中的多行模式而是等于通常的单行模式它只影响点号.的匹配。2.7、环视在Ruby中的肯定顺序环视(?…)和否定顺序环视(?!…)内可以使用任意形式的子表达式。对逆序环视的支持则不那么好Ruby 1.8完全不支持逆序环视Ruby 1.9支持逆序环视但是逆序环视中的表达式匹配的文本长度必须是固定的完全不能使用、*、?之类的量词如果使用了多选结构只能出现竖线比如regex1|regex2而不能出现括号比如(regex1|regex2)。2.8、匹配模式下表列出了Ruby的正则表达式支持的匹配模式。匹配模式可以在表达式中以(?modifier)指定或者在结束分隔符之后写上模式对应的修饰符也可以在生成Regexp对象时通过预定义常量作为参数指定。Ruby支持用(?-modifier)停用某个模式。2.9、捕获分组的引用在Ruby中如果要在正则表达式内部引用捕获分组则应当使用\num记法其中num为对应捕获分组的编号。如果要在替换时引用捕获分组则应当使用$num记法。如果使用了命名分组在表达式内部则应当使用\kname来引用。在replacement字符串中同样使用\kname来引用。3、正则API简介3.1、RegexpRuby中的正则表达式对象是Regexp一般使用/regex/来生成。如果习惯更为“正统”的模式也可以用Regexp.new()来生成参数是表达式对应的字符串。之前讲过Ruby中有两种字符串其中双引号字符串需要进行字符串转义处理而单引号字符串不需要进行字符串转义处理。所以作为正则表达式的字符串最好使用单引号字符串。如果要指定匹配模式可以添加第2个参数虽然实际参数是一个整数但是Ruby其实有3个预定义常量即Regexp::IGNORECASE、Regexp::EXTENDED、Regexp::MULTILINE​。Ruby中也可以混用多个模式用|连接预定义常量。Regexp.new()的第1个参数也可以不用字符串而是用类似/regex/的形式。如果使用这种形式那么有三种形式指定匹配模式下面各举一个例子其结果相同。另外还有Regexp.compile()方法它与Regexp.new()完全一致。值得注意的还有Regexp对象的编码。在Ruby 1.9中Regexp对象自身是具有编码的在默认情况下如果正则表达式中出现的都是ASCII字符且程序采用了兼容ASCII的编码则正则表达式默认采用ASCII编码否则采用与程序相同的编码。调用Regexp.encoding?可以得到编码信息。要正确使用具有编码属性的正则表达式只有两种情况Regexp与字符串使用相同的编码或者Regexp使用ASCII编码而字符串使用兼容ASCII的编码否则会报错。如果不确定使用的Regexp对象的编码是否兼容ASCII可以使用fix_encoding?来判断。不兼容的情况下会返回true如果一个Regexp对象的编码兼容ASCII它可以尝试匹配任何使用了ASCII编码的字符串而不会报错。3.2、Regexp.match(text)之前看到的匹配都是依靠操作符进行的许多读者可能不熟悉所以这里先详细介绍它。用来执行正则表达式和字符串的匹配返回匹配成功的起始位置如果匹配不成功则返回nil。因此可以用这个办法来进行数据验证验证数据时别忘了在正则表达式两端加上\A和\z​。如果匹配成功会生成一个MatchData对象通过Regexp.last_match可以获得它其中包含了匹配的详细信息。也可以不用显式通过MatchData对象来获得这些信息Ruby提供了若干特殊变量使用它们同样也可以达到目的。下表列出了常用的特殊变量。Regexp.match(text)方法与差不多主要的区别在于它返回的并不是匹配的起始位置而是一个MatchData对象如果匹配不成功则返回nil。所以上面的代码可以这样获得MatchData。Regex.match(string)与的另一点不同是它可以通过可选参数设定偏移值设定尝试匹配的开始位置这样就可以在字符串中逐次找到匹配。在下面的例子中每次匹配成功之后记录下匹配的结束位置作为下次开始尝试的起点就实现了逐次匹配。3.3、Regexp.quote(text)和Regexp.escape(text)这两个方法是等价的它们消除text中可能出现的任何元字符的特殊含义。3.4、String.index(Regexp)这个方法默认等价于操作如果匹配成功则返回匹配开始的位置否则返回nil。如果不指定第2个参数它等价于Regex.match()或者。它也可以指定第2个参数设定开始尝试匹配的偏移值。此时可以遍历文本获得逐次匹配的结果。3.5、String.scan(Regexp)操作符和Regexp.match(string)方法只能进行单次匹配如果希望一次性找到某个正则表达式在字符串中的所有匹配就应该使用String.scan(Regexp)它返回各次匹配所对应文本组成的数组。如果正则表达式包含捕获分组则返回数组的元素本身也是数组。请注意这时候并不会返回整个表达式匹配的文本也就是编号为0的捕获分组捕获的文本​如果需要得到整个表达式匹配的文本可以给整个表达式首尾加上括号。也可以用String.scan()进行更复杂的字符串处理方法是在括号后面加上{|match,…|block}其中match,…表示各捕获分组匹配的文本而block则是对这些文本进行处理的程序代码比如下面这样。3.6、String.slice(Regexp)这个方法返回Regexp在String中能匹配的文本。也可以设定第2个参数指定对应捕获分组的编号返回对应的捕获分组匹配的文本。如果正则表达式可以在文本中找到多次匹配那么String.slice(Regexp)只返回第一次匹配的结果。如果将String.slice(Regexp)写成String.slice!(Regexp)则String自身也受到影响在返回匹配文本的同时还会从原字符串中删除匹配的结果。3.7、String.split(Regexp)这个方法用正则表达式来切分字符串它返回一个数组其中的元素是切分之后的文本。如果切分的末尾留下空字符串在默认情况下会忽略它而字符串开头的空字符串不会忽略。这个方法也可以指定第2个参数它是一个整数假设为n按照n取值的不同有几种情况以下分别举例说明。3.8、String.sub(Regexp,Str)String.sub(Regexp,Str)用来执行正则表达式替换Regexp是匹配想要替换文本的表达式而Str是希望更新的字符串调用之后只会对Regexp第一次能匹配的文本进行替换。如果第1个参数不是Regexp而是普通字符串则进行普通字符串替换。也可以在String参数中用\num引用Regexp对应捕获分组所匹配的文本。如果需要进行更灵活的替换则可以使用String.sub(Regexp){| match | block}方式其中的match是Regexp所匹配的文本即一个String对象我们可以写一段代码作为block进行更复杂的处理比如将单词转换为大写。如果需要在block中获得其他匹配的内容可以使用之前讲过的$1、$2、$等特殊变量。String.sub(Regexp,String)方法并不会修改原来的字符串它会先复制原字符串对其进行修改之后返回。如果String对象包含的内容很多复制可能比较消耗资源因此Ruby也提供了直接对原字符串生效的方法String.sub!(Regexp,String)。如果表达式在String中能多次匹配String.sub()只会对第一次匹配进行替换如果需要对所有匹配都进行替换则需要使用String.gsub(Regexp,String)。下面来看这个方法。3.9、String.gsub(Regexp,String)这个方法在使用上完全等价于String.sub(Regexp,String)唯一不同的是它会对每次匹配都进行替换。相应的String.gsub!(Regexp,String)也会修改原来的字符串。4、常用操作示例4.1、验证4.2、提取逐步提取:一次性提取:4.3、替换简单替换在replacement中使用整个表达式匹配的文本4.4、切分5、Ruby 1.9的新变化Ruby 1.8的正则表达式还不够完善许多常用的特性不能支持而Ruby 1.9采用了全新的Oniguruma鬼车引擎对正则表达式的支持则强大了很多。下面列出Ruby1.9中正则表达式的主要变化更详细的信息请参考http://oniguruma.rubyforge.org/oniguruma/。新增了合并多个表达式的方法这个方法返回的仍然是一个Regexp它“合拢”了参数中所有的Regexp任何一段文本只要能被其中某个表达式匹配就能被Regexp匹配。关于这个方法有一个常见的误区在数据验证时想当然地将各条件对应的表达式用Regexp.union()“归并”​但如果这些条件必须同时满足就很可能出错。比如需要验证的字符串有这样两条要求第一全部由数字或字母组成第二长度在612之间。如果想当然地用Regexp.union()就会出错。这是因为Regexp.union()“归并”的表达式并不要求“同时生效”​只要其中有一条能匹配则匹配成功。此处字符串0123可以由表达式^[a-zA-Z0-9]*$匹配所以返回结果0。新增了逆序环视新增了忽略优先量词新增了字符组中的集合运算操作符新增了指定Unicode码值的表示法如果指定了多字节编码十进制和十六进制的数字序列可以用来表示单个多字节字符因此可以在字符组中指定多字节字符的范围。如果不知道自己使用的Ruby的版本或者不确认其使用了Oniguruma引擎则可以使用下面的函数来判断。如果希望无缝兼容Ruby 1.8和1.9不妨自己包装一个方法根据要处理的字符串来生成对应编码的Regexp。
返回列表