3. 字符集与范围:方括号 []、连字符 -、排除字符集 [^]
字符集这个概念,说白了就是「我想在这一位上匹配多个可能字符中的某一个」。你想想看,如果正则只能匹配固定的单个字符,那它的灵活性就大打折扣了。方括号 [] 就是用来解决这个问题的。
3.1 方括号 []:匹配集合中的任意一个字符
方括号定义了一个字符集合。只要目标字符串的某个位置,匹配上了集合里的任意一个字符,就算匹配成功。我刚开始学的时候,总以为它是匹配「一组字符」,其实不是,它只匹配「一个」。
核心规则: [abc] 匹配字符 a、b 或 c 中的任意一个,且只匹配一个位置。
来看几个我常用的例子:
// 匹配 "cat" 或 "bat",但不匹配 "fat"
/[cb]at/
// 匹配任意一个数字
/[0123456789]/
// 匹配一个元音字母
/[aeiou]/
我在项目中遇到过这样一个场景:需要校验用户输入的验证码,它可能是数字也可能是小写字母。我当时就用 /^[a-z0-9]{4}$/ 搞定的,简洁又高效。
3.2 连字符 -:定义字符范围
如果我要匹配所有小写字母,总不能把 26 个字母全写进方括号里吧?那也太蠢了。连字符 - 就是用来干这个的,它表示一个连续的区间。
| 表达式 | 含义 | 等价写法(部分) |
|---|---|---|
[a-z] |
匹配任意小写英文字母 | [abcdefghijklmnopqrstuvwxyz] |
[A-Z] |
匹配任意大写英文字母 | [ABCDEFGHIJKLMNOPQRSTUVWXYZ] |
[0-9] |
匹配任意数字 | [0123456789] |
[a-zA-Z] |
匹配任意英文字母(大小写) | 组合范围 |
[0-9a-fA-F] |
匹配十六进制字符 | 数字 + 大小写 A-F |
注意:连字符 - 只有在方括号内部,且不在开头或结尾时,才表示范围。如果它出现在开头(如 [-abc])或结尾(如 [abc-]),或者被转义(如 [a\-z]),它就只是一个普通字符,匹配字面意义上的连字符。
嗯,这里要注意一个坑:范围是依赖 ASCII 码顺序的。所以 [a-Z] 这种写法是错的,因为大写字母的 ASCII 码比小写字母小,不能构成一个递增的范围。我曾经见过有人写 [3-7] 想匹配数字 3 到 7,这没问题。但有人写 [z-a],这就出问题了,引擎会报错。
3.3 排除字符集 [^]:匹配不在集合中的字符
有时候我们想要的是「除了这些字符以外的任意字符」。在方括号开头加一个脱字符 ^,就表示取反。
核心规则: [^abc] 匹配除了 a、b、c 之外的任意一个字符。
举个例子:
// 匹配除了数字以外的任意字符
/[^0-9]/
// 匹配不是元音字母的任意字符
/[^aeiou]/
// 匹配不是空白字符的任意字符
/[^ \t\n\r\f\v]/
我个人习惯用排除字符集来做输入过滤。比如在表单里,我想禁止用户输入特殊符号,就可以用 /^[^!@#$%^&*()]+$/ 来校验。说白了,排除字符集就是「黑名单」思维。
小技巧: 排除字符集匹配的是「一个字符位」,而不是「一个字符串」。所以 [^abc] 能匹配 "d",也能匹配 "x",但它不能匹配空字符串。如果目标位置没有字符,匹配会失败。
3.4 字符集内部的特殊字符与转义
在方括号内部,大部分元字符都会失去特殊含义,变成普通字符。比如 .、*、+、?、(、) 等,在方括号里就只是字面符号。
但有三个字符需要特别注意:
]:表示方括号的结束。如果想匹配字面意义上的],需要把它放在方括号的开头(如[]abc]),或者用反斜杠转义(如[abc\])。^:放在开头表示排除。如果想匹配字面意义上的^,不要把它放在开头(如[a^bc]),或者转义它。-:表示范围。如果想匹配字面意义上的-,把它放在开头或结尾(如[-abc]或[abc-]),或者转义它。
// 匹配字面意义上的 ]、^ 或 -
/[]^\-]/
// 匹配点号、星号、加号
/[.*+]/
我曾经在解析日志文件时,需要匹配方括号本身。当时写了个 /\[[^\]]+\]/ 来匹配方括号内的内容。这个模式的意思是:匹配一个左方括号,然后跟着一个或多个不是右方括号的字符,最后是一个右方括号。你看,排除字符集在这里就派上大用场了。
3.5 知识体系结构图
下面这张图帮你理清字符集的核心逻辑:
3.6 实战中的常见陷阱
你想想看,字符集看起来简单,但实际用起来还是有不少坑的。我总结几个常见的:
- 范围重叠问题: 比如
[a-zA-Z0-9]是合法的,但[a-Z]会报错。因为 ASCII 码中大写字母(65-90)在小写字母(97-122)之前,无法构成递增范围。 - 连字符位置: 如果你想匹配数字、连字符和字母,写成
[0-9a-z]会匹配数字、小写字母和连字符吗?不会。这里的-在9和a之间,它表示从 ASCII 码 57(数字9)到 97(字母a)的范围,这中间包含了一些标点符号。正确的写法应该是[0-9a-z-],把连字符放在最后。 - 排除字符集的误用: 我曾经用
[^abc]想匹配「不是 abc 这三个字符的字符串」,结果它只排除了单个字符。如果要排除一个字符串,需要用负向前瞻或负向后顾,这个我们后面会讲到。
我的建议: 在写字符集时,尽量把连字符放在开头或结尾,或者显式转义。这样既清晰,又不会踩到范围定义的坑。比如匹配一个十六进制颜色值,我习惯写 #[0-9a-fA-F]{6},而不是 #[a-f0-9A-F]{6},虽然结果一样,但前者更符合阅读习惯。
好了,字符集这部分内容就这些。说白了就是三个核心操作:选一个(普通字符集)、选一段(范围)、排除一些(排除字符集)。把这些搞明白,你就能处理大部分需要「模糊匹配单个字符」的场景了。