当前位置: 首页 > news >正文

JS正则表达式介绍(JavaScript正则表达式)

文章目录

  • JavaScript正则表达式完全指南
    • 正则表达式基础
    • 元字符与特殊字符
      • 基本元字符
        • `.` - 点号
        • `\d` - 数字
        • `\D` - 非数字
        • `\w` - 单词字符
        • `\W` - 非单词字符
        • `\s` - 空白字符
        • `\S` - 非空白字符
    • 正则表达式标志
      • 常用标志详解
        • `g` - 全局匹配
        • `i` - 忽略大小写
        • `m` - 多行匹配
        • `s` - 点号匹配所有字符
        • `u` - Unicode模式
        • `y` - 粘性匹配
      • 组合使用标志
      • 在RegExp构造函数中使用标志
      • 通过属性检查标志
    • 量词与边界
      • 量词
        • `*` - 零次或多次
        • `+` - 一次或多次
        • `?` - 零次或一次
        • `{n}` - 恰好n次
        • `{n,}` - 至少n次
        • `{n,m}` - n到m次
      • 边界匹配
        • `^` - 行首
        • `$` - 行尾
        • `\b` - 单词边界
        • `\B` - 非单词边界
    • 分组与捕获
      • 基本分组
      • 命名捕获组
    • 断言与零宽断言
        • `(?=...)` - 正向前瞻
        • `(?!...)` - 负向前瞻
        • `(?<=...)` - 正向后顾
        • `(?<!...)` - 负向后顾
    • JavaScript中的正则表达式方法
      • String对象的方法
        • `search()` - 查找匹配
        • `replace()` - 替换匹配
        • `split()` - 分割字符串
        • `match()` - 获取匹配
        • `matchAll()` - 获取所有匹配信息
      • RegExp对象的方法
        • `test()` - 测试匹配
        • `exec()` - 执行匹配
    • 高级应用与技巧
      • 贪婪与非贪婪匹配
        • 贪婪匹配
        • 非贪婪匹配
      • 常见应用场景
        • 验证电子邮箱
        • 验证手机号
        • 提取URL参数
    • 性能优化
      • 避免回溯过多
      • 使用非捕获组
    • 总结

JavaScript正则表达式完全指南

正则表达式基础

正则表达式是一种强大的文本处理工具,用于匹配、搜索和替换文本中的特定模式。在JavaScript中,正则表达式是一种对象,可以通过字面量或构造函数创建。

// 使用字面量创建正则表达式
const pattern1 = /hello/;
// 这里创建了一个匹配"hello"字符串的正则表达式// 使用构造函数创建正则表达式
const pattern2 = new RegExp("hello");
// 这种方法也创建了一个匹配"hello"的正则表达式,效果与上面相同

元字符与特殊字符

正则表达式的强大之处在于其元字符系统,这些特殊字符具有特殊含义。

基本元字符

. - 点号

匹配除换行符外的任意单个字符

// 使用点号匹配任意字符
const dotPattern = /h.t/g;
"hit hot hat h t".match(dotPattern); // 结果: ["hit", "hot", "hat"]
// 解释: 点号匹配除换行符外的任意字符,所以匹配到"hit", "hot", "hat",但不匹配"h t"
\d - 数字

匹配任何数字字符(0-9)

// 匹配任意数字
const digitPattern = /\d/g;
"abc123".match(digitPattern); // 结果: ["1", "2", "3"]
// 解释: \d匹配任意单个数字,g标志表示全局匹配,所以找到了所有数字
\D - 非数字

匹配任何非数字字符

// 匹配任意非数字字符
const nonDigitPattern = /\D/g;
"abc123".match(nonDigitPattern); // 结果: ["a", "b", "c"]
// 解释: \D匹配任意非数字字符,所以匹配到了字母a、b和c
\w - 单词字符

匹配字母、数字、下划线

// 匹配单词字符
const wordCharPattern = /\w/g;
"a1_!".match(wordCharPattern); // 结果: ["a", "1", "_"]
// 解释: \w匹配字母、数字和下划线,所以匹配到a、1和_,但不匹配!
\W - 非单词字符

匹配非字母、数字、下划线的字符

// 匹配非单词字符
const nonWordCharPattern = /\W/g;
"a1_!".match(nonWordCharPattern); // 结果: ["!"]
// 解释: \W匹配非字母、数字、下划线的字符,所以只匹配到!
\s - 空白字符

匹配空格、制表符、换行符等空白字符

// 匹配空白字符
const whitespacePattern = /\s/g;
"a b\tc\nd".match(whitespacePattern); // 结果: [" ", "\t", "\n"]
// 解释: \s匹配任意空白字符,所以匹配到空格、制表符和换行符
\S - 非空白字符

匹配任何非空白字符

// 匹配所有非空白字符
const nonWhitespacePattern = /\S/g;
"hello world".match(nonWhitespacePattern); // 结果: ["h","e","l","l","o","w","o","r","l","d"]
// 解释: \S匹配任意非空白字符,这里匹配了所有字母,空格被排除

正则表达式标志

正则表达式末尾的字母是"标志"(flags),用于控制正则表达式的匹配行为。这些标志放在正则表达式的结束斜杠/之后。

// 格式: /pattern/flags
const regex = /hello/g;  // g是全局匹配标志
// 这个正则表达式会匹配所有出现的"hello",而不仅仅是第一个

常用标志详解

g - 全局匹配

启用全局搜索,查找所有匹配项而不是只找第一个

// 不带g标志 - 只匹配第一个结果
"hello hello".match(/hello/); // 结果: ["hello"]
// 解释: 没有g标志,所以只返回第一个匹配的"hello"// 带g标志 - 匹配所有结果
"hello hello".match(/hello/g); // 结果: ["hello", "hello"]
// 解释: 有g标志,所以返回所有匹配的"hello",这里有两个
i - 忽略大小写

