正则指引之字符组

前端之家收集整理的这篇文章主要介绍了正则指引之字符组前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

普通字符组

字符组就是一组字符,在正则表达式中,它表示“在同一个位置可能出现的各种字符”,其写法是在一对方括号 [ ] 之间列出所有可能出现的字符,简单的字符组比如 [ab],[314],[#.?] 。在解决一些常见问题时,使用字符组可以大大简化操作。下面举“匹配数字字符”的例子来说明(java):

......
charStr.matches("[0123456789]");

charStr是需要判断的字符串,而 [0123456789] 则是以字符串形式给出的正则表达式,它是一个字符组,表示“这里可以是0,1,2,3,4,5,6,7,8,9中的任意一个字符。只要charStr与其中任何一个字符相同即为匹配”。

不同语言使用正则表达式的方法也不相同。如果仔细观察会发现Java,.Net,Python,PHP中的正则表达式,都要以字符串形式给出,两端都有双引号"",而ruby和javascript中的正则表达式则不必如此,只在首尾有两个斜线字符/。这也是不同语言中使用正则表达式的不同之处。

字符组中的字符排列顺序不影响字符组的功能,出现重复字符影响,所以 [0123456789] 完全等价于 [9876543210] ,[9987120934556]。不过,代码总是要容易编写,方便阅读,正则表达式也是一样,所以一般并不推荐在字符组中出现重重字符。而且,还应该让字符组中的字符排列更符合谁知习惯,比如 [0123456789]。为此,正则表达式提供了范围表示法(range),它更直观,能进一步简化字符组。

所谓“-范围表示法”,就是用 [x-y] 的形式表示x到y整个范围内的字符,省去一一列出的麻烦,这样 [0123456789]就可以表示为 [0-9]。你可能会问,“-范围表示法”的范围是如何确定的?为什么要写作 [0-9],而不写作 [9-0]?要回答这个问题,必须了解范围表示法的实质。在字符组中,- 表示的范围,一般是根据字符对应的码值(Code Point,也就是字符在对应编码表中的编码的数值)来确定的,码值小的字符在前,码值大的字符在后。在ASCII编码中(包括各种兼容ASCII的编码中),字符0的码值是48(十进制),字符9的码值是57,所以[0-9]等价于[0123456789],而[9-0]则是错误的范围,因为9的码值大于0,所以会报错。如果知道0~9的码值是48~57,a~z的码值是97~122,A~Z的码值是65~90,能不能用 [0~z]统一表示数字字符,小写字母,大写字母?答案是勉强可以,但不推荐这么做。根据惯例,字符组的范围表示法都表示一类字符(数字字符是一类,字母字符也是一类),所以虽然[0-9],[a-z]都是很好理解的。

在字符组中可以同时并列多个“-范围表示法”,字符组 [0-9a-zA-Z] 可以匹配数字,大写字母或小写字母;字符组 [0-9a-fA-F] 可以匹配数字,大,小写形式的a~f,它可以用来验证十六进制字符。

在不少语言中,还可以用转义序列 \xhex 来表示一个字符,其中 \x固定前缀,表示转义序列的开头,num是字符对应的码值,是一个两位的十六进制数值。比如字符A的码值是41(十进制则为65),所以也可以用 \x41表示。

字符组中有时会出现这种表示法,它可以表示一些难以输入或者难以显示的字符,比如 \x7F;也可以用来方便地表示某个范围,比如所有ASCII字符对应的字符组就是 [\x00-\x7F]。这种表示法很重要,依靠这种表示法可以很方便地匹配所有的中文字符(后面再讲)。

元字符与转义

在上面的例子里,字符组中的横线 - 并不能匹配横线字符,而是用来表示范围,这类字符叫做元字符Meta-character)。字符组的开方括号[,闭方括号]都算元字符。在匹配中,它们有着特殊的意义。但是,有时候并不需要表示这些特殊意义,只需要表示普通字符(比如“我就想表示横线字符 - ”),此时就必须做特殊处理

