正则表达式

163 阅读6分钟

先推荐两个正则的工具:

  1. RegExr 测试正则表达式
  2. JavaScript Regular Expression Visualizer 正则表达式可视化

正则表达式是用于匹配字符串中字符组合的模式,也就是根据我们设定的匹配规则在一串字符串中,获取到指定的字符串; 比如:

const re = /ab+c/; // 可以匹配到 abc, abbc, abbbc等

下载.png 根据 正则表达式可视化 可以看出, 可以匹配到 b的个数 在 1个以上, 那么 abc, abbc, abbbc 都是符合要求的

image.png

当然 abbcc也是可以匹配的, 因为abbc是符合 /ab+c/的要求的; 也是 符合 我们刚刚描述的: 根据我们设定的匹配规则在一串字符串中,获取到指定的字符串

了解了正则的基本的概念, 那么就学习 正则表达式;

表现形式

在js中,正则表达式 表现形式 有两种方式:

  1. 字面量: 由斜杠(/)包围而不是引号包围, const re = /ab+c/i; // i 表示忽略大小写
  2. 构造函数: 使用 RegExp 对象, const re = new RegExp("ab+c", 'i')

标志参数

const re = /ab+c/i; 中有一个 i, 这是正则表达式的标志, 表示 忽略大小写, 还有其他的标志

标志描述
g全局搜索。
i不区分大小写搜索。
m多行搜索。
s允许 . 匹配换行符。
u使用unicode码的模式进行匹配。
y执行“粘性(sticky)”搜索,匹配从目标字符串的当前位置开始。

简单模式

所谓的简单模式,就是正则直接由你想找的字符构成,比如: abcdedf这个字符串, 你想找到 cde这个子字符串, 那么正则直接可以写 /cde/, 这就是简单模式, 比较暴力,但是有时候也是有价值的

特殊字符

相对于简单模式, 使用特殊字符就表现的更高级了。

比如:abbbbbcd这个字符串, 你想找到 abbbbbc这个子字符串, 你可以直接用简单字符去写,但是会觉得麻烦不是?

可以采用 特殊字符 进行匹配, 比如: /ab+c/+表示 匹配 一个或者一个以上的字符, 在这里就是 匹配 一个或者一个以上的b

断言

表示一个匹配在某些条件下发生。断言包含先行断言、后行断言和条件表达式。

断言的组成之一是边界。对于文本、词或模式,边界可以用来表明它们的起始或终止部分(如向前断言,向后断言以及条件表达式)。

边界类断言

字符含义
^匹配输入的开头。比如 /^A/ 匹配不了 "an A" 里面的 "A",但是可以匹配 "An A" 里面第一个 "A"。
$匹配输入的结束。比如 /t$/ 不能匹配  "eater" 中的 "t",但是可以匹配 "eat" 中的 "t"。
\b匹配一个单词的边界,这是一个字的字符前后没有另一个字的字符位置, 例如在字母和空格之间。需要注意的是匹配的单词边界不包括在匹配中。换句话说,匹配字边界的长度为零。一些例子: /\bm/  在 "moon" 中匹配到 "m" 
\B匹配非单词边界。这是上一个字符和下一个字符属于同一类型的位置:要么两者都必须是单词,要么两者都必须是非单词,例如在两个字母之间或两个空格之间。字符串的开头和结尾被视为非单词。与匹配的词边界相同,匹配的非词边界也不包含在匹配中。例如,/\Bon/ 在 “at noon” 中匹配 “on” ,/ye\B/ 在 "possibly yesterday"中匹配"ye"

其他断言

字符含义
x(?=y) 向前断言: x 被 y 跟随时匹配 x。例如,对于/Jack(?=Sprat)/,“Jack”在跟有“Sprat”的情况下才会得到匹配.`/Jack(?=SpratFrost)/` “Jack”后跟有“Sprat”或“Frost”的情况下才会得到匹配。不过, 匹配结果不包括“Sprat”或“Frost”。
x(?!y)向前否定断言:  x 没有被 y 紧随时匹配 x。例如,对于/\d+(?!.)/,数字后没有跟随小数点的情况下才会得到匹配。对于/\d+(?!.)/.exec(3.141),匹配‘141’而不是‘3’。
(?<=y)x向后断言:  x 跟随 y 的情况下匹配 x。例如,对于/(?<=Jack)Sprat/,“Sprat”紧随“Jack”时才会得到匹配。对于`/(?<=JackTom)Sprat`,“Sprat”在紧随“Jack”或“Tom”的情况下才会得到匹配。不过,匹配结果中不包括“Jack”或“Tom”。
(?<!y)x向后否定断言:  x 不跟随 y 时匹配 x。例如,对于/(?<!-)\d+/,数字不紧随-符号的情况下才会得到匹配。对于/(?<!-)\d+/.exec(3) ,“3”得到匹配。 而/(?<!-)\d+/.exec(-3)的结果无匹配,这是由于数字之前有-符号。