进行不区分大小写的匹配

// 不带i标志 - 区分大小写
"Hello".match(/hello/); // 结果: null
// 解释: 默认区分大小写,所以"Hello"与"hello"不匹配,返回null// 带i标志 - 忽略大小写
"Hello".match(/hello/i); // 结果: ["Hello"]
// 解释: i标志使匹配不区分大小写,所以"Hello"与"hello"匹配成功
m - 多行匹配

允许^和$匹配每一行的开始和结束

const multiline = "line1\nline2\nline3";// 不带m标志 - ^和$只匹配整个字符串的开始和结束
multiline.match(/^line/g); // 结果: ["line"]
// 解释: 没有m标志,^只匹配整个字符串的开始,所以只匹配到第一行的"line"// 带m标志 - ^和$匹配每一行的开始和结束
multiline.match(/^line/gm); // 结果: ["line", "line", "line"]
// 解释: 有m标志,^匹配每一行的开始,所以匹配到所有三行开头的"line"
s - 点号匹配所有字符

让点号(.)匹配包括换行符在内的所有字符

const text = "line1\nline2";// 不带s标志 - 点号不匹配换行符
text.match(/line1.line2/); // 结果: null
// 解释: 没有s标志,.不匹配换行符\n,所以模式不匹配// 带s标志 - 点号匹配包括换行符在内的所有字符
text.match(/line1.line2/s); // 结果: ["line1\nline2"]
// 解释: 有s标志,.可以匹配换行符\n,所以整个模式匹配成功
u - Unicode模式

启用完整的Unicode支持

// 启用Unicode属性转义
/\p{Script=Greek}/u.test('π'); // 结果: true
// 解释: u标志启用Unicode支持,允许使用\p{...}匹配特定Unicode属性,这里匹配希腊文字符// 正确处理Unicode代理对
"𠮷".match(/./u)[0].length; // 结果: 1 (正确识别为单个字符)
// 解释: u标志使得正则表达式正确处理占用两个代码单元的Unicode字符,如"𠮷",识别为一个字符
y - 粘性匹配

仅从正则表达式的lastIndex属性开始匹配

const str = "first second";
const sticky = /\w+/y;
sticky.lastIndex = 6; // 设置开始搜索位置为6(即"second"的开始位置)
sticky.test(str); // 结果: true,匹配"second"
// 解释: y标志使正则表达式只从指定的lastIndex位置开始匹配,匹配到了"second"// 如果设置位置不是单词开头,则不匹配
sticky.lastIndex = 7; // 设置到"econd"
sticky.test(str); // 结果: false
// 解释: lastIndex设为7,不是单词开头,y标志要求必须从lastIndex位置精确匹配,所以匹配失败

组合使用标志

标志可以组合使用,实现更灵活的匹配行为:

// 全局匹配 + 忽略大小写
"Hello hello HELLO".match(/hello/gi); // 结果: ["Hello", "hello", "HELLO"]
// 解释: g标志使匹配全局化,i标志使匹配不区分大小写,所以找到所有大小写形式的"hello"// 全局匹配 + 多行匹配
const text = "Line1\nLine2\nLine3";
text.match(/^Line\d$/gm); // 结果: ["Line1", "Line2", "Line3"]
// 解释: g标志使匹配全局化,m标志使^和$分别匹配每行的开始和结束,
// \d匹配一个数字,所以匹配到每行的完整内容

在RegExp构造函数中使用标志

// 使用字符串第二参数指定标志
const regex1 = new RegExp("pattern", "gi");
// 解释: 创建一个正则表达式匹配"pattern",带有g(全局)和i(忽略大小写)标志// 从现有正则复制并添加新标志
const originalRegex = /pattern/g;
const newRegex = new RegExp(originalRegex, "i"); // 结果: /pattern/gi
// 解释: 从已有的带g标志的正则表达式创建新的正则表达式,并添加i标志

通过属性检查标志

const regex = /test/gi;regex.global; // true (是否有g标志)
regex.ignoreCase; // true (是否有i标志)
regex.multiline; // false (是否有m标志)
regex.dotAll; // false (是否有s标志)
regex.unicode; // false (是否有u标志)
regex.sticky; // false (是否有y标志)
// 解释: 通过正则表达式对象的属性可以检查该正则表达式是否设置了特定标志

量词与边界

量词

* - 零次或多次

匹配前一项0次或多次

// 使用*匹配0次或多次
const starPattern = /ab*c/g;
"ac abc abbc".match(starPattern); // 结果: ["ac", "abc", "abbc"]
// 解释: b*匹配0个或多个b,所以匹配到"ac"(0个b)、"abc"(1个b)和"abbc"(2个b)
+ - 一次或多次

匹配前一项1次或多次

// 使用+匹配1次或多次
const plusPattern = /ab+c/g;
"ac abc abbc".match(plusPattern); // 结果: ["abc", "abbc"]
// 解释: b+匹配1个或多个b,所以匹配到"abc"和"abbc",但不匹配"ac"(没有b)
? - 零次或一次

匹配前一项0次或1次

// 使用?匹配0次或1次
const questionPattern = /ab?c/g;
"ac abc abbc".match(questionPattern); // 结果: ["ac", "abc"]
// 解释: b?匹配0个或1个b,所以匹配到"ac"和"abc",但不匹配"abbc"(有2个b)
{n} - 恰好n次

匹配前一项恰好n次

// 使用{n}匹配恰好n次
const exactPattern = /ab{2}c/g;
"abc abbc abbbc".match(exactPattern); // 结果: ["abbc"]
// 解释: b{2}匹配恰好2个b,所以只匹配到"abbc"
{n,} - 至少n次

