今天来学习一下正则表达式,主要是目前99%的编辑器都支持正则搜索了,为了下次改代码方便,然后就是JavaScript中也经常用到正则来处理文本、数据等,就比如我之前用纯JavaScript+正则写过一个Markdown2HTML.js,然后就是最近打算在写一个MC综合网,数据来自MciSee仓库中的Json文件,由于JavaScript中的Json不支持注释所以要使用正则来剔除,于是就灵感大现写了这篇文章,主要也是自己学习的笔记和正则表达式的参考手册,方便以后查阅,顺带水一篇博文。
正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE),它是一种文本模式,同时也是计算机科学的一个概念,其中包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。
许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开来的,后来才逐渐被广泛运用于Scala 、PHP、C# 、Java、C++ 、Objective-c、Perl 、Swift、VBScript 、Javascript、Ruby 以及Python等等。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen。
一、基础元字符
元字符 | 说明 | 示例 | ||
---|---|---|---|---|
. | 匹配除换行符外的任意单个字符(启用 s 修饰符时包含换行符) | a.c → "abc", "a%c" | ||
^ | 匹配字符串开头(多行模式下匹配行首) | ^Start → 以 "Start" 开头 | ||
$ | 匹配字符串结尾(多行模式下匹配行尾) | end$ → 以 "end" 结尾 | ||
* | 前一个字符或组匹配 0 次或多次(贪婪) | ab*c → "ac", "abbc" | ||
+ | 前一个字符或组匹配 1 次或多次(贪婪) | ab+c → "abc", "abbbc" | ||
? | 前一个字符或组匹配 0 次或 1 次;或表示非贪婪模式(如 .*? ) | colou?r → "color" 或 "colour" | ||
{n} | 前一个字符或组匹配恰好 n 次 | a{3} → "aaa" | ||
{n,} | 前一个字符或组匹配至少 n 次 | a{2,} → "aa", "aaaaa" | ||
{n,m} | 前一个字符或组匹配 n 到 m 次 | a{2,4} → "aa", "aaa", "aaaa" | ||
`\ | ` | 逻辑“或”,匹配左边或右边的表达式 | `cat\ | dog` → "cat" 或 "dog" |
() | 分组并捕获内容(可结合 `\ | ` 使用) | (ab)+ → "abab" | |
\ | 转义字符(将特殊字符转为字面量,或将普通字符转为特殊功能) | \. 匹配 . ,\\ 匹配 \ |
二、字符类
模式 | 说明 | 示例 |
---|---|---|
[...] | 匹配括号内的任意一个字符 | [aeiou] → 匹配任意元音字母 |
[^...] | 匹配不在括号内的任意一个字符 | [^0-9] → 匹配非数字字符 |
[a-z] | 字符范围(需注意字符编码顺序) | [A-F] → 匹配大写 A 到 F |
\d | 匹配数字(等价于 [0-9] ) | \d+ → "123" |
\D | 匹配非数字(等价于 [^0-9] ) | \D+ → "abc" |
\w | 匹配字母、数字、下划线(等价于 [a-zA-Z0-9_] ) | \w+ → "user_123" |
\W | 匹配非字母、数字、下划线(等价于 [^a-zA-Z0-9_] ) | \W+ → "@#$" |
\s | 匹配空白字符(空格、制表符、换行符等) | \s+ → 匹配连续空白 |
\S | 匹配非空白字符 | \S+ → "abc123" |
\b | 匹配单词边界(单词开头或结尾) | \bword\b → 匹配独立的 "word" |
\B | 匹配非单词边界 | \Bword\B → 匹配嵌入的 "word" |
三、高级语法
语法 | 说明 | 示例 | ||
---|---|---|---|---|
(?:...) | 非捕获分组(不保存匹配内容) | (?:ab)+ → "abab" | ||
(?<name>...) | 命名分组(通过名称引用捕获内容) | (?<year>\d{4}) → 捕获名为 "year" 的组 | ||
\1 , \2 | 反向引用(引用第 1、2 个捕获组) | (a)\1 → "aa" | ||
(?>...) | 原子组(禁止回溯) | `(?>a\ | ab)c` → 匹配 "ac" 但不匹配 "abc" | |
(?=...) | 正向先行断言(匹配后面是 ... 的位置) | X(?=Y) → 匹配后面是 Y 的 X | ||
(?!...) | 负向先行断言(匹配后面不是 ... 的位置) | X(?!Y) → 匹配后面不是 Y 的 X | ||
(?<=...) | 正向后行断言(匹配前面是 ... 的位置) | (?<=Y)X → 匹配前面是 Y 的 X | ||
(?<!...) | 负向后行断言(匹配前面不是 ... 的位置) | (?<!Y)X → 匹配前面不是 Y 的 X | ||
`(?(cond)yes\ | no)` | 条件匹配(若条件 cond 满足,则匹配 yes ,否则 no ) | `(?(1)true\ | false)` → 根据第 1 组是否匹配选择分支 |
四、模式修饰符
修饰符 | 说明 | 示例 |
---|---|---|
i | 忽略大小写 | /abc/i → 匹配 "ABC", "aBc" |
g | 全局匹配(查找所有匹配项) | /a/g → 匹配字符串中所有 "a" |
m | 多行模式(^ 和 $ 匹配每行的开头和结尾) | /^start/m → 匹配每行的 "start" |
s | 单行模式(使 . 匹配换行符) | /a.b/s → 匹配 "a\nb" |
u | Unicode 模式(支持 Unicode 字符) | /\p{L}/u → 匹配任意字母 |
x | 忽略空白和注释(增强可读性) | /\d+ # 数字部分/x |
五、特殊字符与转义
转义符 | 说明 | 示例 |
---|---|---|
\n | 换行符(LF, \x0A ) | \n → 匹配换行 |
\r | 回车符(CR, \x0D ) | \r\n → 匹配 Windows 换行符 |
\t | 制表符(\x09 ) | \t+ → 匹配连续制表符 |
\xHH | 十六进制字符(如 \x41 → "A") | \x41 → "A" |
\uHHHH | Unicode 字符(如 \u4e2d → "中") | \u00A9 → "©" |
\p{...} | Unicode 属性(需 u 修饰符,如 \p{L} 匹配字母) | \p{Emoji} → 匹配表情符号 |
六、POSIX 字符类(部分语言支持)
模式 | 说明 | 示例 |
---|---|---|
[:alnum:] | 字母和数字([a-zA-Z0-9] ) | [[:alnum:]]+ |
[:alpha:] | 字母([a-zA-Z] ) | [[:alpha:]] |
[:digit:] | 数字([0-9] ) | [[:digit:]]{4} |
[:lower:] | 小写字母([a-z] ) | [[:lower:]] |
[:upper:] | 大写字母([A-Z] ) | [[:upper:]] |
七、常用模式示例
匹配 URL:
^https?://[\w.-]+(?:/[\w.-]*)*$
IPv4 地址:
^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$
HTML 注释:
<!--.*?--> # 非贪婪匹配
强密码(至少8位,含大小写字母和数字):
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$
实战
由于Json标准中不包含注释,JavaScript中的json也不支持注释,而目标Json文件存在注释,为了确保代码可以正常执行,我需要写一个正则来匹配和剔除Json文件中的注释行。
这是对应的JavaScript代码:
const fs = require('fs');
// 读取外部 JSON 文件
function readJsonFile(filePath) {
try {
const data = fs.readFileSync(filePath, 'utf8');
return data;
} catch (err) {
console.error('读取文件失败:', err);
return null;
}
}
// 去除 JSON 中的注释
function removeComments(jsonString) {
// 去除单行注释 (//)
jsonString = jsonString.replace(/\/\/.*?(\n|$)/g, '');
// 去除多行注释 (/* */)
jsonString = jsonString.replace(/\/\*[\s\S]*?\*\//g, '');
return jsonString;
}
// 主逻辑
function processJsonFile(filePath) {
const rawData = readJsonFile(filePath);
if (!rawData) return;
// 去除注释
const cleanedJson = removeComments(rawData);
// 解析 JSON
try {
const parsedData = JSON.parse(cleanedJson);
console.log('解析后的 JSON 数据:', parsedData);
return parsedData;
} catch (err) {
console.error('JSON 解析失败:', err);
return null;
}
}
const filePath = './data.json';
processJsonFile(filePath);
这是对应的Json数据:
{
"name": "John Doe", // 这是一个单行注释
"age": 30,
"hobbies": ["reading", "traveling", "gaming"],
/* 这是一个
多行注释 */
"address": {
"street": "123 Main St",
"city": "Anytown"
}
}