匹配多行文本
撰写时间:2024-02-29
修订时间:2024-02-29
换行的实质
对于下面的这行代码:
const src = `First line\nSecond line\nThird line`;
在终端上打印出来:
console.log(src);
终端显示:
First line
Second line
Third line
终端将打印为多行文本。这表明,多行文本只不过是在字符串的相应地方插入换行符\n
,而终端这个应用以及其他的文本编辑器,均能识别该字符,并将它们显示为换行的效果。
因此,所谓多行文本,只有一个输入源,而该输入源中出现了换行符\n
。这是学习正则表达式时考虑这类问题的正确视角。
换行符可以被匹配
换行符\n
,如同其他普通字符一样,可以被正则表达式匹配到。
let src = `First line\nSecond line\nThird line`;
let re = /\n/g;
let result;
while ((result = re.exec(src)) !== null) {
console.log(result.index);
}
终端显示:
10
22
因此,如果我们要匹配第2行及第3行的第一个单词,由于它们均在换行符\n
之后,因此可使用:
let src = `First line\nSecond line\nThird line`;
let re = /\n(\w+)/g;
let result;
while ((result = re.exec(src)) !== null) {
console.log(result[1]); // Second
// Third
}
但,如果我们同时还需要匹配第一行的首单词呢?
如果我们仅考虑到是否存在换行符\n
,因而编写:
let src = `First line\nSecond line\nThird line`;
let re = /\n?(\w+)/g;
则所有的单词都会被匹配到。因为像line
这样的单词,虽然其前面是个空格,但却可匹配前面没有换行符
的特征,因此也被匹配出来了。
应使用^
元字符来匹配一行之首,因此代码应改为:
let src = `First line\nSecond line\nThird line`;
let re = /^\w+|(?:\n(\w+))/g; // ["First", undefined]
// ["↵Second", "Second"]
// ["↵Third", "Third"]
对于上面的结果,还需额外判断数组位于第1个索引值的元素是否为undefined
。
感觉真累。
m: 多行标志
RegExp的m标志可救赎此问题。它将带有\n
换行符的输入源视为多行文本,然后元字符^
可匹配到多行中的首字符。
let src = `First line\nSecond line\nThird line`;
let re = /^\w+/gm;
let result;
while ((result = re.exec(src)) !== null) {
console.log(result[0]);
}
终端输出:
First
Second
Third
从本质上来讲,计算机只看见了一行的输入源,尽管它之间夹有换行符。但它最终屈从了人类的淫威,只好假装看见
了多行。这才有了与人类一样期待的结果。
s: dotAll标志
现在,我们要匹配:字符e, 然后是任意字符,然后是字符S
。编写以下代码:
let src = `First line\nSecond line`;
let re = /e.S/;
let result = re.exec(src);
console.log(result); // null
尽管输入源中有e\nS
的文本,但上面我们试图使用.
来匹配e
及s
之间的任意字符,则会匹配失败。因为默认情况下,匹配任意字符的元字符.
不会匹配换行符\n
。
打开RegExp的s标志,正则表达式引擎的元字符.
就可以匹配换行符\n
。
let src = `First line\nSecond line`;
let re = /e.S/s;
console.dir(re);
终端输出:
/e.s/S:
dotAll: true
flags: "s"
source: "e.S"
...
re的dotAll属性值因设置了s标志而被赋值为true。
dotAll
表示:dot is for all now
,意思是,元字符.
现在可以真正匹配任意字符了,包括换行符\n
。
现在,开始匹配:
let src = `First line\nSecond line`;
let re = /e.S/s;
let result = re.exec(src);
console.log(result); // ["e↵S"]
匹配成功。