匹配前一项至少n次

// 使用{n,}匹配至少n次
const atLeastPattern = /ab{2,}c/g;
"abc abbc abbbc".match(atLeastPattern); // 结果: ["abbc", "abbbc"]
// 解释: b{2,}匹配至少2个b,所以匹配到"abbc"(2个b)和"abbbc"(3个b)
{n,m} - n到m次

匹配前一项至少n次,最多m次

// 使用{n,m}匹配n到m次
const rangePattern = /ab{1,2}c/g;
"abc abbc abbbc".match(rangePattern); // 结果: ["abc", "abbc"]
// 解释: b{1,2}匹配1到2个b,所以匹配到"abc"(1个b)和"abbc"(2个b),但不匹配"abbbc"(3个b)

边界匹配

^ - 行首

匹配输入的开始位置,在多行模式下匹配每一行的开始

// 使用^匹配行首
const startPattern = /^start/;
"start line\nanother line".match(startPattern); // 结果: ["start"]
// 解释: ^匹配字符串开头,所以匹配到开头的"start"// 在多行模式下
const multilineStartPattern = /^line/gm;
"first line\nline two".match(multilineStartPattern); // 结果: ["line"]
// 解释: 在多行模式下,^匹配每行的开头,所以匹配到第二行开头的"line"
$ - 行尾

匹配输入的结束位置,在多行模式下匹配每一行的结束

// 使用$匹配行尾
const endPattern = /end$/;
"line end\nline".match(endPattern); // 结果: ["end"]
// 解释: $匹配字符串结尾,所以匹配到第一行结尾的"end"// 在多行模式下
const multilineEndPattern = /line$/gm;
"first line\nline two".match(multilineEndPattern); // 结果: ["line"]
// 解释: 在多行模式下,$匹配每行的结尾,所以匹配到第一行结尾的"line"
\b - 单词边界

匹配单词边界,即字母与非字母之间的位置

// 匹配以字母a开头的单词
const startsWithA = /\ba\w*/g;
"apple banana grape avocado".match(startsWithA); // 结果: ["apple", "avocado"]
// 解释: \b匹配单词边界,a匹配字母a,\w*匹配零个或多个字母、数字或下划线,
// 所以匹配以a开头的单词"apple"和"avocado"
\B - 非单词边界

匹配非单词边界

// 匹配不在单词边界的'a'
const nonBoundaryA = /\Ba\w*/g;
"apple banana grape avocado".match(nonBoundaryA); // 结果: ["ana"]
// 解释: \B匹配非单词边界,a匹配字母a,\w*匹配零个或多个字母、数字或下划线,
// 所以匹配到"banana"中的"ana",因为这个'a'不在单词开头

分组与捕获

基本分组

使用圆括号()可以将正则表达式的一部分组合起来,作为一个单元。

// 匹配重复的单词
const repeatedWord = /(\w+)\s+\1/g;
"hello hello world".match(repeatedWord); // 结果: ["hello hello"]
// 解释: 
// (\w+) - 捕获组,匹配一个或多个单词字符(字母、数字或下划线)
// \s+ - 匹配一个或多个空白字符
// \1 - 反向引用,引用第一个捕获组匹配的内容
// 整体匹配连续重复的单词,如"hello hello"// 捕获组的使用
const datePattern = /(\d{4})-(\d{2})-(\d{2})/;
const match = "2023-05-15".match(datePattern);
// match[0]: "2023-05-15" (完整匹配)
// match[1]: "2023" (第一个捕获组 - 年份)
// match[2]: "05" (第二个捕获组 - 月份)
// match[3]: "15" (第三个捕获组 - 日期)
// 解释: 每个括号内的内容形成一个捕获组,匹配结果中可以通过索引访问每个捕获组

命名捕获组

ES2018引入了命名捕获组,使得访问捕获的内容更加直观。

// 使用命名捕获组解析日期
const datePattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = "2023-05-15".match(datePattern);
// match.groups.year: "2023"
// match.groups.month: "05"
// match.groups.day: "15"
// 解释: 
// (?<year>\d{4}) - 命名为"year"的捕获组,匹配四位数字
// (?<month>\d{2}) - 命名为"month"的捕获组,匹配两位数字
// (?<day>\d{2}) - 命名为"day"的捕获组,匹配两位数字
// 通过match.groups对象可以按名称访问捕获的内容,更加直观

断言与零宽断言

断言不消耗字符,只进行位置匹配。

(?=...) - 正向前瞻

匹配后面跟着特定模式的位置

// 前瞻断言:匹配后面跟着"张"的所有字符
const lookAheadPattern = /\w+(?=张)/g;
"李明张三王五".match(lookAheadPattern); // 结果: ["李明"]
// 解释: \w+匹配一个或多个字符,(?=张)是一个前瞻断言,表示后面必须跟着"张"
// 所以匹配到"李明",因为它后面是"张"
(?!...) - 负向前瞻

匹配后面不跟着特定模式的位置

// 负前瞻:匹配后面不是数字的字母序列
const negativeLookAhead = /[a-z]+(?!\d)/g;
"abc1 def2 ghi".match(negativeLookAhead); // 结果: ["ab", "de", "ghi"]
// 解释: [a-z]+匹配一个或多个小写字母,(?!\d)是一个负前瞻断言,表示后面不能是数字
// 所以匹配到"abc1"中的"ab"(c后面是数字1),"def2"中的"de"(f后面是数字2),
// 以及"ghi"(后面没有任何字符)
(?<=...) - 正向后顾

匹配前面有特定模式的位置

