【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
字符类(Character classes)是正则表达式中最基础、最高频的一类特殊符号,它允许我们用一两个字符去匹配一整组候选字符。本篇文章以本项目 zh.javascript.info 中《正则表达式》章节的 字符类一文 为骨架,结合仓库中相邻章节的源码与示例,系统讲解 \d、\s、\w 三大核心类、其反向类 \D、\S、\W、点号 . 的特殊语义以及 s 修饰符的用法。学完本篇,你将能够仅用几行正则表达式完成"从混合文本中提取纯数字电话号码"这类典型的数据清洗任务,并理解字符类与后续的集合和范围、Unicode 属性之间的递进关系。
从一个真实任务出发:提取纯数字电话号码
我们有一个电话号码 "+7(903)-123-45-67",希望把它转换成纯数字形式:79031234567。直接手写循环逐个字符判断当然可行,但正则表达式提供了一条更简洁的路径:找出所有数字并拼接,或者删掉所有非数字。而这两条路分别依赖字符类与其反向类。
在动手之前,先回顾一下正则表达式的两种创建语法(详见模式与修饰符):
regexp = new RegExp("pattern", "flags"); // 较长语法,适合动态生成
regexp = /pattern/g; // 斜线字面量语法,最常见
两者的本质都是内建类 RegExp 的实例。斜线语法更常用,因为它在代码中一目了然;new RegExp 则用于模式字符串在运行时才确定(例如来自 prompt 输入)的场景。
三大核心字符类:\d、\s、\w
字符类是一种特殊的符号,匹配"特定集合中的任何符号"。最常用的有三个:
| 字符类 | 名称来源 | 匹配内容 |
|---|---|---|
pattern:\d | digit | 数字:0 到 9 的任意一位 |
pattern:\s | space | 空格符号:空格、制表符 \t、换行符 \n,以及少数稀有字符如 \v、\f、\r |
pattern:\w | word | "单字"字符:拉丁字母、数字、下划线 _ |
关于 \w 需要特别注意:它只覆盖拉丁字母,不包含非拉丁字母(如西里尔字母、中文象形文字)。这一点在本仓库后续的 集合和范围 一节中有更精确的说明——\w 本质上就是简写形式的 [a-zA-Z0-9_],而 \d 等价于 [0-9],\s 等价于 [\t\n\v\f\r ] 外加少量罕见的 Unicode 空格字符。理解这层"简写"关系,是后面理解集合与排除范围的基础。
用 \d 提取电话号码中的数字
先找到第一个数字。注意:没有修饰符 g 时,正则表达式只返回第一个匹配项:
let str = "+7(903)-123-45-67";
let regexp = /\d/;
alert( str.match(regexp) ); // 7
加上修饰符 g(global,全局搜索),str.match(regexp) 返回由所有匹配项组成的数组:
let str = "+7(903)-123-45-67";
let regexp = /\d/g;
alert( str.match(regexp) ); // 7,9,0,3,1,2,3,4,5,6,7
alert( str.match(regexp).join('') ); // 79031234567
这里用到了两个关键点:其一,g 修饰符决定 match 返回"全部匹配项数组"还是"第一个匹配项(含详细信息)";其二,match 在没有匹配项时返回的是 null 而非空数组(详见模式与修饰符),因此拿到结果后若不确定是否有匹配,可用 || [] 兜底。上例中我们直接对数组调用 .join(''),成功拼出纯数字电话号码。
字符类可与普通字符自由组合
正则表达式可以同时包含常规符号和字符类。pattern:\d\s\w 表示"数字 + 空格字符 + 单字字符",例如能匹配 match:1 a。
再看一个更贴近真实业务的例子:pattern:CSS\d 匹配 CSS 后面紧跟一位数字的字符串:
let str = "Is there CSS4?";
let regexp = /CSS\d/;
alert( str.match(regexp) ); // CSS4
多个字符类连用也是常见手法,例如 pattern:\s\w\w\w\w\d 在 "I love HTML5!" 中匹配出 ' HTML5':
alert( "I love HTML5!".match(/\s\w\w\w\w\d/) ); // ' HTML5'
该匹配过程(每个正则表达式字符类对应一个结果字符)可以用仓库中的示意图直观呈现:
反向类:\D、\S、\W
对于每个字符类,都有一个反向类:字母相同、改为大写,表示"匹配所有其他字符"。
| 反向类 | 匹配内容 |
|---|---|
pattern:\D | 非数字:除 \d 之外的任何字符,例如字母 |
pattern:\S | 非空格符号:除 \s 之外的任何字符 |
pattern:\W | 非单字字符:除 \w 之外的任何字符,例如非拉丁字母或空格 |
回到开头的电话号码任务。用反向类可以写出第二种解法:找出所有非数字 \D 并替换为空字符串。str.replace(regexp, replacement) 在带有 g 修饰符时会替换所有匹配项,否则只替换第一处:
let str = "+7(903)-123-45-67";
alert( str.replace(/\D/g, "") ); // 79031234567
于是"提取纯数字"有了两条对称的思路——match(/\d/g).join('') 与 replace(/\D/g, "")。前者"取正",后者"取反",两者都依赖字符类与反向类的互补关系。后续集合和范围中的排除范围 [^\d] 与 \D 作用相同、[^\s] 与 \S 作用相同,正是同一思想的延续。
点号(.):匹配"任何字符"
点 pattern:. 是特殊的字符类,匹配除换行符之外的任何字符。
alert( "Z".match(/./) ); // Z
放在正则表达式中间时,它能"填"任意一个字符位:
let regexp = /CS.4/;
alert( "CSS4".match(regexp) ); // CSS4
alert( "CS-4".match(regexp) ); // CS-4
alert( "CS 4".match(regexp) ); // CS 4(空格也是一个字符)
必须强调:点表示"任何字符",而不是"缺少字符"——它必须实际消费一个字符。若中间没有字符可匹配,整个模式就会失败:
alert( "CS4".match(/CS.4/) ); // null,没有匹配项,因为这里没有与点匹配的字符
修饰符 s:让点号也能匹配换行符
默认情况下,点不匹配换行符 \n。因此 pattern:A.B 无法匹配 "A\nB":
alert( "A\nB".match(/A.B/) ); // null(无匹配项)
在很多场景(例如跨行文本解析)中,我们希望点能匹配包括换行符在内的任何字符。这正是修饰符 s(dotall 模式)的职责——关于该修饰符的定义可参见模式与修饰符中的修饰符清单:
alert( "A\nB".match(/A.B/s) ); // A\nB(匹配了!)
历史兼容提示:IE 浏览器不支持 s 修饰符。幸运的是有经典替代方案——用互补类组合 [\s\S] 来匹配"任何字符"(该模式将在集合和范围中正式介绍):
alert( "A\nB".match(/A[\s\S]B/) ); // A\nB(匹配了!)
[\s\S] 的字面含义是"空格字符或非空格字符",合起来即"任何字符"。同理,还可以使用 [\d\D],甚至 [^](匹配任何字符,除了什么都没有)。这种写法还有额外好处:当同一模式中既需要"普通点"(不含换行)又需要"万能点"时,可以用 [\s\S] 表达后者,而 . 保持默认语义。
易错点:正则表达式中的空格同样重要
日常阅读中我们常常忽略空格——字符串 1-5 与 1 - 5 对我们"看起来差不多"。但正则表达式是严格按字符逐一匹配的,空格也是一个字符,与其他字符同等重要。
试图用 \d-\d 匹配 "1 - 5" 会失败:
alert( "1 - 5".match(/\d-\d/) ); // null,没有匹配项!
原因很简单:模式中没有给两边的空格预留位置。修复方式是在正则表达式中显式写入空格,或用 \s 类:
alert( "1 - 5".match(/\d - \d/) ); // 1 - 5,现在可以了
alert( "1 - 5".match(/\d\s-\s\d/) ); // 1 - 5,也可以
因此,在正则表达式里添加或删除空格,都会改变匹配行为。这也是后续章节反复强调的"所有字符都重要"的体现——它同样适用于我们正在阅读的 集合和范围 与 Unicode:修饰符 "u" 和类 \p{...} 等进阶主题。
字符类的边界与下一步:Unicode 属性
本章介绍的七个符号(\d、\D、\s、\S、\w、\W、.)覆盖了 ASCII 世界的大多数场景,但它们并不是全部。
JavaScript 的字符串采用 Unicode 编码,每个字符都携带大量属性:它属于哪种语言的字母、是否是标点符号、是否是货币符号等。仅凭 \w 无法匹配中文、西里尔文等非拉丁文字(这一点在集合和范围的"多语言 \w"示例中得到了进一步印证:那里用 [\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}] 组合出了跨语言的单词字符)。要按这些属性搜索,需要修饰符 u 配合 \p{...} 语法——这正是下一篇:Unicode:修饰符 "u" 和类 \p{...} 的主题,它会让 \p{L}(任何语言的字母)、\p{Sc}(货币符号)等强大工具登场,并且修正代理对(surrogate pair)导致的"半个字符"问题。
总结
字符类一览:
pattern:\d—— 数字(等价于[0-9])。pattern:\D—— 非数字。pattern:\s—— 空格符号、制表符、换行符(等价于[\t\n\v\f\r ]及少量 Unicode 空格)。pattern:\S—— 除\s之外。pattern:\w—— 拉丁字母、数字、下划线'_'(等价于[a-zA-Z0-9_])。pattern:\W—— 除\w之外。pattern:.—— 带修饰符s时匹配任何字符;否则匹配除换行符\n之外的任何字符。
实战要点回顾:
- 用
str.match(/\d/g)收集全部数字、str.replace(/\D/g, "")删除全部非数字,均可把"+7(903)-123-45-67"清洗为"79031234567"; - 字符类可与普通字符、多个字符类自由拼接(如
CSS\d、\s\w\w\w\w\d); - 点号必须消费一个字符,
s修饰符或[\s\S]可让它匹配换行符; - 正则表达式中的空格是实打实的字符,别忽略它。
这些符号是后续一切正则进阶(集合范围、量词、分组、前瞻后瞻)的地基,建议配合正则表达式章节中的任务与解法(例如"查找时间 HH:MM"这类需要同时运用数字类与边界判断的练习)动手验证,加深理解。
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



