今天来学习一下正则表达式,主要是目前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"
}
}