// 后顾断言:匹配前面是数字的字母
const lookBehindPattern = /(?<=\d)[a-z]+/g;
"1apple 2banana 3grape".match(lookBehindPattern); // 结果: ["apple", "banana", "grape"]
// 解释: (?<=\d)是一个后顾断言,表示前面必须有一个数字
// [a-z]+匹配一个或多个小写字母
// 所以匹配到跟在数字后面的字母序列:"apple"、"banana"和"grape"
(?<!...) - 负向后顾

匹配前面没有特定模式的位置

// 负后顾断言:匹配前面不是数字的字母
const negativeLookBehindPattern = /(?<!\d)[a-z]+/g;
"1apple word 2banana".match(negativeLookBehindPattern); // 结果: ["word"]
// 解释: (?<!\d)是一个负后顾断言,表示前面不能是数字
// [a-z]+匹配一个或多个小写字母
// 所以只匹配到"word",因为它前面不是数字

JavaScript中的正则表达式方法

String对象的方法

search() - 查找匹配

查找字符串中是否存在匹配,返回首次匹配的位置索引,如果没有匹配则返回-1

const str = "JavaScript正则表达式很强大";
const pattern = /正则表达式/;// 测试是否包含模式
str.search(pattern); // 返回首次匹配的索引: 10
// 解释: search方法返回第一个匹配的位置索引,这里"正则表达式"从索引10开始
replace() - 替换匹配

将匹配的文本替换为指定内容

const str = "JavaScript正则表达式很强大";
const pattern = /正则表达式/;// 替换匹配内容
str.replace(pattern, "RegExp"); // 结果: "JavaScriptRegExp很强大"
// 解释: replace方法将匹配的"正则表达式"替换为"RegExp"
split() - 分割字符串

使用正则表达式匹配的内容作为分隔符来分割字符串

// 分割字符串
"apple,banana,orange".split(/,/); // 结果: ["apple", "banana", "orange"]
// 解释: split方法根据正则表达式匹配的内容(这里是逗号)分割字符串
match() - 获取匹配

获取字符串中匹配正则表达式的部分

const str = "JavaScript正则表达式很强大";// 匹配所有结果
const matches = str.match(/\w+/g); // 结果: ["JavaScript"]
// 解释: match方法返回匹配的结果,\w+匹配一个或多个字母、数字或下划线字符
// 这里只匹配到拉丁字符部分"JavaScript",因为\w默认不匹配中文字符
matchAll() - 获取所有匹配信息

返回一个包含所有匹配结果的迭代器

const str = "test1 test2 test3";
const regexp = /test(\d)/g;
const matches = [...str.matchAll(regexp)];// matches[0]: ["test1", "1"] (第一次匹配及其捕获组)
// matches[1]: ["test2", "2"] (第二次匹配及其捕获组)
// matches[2]: ["test3", "3"] (第三次匹配及其捕获组)
// 解释: matchAll返回所有匹配的信息,包括每次匹配的完整字符串和捕获组

RegExp对象的方法

test() - 测试匹配

测试字符串是否匹配正则表达式模式,返回布尔值

const pattern = /Java(Script)/;
const str = "JavaScript是一种脚本语言";// 测试是否匹配
pattern.test(str); // 结果: true
// 解释: test方法检查字符串是否匹配正则表达式,返回布尔值
exec() - 执行匹配

在字符串中执行匹配搜索,返回详细的匹配信息

const pattern = /Java(Script)/;
const str = "JavaScript是一种脚本语言";// 获取匹配信息
const execResult = pattern.exec(str);
// execResult[0]: "JavaScript" (完整匹配)
// execResult[1]: "Script" (第一个捕获组)
// execResult.index: 0 (匹配位置)
// 解释: exec方法返回一个数组,包含匹配的完整字符串和所有捕获组,
// 以及匹配的位置信息(index属性)

高级应用与技巧

贪婪与非贪婪匹配

贪婪匹配

默认情况下,正则表达式的量词是贪婪的,会尽可能多地匹配字符

const text = "<div>内容1</div><div>内容2</div>";// 贪婪匹配 - 匹配尽可能长的字符串
const greedyPattern = /<div>.*<\/div>/;
text.match(greedyPattern)[0]; // 结果: "<div>内容1</div><div>内容2</div>"
// 解释: .*是贪婪匹配,会匹配尽可能多的字符,所以匹配了整个字符串从第一个<div>到最后一个</div>
非贪婪匹配

在量词后添加?使其变为非贪婪匹配,会尽可能少地匹配字符

const text = "<div>内容1</div><div>内容2</div>";// 非贪婪匹配 - 匹配尽可能短的字符串
const nonGreedyPattern = /<div>.*?<\/div>/g;
text.match(nonGreedyPattern); // 结果: ["<div>内容1</div>", "<div>内容2</div>"]
// 解释: .*?是非贪婪匹配,会匹配尽可能少的字符,加上g标志匹配所有可能的结果,
// 所以分别匹配到了两个完整的div标签内容

常见应用场景

验证电子邮箱

使用正则表达式验证电子邮件地址格式

// 验证电子邮箱
const emailPattern = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/;
emailPattern.test("test@example.com"); // 结果: true
// 解释: 
// ^[\w-]+ - 以一个或多个字母、数字、下划线或连字符开始
// (\.[\w-]+)* - 可能跟着点号和更多字符(如user.name)
// @ - 然后是@符号
// ([\w-]+\.)+ - 一个或多个由点号分隔的域名部分
// [a-zA-Z]{2,7}$ - 以2-7个字母的顶级域名结束
验证手机号

验证中国大陆手机号格式

// 验证中国手机号
const phonePattern = /^1[3-9]\d{9}$/;
phonePattern.test("13812345678"); // 结果: true
// 解释:
// ^1 - 以数字1开头
// [3-9] - 第二位是3到9之间的数字
// \d{9}$ - 后面跟着9个数字结束
// 此正则匹配中国大陆常见的11位手机号码
提取URL参数