字符类

区分不同类型的字符,例如区分字母和数字。

字符含义
.匹配除行终止符之外的任何单个字符
\d匹配任何数字(阿拉伯数字), 0-9
\D匹配任何非数字(阿拉伯数字)的字符
\w匹配一个单字字符(字母、数字或者下划线)
\W匹配任何不是来自基本拉丁字母的单词字符
\s匹配一个空白字符,包括空格、制表符、换页符和换行符 , [ \f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]
\S匹配非空白字符
\t匹配一个水平制表符 (U+0009)
\r匹配一个回车符 (U+000D)
\n匹配一个换行符 (U+000A)
\v匹配一个垂直制表符 (U+000B)
\f匹配一个换页符 (U+000C)
[\b]匹配退格键
\0匹配 NULL(U+0000)字符, 不要在这后面跟其它小数,因为 \0<digits> 是一个八进制转义序列。
\cX当X是处于A到Z之间的字符的时候,匹配字符串中的一个控制符
\xhh匹配一个两位十六进制数(\x00-\xFF)表示的字符
\uhhhh匹配一个四位十六进制数表示的 UTF-16 代码单元
\u{hhhh} or \u{hhhhh}仅当设置了u标志时)匹配一个十六进制数表示的 Unicode 字符
\转义字符

组和范围

表示表达式字符的分组和范围

字符含义
x|y匹配 "x" 或 "y" 任意一个字符
[xyz] 或者 [a-c][xyz] 匹配 "x",  "y" 或者 "z" 任意一个字符, [a-c]匹配 a到c的这个范围内的任意字符
[^xyz] 或者 [^a-c]匹配不是 这些字符
(x)分组
\n其中n是一个正整数, 对正则表达式中与n括号匹配的最后一个子字符串的反向引用(计算左括号), 例如,/apple(,)\sorange\1/ 匹配 “apple,orange,cherry,peach” 中的 "apple,orange,", 其中 \1 引用了 之前使用 () 捕获的
(?<Name>x)具名捕获, 匹配"x"并将其存储在返回的匹配项的groups属性中,该属性位于<Name>指定的名称下。尖括号(< 和 >) 用于组名,例如'web-doc'.match(/-(?<customName>\w)/).groups   // {customName: "d"} 
(?:x)非捕获组, 匹配 “x”,但不记得匹配。不能从结果数组的元素中收回匹配的子字符串

量词

表示匹配的字符或表达式的数量。

字符含义
x*将前面的项“x”匹配0次或更多次
x+将前一项“x”匹配1次或更多次
x?将前面的项“x”匹配0或1次
x{n}其中“n”是一个正整数,与前一项“x”的n次匹配
x{n,}其中,“n”是一个正整数,与前一项“x”至少匹配“n”次
x{n,m}其中,“n”是0或一个正整数,“m”是一个正整数,而m > n至少与前一项“x”匹配,最多与“m”匹配
x*?, x+?, x??, x{n}?, x{n,}?, x{n,m}?默认情况下,像 和 这样的量词是“贪婪的”,这意味着它们试图匹配尽可能多的字符串。?量词后面的字符使量词“非贪婪”:意思是它一旦找到匹配就会停止

使用范例

58. 最后一个单词的长度

image.png

function lengthOfLastWord(s: string): number {
  const regx: RegExp = /\b([^\s]*)\s*$/ig
  const res = regx.exec(s)
  return res === null ? 0 : res[1].length
};

2129. 将标题首字母大写

image.png

function capitalizeTitle(title: string): string {
  return title.toLowerCase().replace(/\b([a-z]{3,})/ig, (input, char) => {
    return char[0].toUpperCase() + char.substring(1)
  })
};

剑指 Offer 05. 替换空格

image.png

function replaceSpace(s: string): string {
  return s.replace(/\s/ig, '%20');
};

8. 字符串转换整数 (atoi)

image.png

function myAtoi(s: string): number {
    const reg = /^\s*([+-]?\d+)/
    const result = s.match(reg)
    const t:number = result ? Number(result[1]) : 0
    return Math.max(-1 * 2 ** 31, Math.min(2 ** 31 - 1 , t))
};