字符类
字符类(character class)是正则里最常用的砖块——用方括号定义一个"集合",告诉引擎"匹配这个集合里的任意一个字符"。这一章我们把自定义字符类、范围写法、预定义简写、取反一次性讲透。
自定义字符类
用方括号把几个字符包起来,就构成一个字符类——它只匹配一个字符,但这个字符可以是方括号里列出的任意一个。在方括号开头加脱字符表示"取反",匹配"不在列表里的字符"。
// 字符类(character class):用方括号定义"匹配其中任意一个字符"
[abc] // a 或 b 或 c(三选一)
[^abc] // ^ 在开头表示"取反":不是 a、不是 b、不是 c
[a-z] // 任意小写字母(a 到 z 闭区间)
[A-Z] // 任意大写字母
[0-9] // 任意数字
[a-zA-Z] // 任意字母(大小写)
[a-zA-Z0-9_] // 字母、数字、下划线(等价于预定义类 \w)
// 实例
/[aeiou]/.test("hello"); // true (匹配任意元音)
/[^0-9]/.test("12345"); // false (全是数字,没有非数字)
/[A-Z][a-z]+/.exec("Hello"); // ["Hello"] (大写开头的小写串)记住:不管方括号里写多少字符,它始终只匹配一个位置上的字符。要匹配多个,得配合下一章讲的量词(星号、加号、问号)。
预定义字符类
有几组字符集合出现频率极高——数字、字母、空白——正则为它们准备了一两个字符的简写。这些"魔法字符"必须背下来,日常 90% 的正则都离不开它们。
// 预定义字符类(背下来,天天用):
\d // 数字(digit) 等价于 [0-9]
\D // 非数字 等价于 [^0-9]
\w // 单词字符(word) 等价于 [a-zA-Z0-9_]
\W // 非单词字符 等价于 [^a-zA-Z0-9_]
\s // 空白(space) 包括空格、Tab、换行、回车、换页
\S // 非空白
. // 任意一个字符(除换行,加 s 标志后含换行)
// 大小写对应"是 / 否":大写就是小写的反义
// \d ↔ \D \w ↔ \W \s ↔ \S
// 实例
/\d+/.exec("订单号 12345"); // ["12345"]
/\w+/.exec("hello world"); // ["hello"]
/\s+/.exec("a b"); // [" "]
/[a-z]+\d+/i.exec("abc123"); // ["abc123"]几个易混点:点号默认不匹配换行(加 s 标志才匹配);大小写对应"是 / 否"——大写就是小写的反义,理解这种对称性能加速记忆。还有几个特殊序列(单词边界、字符串开头)会在锚点一章讲。
范围写法的细节
方括号里用连字符表示范围,比如 a-z 表示从 a 到 z。但范围有几个细节新手容易踩坑:必须按 ASCII 码顺序、可以连写多个范围、还能跨字符类别。
// 范围(range)的注意事项
// 1. 范围按 ASCII 码顺序,不能反过来
/[a-z]/.test("m"); // true
/[z-a]/; // ❌ 语法错误(起始 > 结束)
// 2. 多个范围可以连写
/[a-zA-Z0-9]/.test("X"); // true
/[0-9a-fA-F]/.test("F"); // true (十六进制字符)
// 3. 范围两端不限于字母数字
/[ !"#$%]/.test(" "); // true (含空格、感叹号、井号等)
/[\x00-\x7F]/.test("A"); // true (整个 ASCII 范围)
// 4. 中文区间需要小心:UTF-16 码点连续才行
/[\u4e00-\u9fa5]/.test("中"); // true (常用 CJK 汉字范围)
/[\u4e00-\u9fa5]+/.exec("一段中文abc"); // ["一段中文"]
// 5. 在方括号里,特殊字符大多失去特殊含义
/[.*+?{}()]/.test("."); // true (这些元字符在 [] 里是字面值)
/[-]/.test("-"); // true (连字符放首尾也是字面值)
/[]]/.test("]"); // true (右方括号放首位是字面值)一个反直觉但很重要的规则:在方括号内部,大部分元字符会自动失去特殊含义,变成字面值。点号、星号、加号、问号、圆括号在方括号里就是普通字符,不需要转义。唯一仍需小心的特殊字符是:右方括号(结束标志)、连字符(范围分隔)、脱字符(开头取反)、反斜杠(转义符)。
取反的妙用:排斥型匹配
取反字符类看似只是"反过来",但实战中它解决的是正则最容易出 bug 的场景——"贪婪跨过本应停止的位置"。下面这个对比是正则学习里的经典案例:
// 取反字符类的妙用
// [^x] 表示"除了 x 的任意字符"
/[^aeiou]/.test("b"); // true
/[^0-9]/.test("a"); // true
// 经典用法:排斥型匹配(比 . 更精确)
// 想匹配 HTML 标签里的内容,不要跨标签
/<.+>/.exec("<a><b><c>"); // ["<a><b><c>"] (. 太贪,跨了所有标签)
/<[^>]+>/.exec("<a><b><c>"); // ["<a>"] (不含 > 的字符,自然不跨标签)
// 提取引号里的内容
/"[^"]+"/.exec('say "hello" then'); // ['"hello"']
/'[^']+'/.exec("it's 'fine'"); // ["'fine'"]
// 提取括号里的内容
/\([^)]+\)/.exec("foo(bar)baz"); // ["(bar)"]原则是:能用具体的取反字符类,就别用通配点号。点号会匹配任何字符,引擎一不小心就跨过边界;而"非右尖括号的任意字符"自然不会跨过右尖括号,既快又安全。提取引号内容、括号内容、标签内容,统统用这个套路。
小结与速记表
- 方括号包列表,只匹配一个字符,三选一写三个。
- 脱字符在方括号开头是"取反",在外是"开头锚点"——同一个符号身兼多职。
- 连字符在方括号里是"范围",放首尾才是字面连字符。
- 预定义简写:数字、单词、空白,大小写对应"是/否"。
- 方括号内大多元字符自动变字面,不需要转义。
下一章讲量词——把"匹配一个字符"升级成"匹配几个字符",你的正则才算真正"动起来"。
← 上一篇 基础语法与元字符
下一篇 量词 →