从URL中提取查询参数

// 提取URL参数
function getUrlParams(url) {const pattern = /[?&]([^=&#]+)=([^&#]*)/g;const params = {};let match;while ((match = pattern.exec(url)) !== null) {// match[1]: 参数名// match[2]: 参数值params[match[1]] = decodeURIComponent(match[2]);}return params;
}// 示例
const url = "https://example.com/search?query=正则表达式&lang=zh&page=1";
const params = getUrlParams(url);
// 结果: {query: "正则表达式", lang: "zh", page: "1"}
// 解释:
// [?&] - 匹配问号或&符号(URL参数的开始)
// ([^=&#]+) - 第一个捕获组匹配参数名(不包含=、&、#的字符序列)
// = - 匹配等号
// ([^&#]*) - 第二个捕获组匹配参数值(不包含&、#的字符序列)
// 循环使用exec方法获取所有参数,并构建参数对象

性能优化

避免回溯过多

避免使用可能导致灾难性回溯的模式

// 避免回溯过多
// 不良示例 - 可能导致灾难性回溯
const badPattern = /^(\w+)+$/;
// 解释: 这种嵌套量词的模式((\w+)+)可能导致指数级回溯,
// 对于某些输入可能引起正则表达式引擎长时间计算甚至崩溃// 改进版本
const betterPattern = /^\w+$/;
// 解释: 简化版本,仅匹配一个或多个单词字符,没有嵌套量词,性能更好

使用非捕获组

当不需要引用捕获内容时,使用非捕获组提高性能

// 避免使用不必要的捕获组
// 不良示例
const capturePattern = /(\d+)-(\d+)-(\d+)/;
// 解释: 如果不需要使用捕获的内容,这些捕获组会消耗额外的内存和处理时间// 非捕获组版本 - 如果不需要使用捕获的内容
const nonCapturePattern = /(?:\d+)-(?:\d+)-(?:\d+)/;
// 解释: 使用非捕获组(?:...)代替捕获组(),如果不需要引用匹配的内容,
// 这样可以提高性能,特别是在大型文本处理中

总结

JavaScript正则表达式是一种强大的文本处理工具,掌握其基本语法和高级技巧可以大幅提高文本处理效率。正则表达式由元字符、量词、分组等组成,通过合理组合这些元素,能够实现复杂的模式匹配需求。正确理解和使用标志(flags)可以让正则表达式匹配更精确、更高效,适应不同的文本处理需求。在实际应用中,需要注意正则表达式的可读性和性能问题,避免过于复杂的模式导致难以维护或性能下降。

相关文章:

JS正则表达式介绍(JavaScript正则表达式)

文章目录 JavaScript正则表达式完全指南正则表达式基础元字符与特殊字符基本元字符. - 点号\d - 数字\D - 非数字\w - 单词字符\W - 非单词字符\s - 空白字符\S - 非空白字符 正则表达式标志常用标志详解g - 全局匹配i - 忽略大小写m - 多行匹配s - 点号匹配所有字符u - Unicod…...

(51单片机)LCD显示红外遥控相关数字(Delay延时函数)(LCD1602教程)(Int0和Timer0外部中断教程)(IR红外遥控模块教程)

前言&#xff1a; 本次Timer0模块改装了一下&#xff0c;注意&#xff01;&#xff01;&#xff01;今天只是简单的实现一下&#xff0c;明天用次功能显示遥控密码锁 演示视频&#xff1a; 在审核 源代码&#xff1a; 如上图将9个文放在Keli5 中即可&#xff0c;然后烧录在…...

关于单片机的基础知识(一)

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于单片机基础知识的相关内容&#xf…...

操作系统学习笔记第2章 (竟成)

第 2 章 进程管理 【考纲内容】 1.进程与线程&#xff1a; (1) 进程 / 线程的基本概念&#xff1b; (2) 进程 / 线程的状态与转换&#xff1b; (3) 线程的实现&#xff1a;内核支持的线程&#xff1b;线程库支持的线程&#xff1b; (4) 进程与线程的组织与控制&#xff1b; (5)…...

《从零开始:构建你的第一个区块链应用》

一、引言 区块链技术&#xff0c;这个曾经只在金融领域被广泛讨论的技术&#xff0c;如今已经渗透到各个行业。从供应链管理到智能合约&#xff0c;区块链的应用场景越来越丰富。对于开发者来说&#xff0c;理解区块链的基本原理并构建一个简单的区块链应用&#xff0c;是进入这…...

[思维模式-24]:《本质思考力》-5- 马克思主义毛泽东思想揭示了了人类社会运作的普遍规律有哪些?

目录 一、马克思主义毛泽东思想揭示了了人类社会运作的普遍规律有哪些&#xff1f; 1、生产力与生产关系的辩证运动规律 2、阶级斗争与社会革命规律 3、社会形态演变规律 4、人民群众是历史创造者的规律 5、社会基本矛盾运动规律 6、认识与实践的辩证关系规律 二、马克…...

CentOS7.9部署FunASR实时语音识别接口 | 部署商用级别实时语音识别接口FunASR

0. 环境说明 本次在云服务器中部署一套实时语音识别接口&#xff0c;基于阿里开源的FunASR。 云服务器使用莱卡云&#xff0c;4核心4GB内存50GB存储空间&#xff0c;带宽10Mbps。 操作系统使用CentOS7.9 视频演示可以看 云服务器中部署实时语音识别接口 | FunASR在云服务器…...

炫酷粒子系统动画实战:Matplotlib实现银河漩涡效果

炫酷粒子系统动画实战&#xff1a;Matplotlib实现银河漩涡效果 效果演示&#xff1a;银河粒子漩涡核心代码分析1. 粒子系统初始化2. 动画更新函数3. 渲染优化技巧 完整实现代码Matplotlib的动画模块介绍​核心类对比核心功能分点注意事项 效果演示&#xff1a;银河粒子漩涡 动…...

MAD-TD: MODEL-AUGMENTED DATA STABILIZES HIGH UPDATE RATIO RL

ICLR 2025 spotlight paper 构建能够在少量样本下学习出优良策略的深度强化学习&#xff08;RL&#xff09;智能体一直是一个极具挑战性的任务。为了提高样本效率&#xff0c;近期的研究尝试在每获取一个新样本后执行大量的梯度更新。尽管这种高更新-数据比&#xff08;UTD&am…...

机器学习第四讲:无监督学习 → 给无标签积木自由组合,发现隐藏规律

机器学习第四讲&#xff1a;无监督学习 → 给无标签积木自由组合&#xff0c;发现隐藏规律 资料取自《零基础学机器学习》。 查看总目录&#xff1a;学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章&#xff1a;DeepSeek R1本地与线上满血版部署&#xff1a;超详细…...

Vue 两种导航方式

目录 一、声明式导航 二、编程式导航 三、两句话总结 一、声明式导航 1. 传参跳转&#xff1a; <router-link :to"/user?nameCHEEMS&id114514">Query传参 </router-link><router-link :to"/user?参数名1参数值1&参数名2参数值2&a…...

HTTP 的发展史:从前端视角看网络协议的演进

别再让才华被埋没&#xff0c;别再让github 项目蒙尘&#xff01;github star 请点击 GitHub 在线专业服务直通车GitHub赋能精灵 - 艾米莉&#xff0c;立即加入这场席卷全球开发者的星光革命&#xff01;若你有快速提升github Star github 加星数的需求&#xff0c;访问taimili…...

Spring 必会之微服务篇(2)

经过上一篇文章的介绍,应该对微服务有了基本的认识,以及为什么要用微服务和微服务要面临的挑战和对应的解决问题,这一期继续聊聊关于微服务的相关知识。 服务拆分 为什么拆 对于大多数的小型项目来说,一般是先采用单体架构,但是随着后面的用户规模变大,业务越来越复杂…...

21.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--身份认证服务拆分规划

从这篇文章开始我们将开始一步一步的拆分现有的单体应用孢子记账项目。按照上一篇文章中的介绍&#xff0c;我们首先把身份认证服务拆分出来。 一、功能分析 在当前的单体应用中&#xff0c;身份认证服务主要负责用户认证、授权以及角色权限管理等核心功能。 在拆分之前&…...

人工智能100问☞第19问:什么是专家系统?

目录 一、通俗解释 二、专业解析 ​三、权威参考 专家系统是基于​​知识库​​(存储专家经验与规则)和​​推理机​​(模拟专家逻辑判断)的人工智能程序,能在特定领域(如医疗诊断、工业控制)高效解决复杂问题。 一、通俗解释 ​​专家系统​​就像个“智能版老师傅…...

AutoGen+Deepseek+chainlit的简单使用

AutoGen 的应用场景 AutoGen 作为一个强大的多智能体协作框架&#xff0c;可用于多种复杂任务&#xff1a; 自动化工作流&#xff1a;构建由多个智能体组成的流水线&#xff0c;例如数据收集、分析、报告生成复杂问题分解&#xff1a;将难题拆解为子任务&#xff0c;分配给不…...

贪心算法专题(Part1)

目录 1. 贪心算法简介 2. 柠檬水找零 3. 将数组和减半的最少操作次数 4. 递增的三元子序列 5. K次取反后最大化的数组和 6. 增减字符串匹配 7. 分发饼干 8. 整数替换 1. 贪心算法简介 2. 柠檬水找零 题目链接&#xff1a;860. 柠檬水找零 - 力扣&#xff08;LeetCode…...

PyTorch API 4 - 分布式通信、分布式张量

文章目录 分布式通信包 - torch.distributed后端支持PyTorch 内置的后端选择哪个后端&#xff1f;常见环境变量选择使用的网络接口其他NCCL环境变量 基础概念初始化返回类型&#xff1a;boolTCP初始化共享文件系统初始化环境变量初始化方法 初始化后操作关闭处理重新初始化 组D…...

《类和对象(中)》

引言&#xff1a; 上次我们主要学习了类的相关知识&#xff0c;今天我们就来学习类和对象(中)&#xff0c;今天也会用到之前学习过的东西&#xff0c;可以说是前面知识的结合&#xff0c;较前面会难一点&#xff08;打个预防针&#xff09;。 一&#xff1a;类的默认成员函数…...

SSH终端登录与网络共享

SSH 是较可靠&#xff0c;专为远程登录会话和其他网络服务提供安全性的协议 注意 SSH终端登录的前提是&#xff1a;电脑和板卡都能够通过网络相连接及通信 与连接互联网不一样&#xff0c;SSH可以不用互联网&#xff0c;只要电脑和板卡组成一个小型网络即可 网络方案 如果您…...

n8n系列(5):LangChain与大语言模型应用

引言 n8n作为一个强大的工作流自动化平台,可以通过集成LangChain框架,为用户提供了便捷地利用OpenAI、Azure OpenAI等大语言模型的能力。 本文将深入探讨n8n中的AI集成功能,特别是LangChain节点的使用,以及如何构建智能化的工作流程来解决实际业务问题。 1. n8n的AI集成概…...

springboot3+vue3融合项目实战-大事件文章管理系统-更新用户信息

在一下三个代码处进行修改 在UserController里面增加uadate方法 PutMapping ("/update")public Result update(RequestBody Validated User user){userService.update(user);return Result.success();}在userservice中增加update方法 void update(User user); 然…...

20250510-查看 Anaconda 配置的镜像源

打开 Anaconda Prompt 查看 Anaconda 当前配置的镜像源&#xff0c;使用命令 conda config --show channels这将显示当前配置的通道&#xff08;channels&#xff09;&#xff0c;即镜像源列表。 此外&#xff0c;还可以使用 conda config --show命令来显示conda的配置信息&…...

CDGP数据治理主观题评分标准与得分策略

1.数据模型题目评分标准 1)准确理解题目中所描述的业务逻辑和需求得[1分] 2)正确使用模型设计方法,使用信息工程、信息建模集成定义、巴克符号、陈氏符号等其中一种得[1分] 3)正确设计实体和属性,题目中涉及的实体数量为25-30个,10个以内得[2分],10-20个得[3分],25个…...