先来看字符组中的 -,如果它紧邻着字符组中的开方括号 [,那么它就是普通字符,其他情况下都是元字符而对于其他元字符,取消特殊含义的做法都是转义,也就是在正则表达式中的元字符之前加上反斜线字符 \

如果要在字符组内部使用横线 - ,最好的办法是将它排列在字符组的最开头。[-09]就是包含三个字符 -,0,9的字符组;[0-9]是包含0~9这10个字符的字符组,[-0-9]则是由“-范围表示法”0~9和横线 - 共同组成的字符组,它可以匹配11个字符。

//作为普通字符
Stringtext="-";
Patternp=Pattern.compile("[-09]");
Matcherm=p.matcher(text);
System.out.println(m.matches());
//作为元字符
Stringtext="-";
Patternp=Pattern.compile("[0-9]");
Matcherm=p.matcher(text);
System.out.println(m.matches());
//转义后作为普通字符
Stringtext="-";
Patternp=Pattern.compile("[0\\-9]");
Matcherm=p.matcher(text);
System.out.println(m.matches());

仔细观察会发现,在正文里说“在正则表达式中的元字符之前加上反斜线字符 \ ”,而在代码里写的却不是 [0\-9],而是 [0\\-9]。这并不是错误。因为在这段程序里,正则表达式是以字符串(String)的方式提供的,而字符串本身也有关于转义的规定(你或许记得,在字符串中有 \n,\t之类的转义序列)。上面说的“正则表达式”,其实是经过“字符串转义处理”之后的字符串的值,正则表达式 [0\-9]包含6个字符:[,0,\,-,9,],在字符串中表达这6个字符;但是在源代码里,必须使用7个字符:\需要转义成\\,因为处理字符串时,反斜线和之后的字符会被认为是转义序列(Escape Sequence),比如 \n,\t,都是合法的转义序列,然而 \- 不是

继续看转义,如果希望在字符组中列出闭方括号 ],比如 [012]345],就必须在它之前使用反斜线转义,写成 [012\]345],否则正则表达式将]与最近的[匹配,这个表达式就成了“字符组[012]加上4个字符345]”,它能匹配的是字符串0345]或1345]或2345],却不能匹配 ]。

//未转义的]
Stringtext="0345]";
Patternp=Pattern.compile("[012]345]");
Matcherm=p.matcher(text);
System.out.println(m.matches());
//转义的]
Stringtext="]";
Patternp=Pattern.compile("[012\\]345]");
Matcherm=p.matcher(text);
System.out.println(m.matches());

