正则表达式参考手册及开发记录

今天来学习一下正则表达式,主要是目前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} 前一个字符或组匹配 nm 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:]]

七、常用模式示例

  1. 匹配 URL

    ^https?://[\w.-]+(?:/[\w.-]*)*$
  2. 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}$
  3. HTML 注释

    <!--.*?-->  # 非贪婪匹配
  4. 强密码(至少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"
    }
}