[学习]RTKLib详解:sbas.c与rtcm.c

RTKLib详解&#xff1a;sbas.c与rtcm.c 本文是 RTKLlib详解 系列文章的一篇&#xff0c;目前该系列文章还在持续总结写作中&#xff0c;以发表的如下&#xff0c;有兴趣的可以翻阅。 [学习] RTKlib详解&#xff1a;功能、工具与源码结构解析 [学习]RTKLib详解&#xff1a;pntp…...

【基础IO下】磁盘/软硬链接/动静态库

前言&#xff1a; 文件分为内存文件和磁盘文件。磁盘文件是一个特殊的存在&#xff0c;因为磁盘文件不属于冯诺依曼体系&#xff0c;而是位于专门的存储设备中。因此&#xff0c;磁盘文件存在的意义是将文件更好的存储起来&#xff0c;一边后续对文件进行访问。在高效存储磁盘…...

JAVA练习题(1) 卖飞机票

import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner scnew Scanner(System.in);System.out.println("请输入飞机的票价&#xff1a;");int pricesc.nextInt();System.out.println("请输入月份&#xff1a;");…...

SpringBoot框架开发网络安全科普系统开发实现

概述 基于SpringBoot框架的网络安全科普系统开发指南&#xff0c;该系统集知识科普、案例学习、在线测试等功能于一体&#xff0c;本文将详细介绍系统架构设计、功能实现及技术要点&#xff0c;帮助开发者快速构建专业的网络安全教育平台。 主要内容 系统功能架构 本系统采…...

