第 2 课:匹配一组字符的字符类
本课目标:
- 用字符类
[…]匹配若干字符中的任意一个,包括范围写法。- 用
[^…]给字符类取反,并使用\d、\w、\s简写。- 确切说出点号
.匹配什么、不匹配什么。
“gray” 还是 “grey”?字面量只能二选一
字面量帮你拿到了精确文本,但真实数据比这更乱。是 “gray” 还是 “grey”?那个字段是数字还是字母?字面量模式强迫你在每个位置上认定一个确切字符,而你常常做不到。字符类正是解决这个问题的元字符家族:它让单个位置接受一组字符里的任意一个。从这里开始,正则才不再是一个花哨的字符串搜索,而开始成为真正的模式;也是在这里,那个神秘的 . 终于被钉清楚。
讲解
字符类(character class)写在方括号里,精确匹配集合中的一个字符1。所以 [abc] 匹配单个 “a”、“b” 或 “c”:一个字符,三选一。经典用法:gr[ae]y 同时匹配 “gray” 和 “grey”,因为第三个位置接受 “a” 或 “e”。
把每个字符都列出来很快就会累,所以连字符可以构成范围(range):[a-d] 等价于 [abcd]2。日常最常用的范围是 [a-z](一个小写字母)、[A-Z](大写)和 [0-9](一个数字)。它们可以组合:[A-Za-z] 是任意一个大小写字母。记住整个字符类仍然只匹配一个字符:范围只是加宽了那一个位置的菜单。
在方括号内部的最前面放一个插入符 ^,字符类就被取反:[^…] 匹配任何不在集合里的字符1。所以 [^0-9] 匹配一个不是数字的字符。这里的陷阱是把 [^a-z] 读成“不是字母”:它实际的意思是“不是小写的 a-z”,所以大写的 “H” 和空格都算匹配。
有三个简写出现得太频繁,以至于拥有自己的转义形式2:
\d:一个数字。等价于[0-9]。\w:一个“单词”字符:任意字母、数字或下划线。等价于[A-Za-z0-9_]。\s:一个空白字符:空格、制表符、换行等。
最后是点号。元字符 . 匹配除了行终止符(如 \n)之外的任意单个字符2。仔细读这句话:“任意字符”但不含换行,除非你打开 s(dotAll)标志。这个默认行为是模式跑在多行文本上时一个真实的 bug 来源。
要匹配一个字面量的点(真正的句点),用反斜杠转义它:\. 的意思是“一个真实的点”,而不是“任意字符”。这也是元字符的通用规则:在前面放一个 \,剥掉它的特殊能力,让它按字面匹配。
完整示例(跟着做)
你想检测单个十六进制数字:0-9 或 a-f,大小写皆可。用范围拼出字符类,然后测试它:
一步步看:字符类 [0-9a-fA-F] 把三个范围叠进一个集合,所以这单个位置接受任意数字或任意大小写的字母 a-f。“3”、“b”、“B” 各自落进某个范围,所以 test 是 true;“g” 在每个范围里都排在 “f” 之后,所以是 false。一个字符类,一个字符位置,三个范围的选择余地。
你来试试(填空示例)
你想匹配单个不是空白的字符,并且用简写而不是方括号字符类。填空:
答案:空格处填 \S,即 \s 的大写对应形式。规律是:大写简写就是其小写形式的取反:\S 是“非空白”,正如 \D 是“非数字”、\W 是“非单词字符”。所以 nonSpace.test(" ") 是 false(空格是空白),nonSpace.test("x") 是 true。你也可以写取反字符类 [^\s],意思相同。
小结 + 下节课预告
字符类 […] 匹配集合中的一个字符;[a-z] 这样的范围加宽菜单;开头的插入符做取反([^…] 是“除了这些都行”);\d / \w / \s 分别是数字 / 单词字符 / 空白的简写。点号匹配除行终止符之外的任意字符,反斜杠可以转义元字符让它按字面匹配。但每个字符类仍然恰好匹配一个字符。下一课我们补上故事的另一半:量词。它说明一个字符或字符类重复多少次,让 \d 能变成“一整串数字”。
Footnotes
-
regular-expressions.info: Character Classes — https://www.regular-expressions.info/charclass.html ↩ ↩2
-
MDN: Character classes — https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions/Character_classes ↩ ↩2 ↩3
练习
你想匹配单个字符:任意数字或下划线,别的都不行。写出这个字符类,然后依次用 "7"、"_"、"a" 测试。再回答:为什么 [^a] 是表达“数字或下划线”的坏办法?
第 2 级(进阶)我的笔记
记下想法、痛点、没懂的地方。只写进这门课的附录,正课文件不动。