除去字符组内部的 - ,其他元字符的转义都必须在字符之前添加反斜线,[ 的转义也是如此。如果只希望匹配字符串 [012]是不行的,因为这会被识别为一个字符组,它只能匹配0,1,2这三个字符中的任意一个,而必须转义,把正则表达式写作 \[012],请注意,只有开方括号 [ 需要转义,闭方括号 ] 不需要转义

//取消其他元字符的特殊含义
Stringtext="[012]";
Patternp=Pattern.compile("\\[012]");
Matcherm=p.matcher(text);
System.out.println(m.matches());

排除型字符组

在方括号[...]中列出希望匹配的所有字符,这种字符组叫做“普通字符组”,它的确非常方便。不过,也有些问题是普通字符组不能解决的。

给定一个由两个字符构成的字符串str,要判断这两个字符是否都是数字字符,可以用[0-9][0-9]来匹配。但是,如果要求判断的是这样的字符串——第一个字符不是数字字符,第二个字符才是数字字符(比如 A8,x6)——应当如何办?数字字符的匹配很好处理,用[0-9]即可;“不是数字”则很难办——不是数字的字符太多了,全部列出几乎不可能,这时就应当使用排除型字符组。排除型字符组(Negated Character Class)非常类似普通字符组 [...],只是在开方括号 [ 之后紧跟一个脱字符 ^ ,写作 [^...],表示“在当前位置,匹配一个没有列出的字符”。所以[^0-9]就表示“0~9之外的字符”,也就是“非数字字符”。那么上面那个问题就可以用[^0-9][0-9]解决了。例:

//使用排除型字符组
Stringtext="A8";
Patternp=Pattern.compile("[^0-9][0-9]");
Matcherm=p.matcher(text);
System.out.println(m.matches());

排除型字符组看起来很简单,不过新手常常会犯一个错误,就是把“在当前位置匹配一个没有列出的字符”理解成“在当前位置不要匹配列出的字符”,两者其实不同的,后者暗示“这里不出现任何字符也可以”。排除型字符组必须匹配一个字符,这点一定要记住。例:

//排除型字符组必须匹配一个字
Stringtext="A8";
Patternp=Pattern.compile("[^0-9][0-9]");
Matcherm=p.matcher(text);
System.out.println(m.matches());//true
text="8";
Matcherm1=p.matcher(text);
System.out.println(m1.matches());//false

除了开方括号 [ 之后的 ^,排除型字符组的用法与普通字符组几乎完全相同,唯一需要改动的是:在排除型字符组中,如果需要表示横线字符 - (而不是用于“-范围表示法”),那么 - 应该紧跟在 ^ 之后; 而在普通字符组中,作为普通字符的横线 - 应该紧跟在开方括号之后

//在排除型字符组中,紧跟在^之后的-不是元字符
Stringtext="-";
Patternp=Pattern.compile("[^-09]");
Matcherm=p.matcher(text);
System.out.println(m.matches());//false
text="8";
Matcherm1=p.matcher(text);
System.out.println(m1.matches());//true

在排除型字符组中,^ 是一个元字符,但只有它紧跟在 [ 之后时才是元字符,如果想表示“这个字符组中可以出现 ^ 字符”,不要让它紧挨着 [ 即可,否则就要转义。如例:

//匹配一个012之外的字符
Stringtext="^";
Patternp=Pattern.compile("[^012]");
Matcherm=p.matcher(text);
System.out.println(m.matches());//true
//匹配4个字符之一:012^
Patternp1=Pattern.compile("[0^12]");
Matcherm1=p1.matcher(text);
System.out.println(m1.matches());//true
//^紧跟在[之后,但已经转义成普通字符,等于上面的表达式,不推荐
Patternp2=Pattern.compile("[\\^012]");
Matcherm2=p2.matcher(text);
System.out.println(m2.matches());//true

字符组简记法

用[0-9],[a-z]等字符组,可以很方便地表示数字字符和小写字母字符。对于这类常用的字符组,正则表达式提供了更简单的记法,这就是字符组简记法(shorthands)。

常见的字符组简记法有 \d\w\s。从表面上看,它们与[...]完全没联系,其实是一致的。其中 \d 等价于 [0-9],其中的d代表“数字(digit)”;\w 等价于 [0-9a-zA-Z_],其中的w代表“单词字符(word)”; \s 等价于 [ \t\r\n\v\f](第一个字符是空格)s表示“空白字符(space)”。下例说明了这几个字符组简记法的典型匹配:

Stringtext="8";
Patternp=Pattern.compile("\\d");
Matcherm=p.matcher(text);
System.out.println(m.matches());//true
text="a";
Patternp1=Pattern.compile("\\w");
Matcherm1=p1.matcher(text);
System.out.println(m1.matches());//true
text="\t";
Patternp2=Pattern.compile("\\s");
Matcherm2=p2.matcher(text);
System.out.println(m2.matches());//true

一般印像中,单词字符似乎只包含大小写字母,但是字符组简记法中的“单词字符”不只有大小写单词,还包含数字字符和下画线_,其中的下画线 _ 尤其值得注意:在进行数据验证时,有可能只容许输入“数字和字母”,有人会人偷懒用 \w 验证,而忽略了 \w 能匹配下画线,所以这种匹配不严格,[0-9a-zA-Z]才是准确的选择。

空白字符”并不难定义,它可以是空格字符,制表符\t,回车符\r,换行符\n等各种“空白”字符,只是不方便展现(因为显示和印刷出来都是空白)。不过这也提醒我们注意,匹配时看到的“空白”可能不是空格字符,因此, \s 才是准确的选择。

字符组简记法可以单独出现,也可以使用在字符组中,比如 [0-9a-zA-Z]也可以写作[\da-zA-Z],所以匹配十六进制字符的字符组可以写成 [\da-fA-F]。字符组简记法也可以用在排除型字符组中,比如 [^0-9]就可以写成[^\d],[^0-9a-zA-Z_]就可以写成 [^\w],代码如下:

Stringtext="8";
//在普通字符组内
Patternp=Pattern.compile("[\\da-zA-Z_]");
Matcherm=p.matcher(text);
System.out.println(m.matches());//true
//在排除型字符组内
Patternp1=Pattern.compile("[^\\w]");
Matcherm1=p1.matcher(text);
System.out.println(m1.matches());//false

相对于 \d,\w和\s 这三个普通字符组简记法,正则表达式也提供了对应排除型字符组的简记法:\D,\W\S ——字母完全一样,只是改为大写。这些简记法匹配的字符互补:\s能匹配的字符,\S一定不能匹配;\w能匹配的字符,\W一定不能匹配;\d能匹配的字符,\D一定不能匹配。例:

//\d和\D
Stringtext="8";
Patternp=Pattern.compile("\\d");
Patternpp=Pattern.compile("\\D");
Matcherm=p.matcher(text);
Matchermm=pp.matcher(text);
System.out.println(m.matches());//true
System.out.println(mm.matches());//false
//\w和\W
text="a";
Patternp1=Pattern.compile("\\w");
Patternpp1=Pattern.compile("\\W");
Matcherm1=p1.matcher(text);
Matchermm1=pp1.matcher(text);
System.out.println(m1.matches());//true
System.out.println(mm1.matches());//false
//\s和\S
text="\t";
Patternp2=Pattern.compile("\\s");
Patternpp2=Pattern.compile("\\S");
Matcherm2=p2.matcher(text);
Matchermm2=pp2.matcher(text);
System.out.println(m2.matches());//true
System.out.println(mm2.matches());//false

妥善利用这种互补的属性,可以得到一些非常巧妙的效果,最简单的应用就是字符组[\s\S]。初看起来,在同一个字符组中并列两个互补的简记法,这种做法有点奇怪,不过仔细想想就会明白, \s和\S组合在一起,匹配的就是“所有的字符”(或者叫“任意字符”)。许多语言中的正则表达式并没有直接提供“任意字符”的表示法,所以 [\s\S\,[\w\W],[\d\D]虽然看起来有点古怪,但确实可以匹配任意字符(注:许多关于正则表达式的文档说,点号(.)能匹配任意字符。但在默认情况下,点号其实不能匹配换行符!!!)。

关于字符组简单法,最后需要补充两点:第一如果字符组中出现了字符组简记法,最好不要出现单独的 - ,否则可能引起错误,比如 [\d-a]就很迷惑,在有些语言中,- 会被作为普通字符,而有些语言中,这样写会报错;第二以上说的 \d,\w,\s的匹配规则,都是针对ASCII编码而言,也叫ASCII匹配规则。但是,目前一些语言中的正则表达式已经支持了Unicode字符,那么数字字符,单词字符,空白字符的范围,已经不仅仅限于ASCII编码中的字符(后面再讲)。

字符组运算

有些语言为字符组提供了更强大的功能,比如Java和.Net就提供了字符组运算的功能可以在字符组内进行集合运算,在某些情况下这种功能非常实用。

如果要匹配所有的元音字母,可以用 [aeIoU],但是要匹配所有的辅音字母却没有什么方便的办法,最直接的写法是[b-df-hj-np-tv-z],不但烦琐,而且难理解。其实,从26个字母中“减去”元音字母,剩下的就是辅音字母,如果有办法做个“减法”,就方便多了。Java语言中提供了这样的字符组:[[a-z]&&[^aeIoU]]表示除元音字母之外的所有字符(还包括大写字母,数字和各种符号),两者取交集,就得到“26个英文字母中,除去5个元音字母,剩下的21个辅音字母”。示例:

//字符组运算
Stringtext="p";
Patternp=Pattern.compile("[[a-z]&&[^aeIoU]]");
Matcherm=p.matcher(text);
System.out.println(m.matches());//true

POSIX字符组

前面介绍了常用的字符组,但是在某些文档中,你可能会发现类似 [:digit:],[:lower:]之类的字符组,看起来不难理解(digit就是“数字”,lower就是“小写”)但又很奇怪,它们就是POSIX字符组(POSIX Character Class)。因为某些语言的文档中出现了这些字符组,为避免困惑,这里有必要做个简要介绍。如果只使用常用的编程语言,可以忽略文档中的POSIX字符组,也可以忽略本节;如果想了解POSIX字符组,或者需要在Linux/UNIX下的各种工具中使用正则表达式,最好阅读本节。(注:你书上既然这样说,而我又只在Java中使用,这节的内容我也就过滤了,不写出来了!!!)

原文链接:https://www.f2er.com/regex/360835.html

猜你在找的正则表达式相关文章