机器学习 day02

文章目录 前言一、TF-IDF特征词重要度特征提取二、无量纲化处理1.最大最小值归一化2.normalize归一化3.StanderScaler标准化 前言 通过今天的学习&#xff0c;我掌握了TF-IDF特征词重要度特征提取以及无量纲化处理的相关知识和用法 一、TF-IDF特征词重要度特征提取 机器学习算…...

《AI大模型应知应会100篇》第53篇:Hugging Face生态系统入门

第53篇&#xff1a;Hugging Face生态系统入门 ——从模型获取到部署的全流程实战指南 &#x1f4cc; 摘要 在人工智能快速发展的今天&#xff0c;Hugging Face已成为自然语言处理&#xff08;NLP&#xff09;领域最具影响力的开源平台之一。它不仅提供丰富的预训练模型、强大…...

计网学习笔记———网络

&#x1f33f;网络是泛化的概念 网络是泛化的概念 &#x1f342;泛化理解 网络的概念在生活中无处不在举例&#xff1a;社交网络、电话网路、电网、计算机网络 &#x1f33f;网络的定义 定义&#xff1a; 离散的个体通过通讯手段连成群体&#xff0c;实现资源的共享与交流、个…...

Vue3 怎么在ElMessage消息提示组件中添加自定义icon图标

1、定义icon组件代码&#xff1a; <template><svg :class"svgClass" aria-hidden"true"><use :xlink:href"iconName" :fill"color"/></svg> </template><script> export default defineComponen…...

17.Excel:实用的 VBA 自动化程序

一 excel 设置 开始-选项 二 批量创建工作表 某工作簿用于保存31天的东西&#xff0c;手动创建31个工作表不方便。 A1单元格输入内容&#xff0c;或者空着。从A2单元格开始&#xff0c;一定要以字符形式的&#xff0c;不能以数值和日期形式。12345这是数值形式&#xff0c;1月…...

Kubernetes生产实战(十六):集群安全加固全攻略

Kubernetes集群安全加固全攻略&#xff1a;生产环境必备的12个关键策略 在容器化时代&#xff0c;Kubernetes已成为企业应用部署的核心基础设施。但根据CNCF 2023年云原生安全报告显示&#xff0c;75%的安全事件源于K8s配置错误。本文将基于生产环境实践&#xff0c;系统讲解集…...

Cadence学习笔记之---导入PCB板框、网表

目录 01 | 引 言 02 | 环境描述 03 | 导入PCB板框 04 | 自画PCB板框 05 | 导入PCB网表 06 | 总 结 01 | 引 言 在上一篇小记中讲述了创建PCB工程的操作步骤、PCB工程中的类与子类&#xff0c;以及Cadence颇具特色的颜色管理器。 本篇小记主要记述如何导入PCB板框、自画…...

嵌入式硬件篇---麦克纳姆轮(简单运动实现)

文章目录 前言1. 麦克纳姆轮的基本布局X型布局O型布局 2. 运动模式实现原理(1) 前进/后退前进后退 (2) 左右平移向左平移向右平移 (3) 原地旋转顺时针旋转&#xff08;右旋&#xff09;逆时针旋转&#xff08;左旋&#xff09; (4) 斜向移动左上45移动 (5) 180旋转 3. 数学原理…...

en33网络配置文件未托管

从 nmcli device status 的输出可以看到&#xff0c;所有网络设备&#xff08;包括 ens33&#xff09;都处于 "未托管"&#xff08;unmanaged&#xff09;状态&#xff0c;这导致 NetworkManager 和传统的 network.service 都无法管理网络接口&#xff0c;从而引发 n…...

嵌入式学习--江协51单片机day4

昨天周五没有学习&#xff0c;因为中午没有睡觉&#xff0c;下午和晚上挤不出整块的时间。周日有考试今天也没有学很多啊&#xff0c;但以后周末会是学一天&#xff0c;另一天休息和写周总结。 今天学了串口通信和LED点阵屏&#xff0c;硬件原理是真的很迷&#xff0c;一但想搞…...

Hadoop 2.x设计理念解析

目录 一、背景 二、整体架构 三、组件详解 3.1 yarn 3.2 hdfs 四、计算流程 4.1 上传资源到 HDFS 4.2 向 RM 提交作业请求 4.3 RM 调度资源启动 AM 4.4 AM运行用户代码 4.5 NodeManager运行用户代码 4.6 资源释放 五、设计不足 一、背景 有人可能会好奇&#xf…...

diy装机成功录

三天前&#xff0c;我正式开启了这次装机之旅&#xff0c;购入了一颗性能强劲的 i5-12400 CPU&#xff0c;一块绘图能力出色的 3060ti 显卡&#xff0c;还有技嘉主板、高效散热器、16G 内存条、2T 固态硬盘&#xff0c;以及气派的机箱和风扇&#xff0c;满心期待能亲手打造一台…...

睿思量化小程序

睿思量化小程序是成都睿思商智科技有限公司最新研发和运营的金融数据统计分析工具&#xff0c;旨在通过量化指标筛选与多策略历史回测&#xff0c;帮助用户科学配置基金资产&#xff0c;成为个人投资者与机构用户的“智能化财富管家”。 核心功能&#xff1a;数据驱动决策&…...

STM32实现九轴IMU的卡尔曼滤波

在嵌入式系统中&#xff0c;精确的姿态估计对于无人机、机器人和虚拟现实等应用至关重要。九轴惯性测量单元&#xff08;IMU&#xff09;通过三轴加速度计、陀螺仪和磁力计提供全面的运动数据。然而&#xff0c;这些传感器数据常伴随噪声和漂移&#xff0c;单独使用无法满足高精…...

JS DOM操作与事件处理从入门到实践

对于前端开发者来说&#xff0c;让静态的 HTML 页面变得生动、可交互是核心技能之一。实现这一切的关键在于理解和运用文档对象模型 (DOM) 以及 JavaScript 的事件处理机制。本文将带你深入浅出地探索 DOM 操作的奥秘&#xff0c;并掌握JavaScript 事件处理的方方面面。 目录 …...

Hive表JOIN性能问

在处理100TB的Hive表JOIN性能问题时&#xff0c;需采用分层优化策略&#xff0c;结合数据分布特征、存储格式和计算引擎特性。以下是系统性优化方案&#xff1a; 1. 数据倾斜优化&#xff08;Skew Join&#xff09; 1.1 识别倾斜键 方法&#xff1a;统计JOIN键的分布频率&…...

关键点检测--使用YOLOv8对Leeds Sports Pose(LSP)关键点检测

目录 1. Leeds Sports Pose数据集下载2. 数据集处理2.1 获取标签2.2 将图像文件和标签文件处理成YOLO能使用的格式 3. 用YOLOv8进行训练3.1 训练3.2 预测 1. Leeds Sports Pose数据集下载 从kaggle官网下载这个数据集&#xff0c;地址为link&#xff0c;下载好的数据集文件如下…...

2025年客运从业资格证备考单选练习题

客运从业资格证备考单选练习题 1、从事道路旅客运输活动时&#xff0c;应当采取必要措施保证旅客的人身和财产安全&#xff0c;发生紧急情况时&#xff0c;首先应&#xff08; &#xff09;。 A. 抢救财产 B. 抢救伤员 C. 向公司汇报 答案&#xff1a;B 解析&#xff1a;…...

QMK自定义4*4键盘固件创建教程:最新架构详解

QMK自定义4*4键盘固件创建教程&#xff1a;最新架构详解 前言 通过本教程&#xff0c;你将学习如何在QMK框架下创建自己的键盘固件。QMK是一个强大的开源键盘固件框架&#xff0c;广泛用于DIY机械键盘的制作。本文将详细介绍最新架构下所需创建的文件及其功能。 准备工作 在…...

获取conan离线安装包

1、获取conan离线安装包 # apt-get install python3.12-venv pip #缓存的安装存放在/var/cache/apt/archives目录 # mkdir /myenv && cd /myenv #创建虚拟环境目录 # python3 -m venv myenv #创建虚拟环境 # source myenv/bin/activate #激活虚拟环境&#xff…...

【Java ee初阶】网络原理

应用层 由于下面的四层都是系统已经实现好了的&#xff0c;但是应用层是程序员自己写的&#xff0c;因此应用层是程序员最重要的一层。 应用层中&#xff0c;程序员通常需要定义好数据传输格式&#xff0c;调用传输层api&#xff08;socket api&#xff09;进行真正的网络通信…...

Makefile中 链接库,同一个库的静态库与动态库都链接了,生效的是哪个库

Makefile中 链接库&#xff0c;同一个库的静态库与动态库都链接了&#xff0c;生效的是哪个库 在 Makefile 中同时链接同一个库的静态库&#xff08;.a&#xff09;和动态库&#xff08;.so&#xff09;时&#xff0c;具体哪个库生效取决于链接顺序和编译器行为。以下是详细分析…...