否定后视不匹配转义字符,在转义反斜杠上失败
Negative lookbehind to not match escaped characters, fails on escaped backslash
假设我想在任何分隔符字符处拆分一个字符串,但不是转义字符,我通常可以使用负向回顾和 string.split(regex)。
例如:
const regex = /(?<!\)\,/;
'abc,def'.split(regex);
'abc\,def'.split(regex);
在 abc,def
中的 ,
处拆分,但在 abc\,def
中不拆分。这很好!
但是如果分隔符本身是反斜杠,负向回顾似乎无法按预期工作:
const regex = /(?<!\)\/;
'abc\def'.split(regex);
'abc\\def'.split(regex);
在 abc\def
和 abc\def
中的第一个 \
和 abc\def
中拆分。
我天真地以为负面回顾不会匹配前面有 \
的 \
。
参见:https://regex101.com/r/ozkZR1/1
如何在任何不与反斜杠或换行符等特殊字符分开的非转义字符处实现 string.split(正则表达式)(也应该能够转义它们)?
天真的解决方案
如果你的分隔符和你的定界符相同,你可以在分隔符之后的负面展望之上,在后面的负面展望之上:
/(?<!\)\(?!\)/
注意事项
这种方法有很多问题,我不建议用正则表达式来解决,尤其不建议让分隔符和转义符相同。
- 使用
,
作为分隔符,字段末尾的文字字符将欺骗正则表达式,例如,abc\,def
不会被拆分。
- 使用
\
作为您的分隔符和转义字符,您不能有空字段:abc,,def
将是三个字段,包括一个空字段,但 abc\def
将只是一个字段。
- 那
abc\\def
呢?在第一个字段的末尾或第二个字段的开头是否有文字 \
?无论哪种方式,我的正则表达式都不会分裂。
如果您愿意在边界处逐字禁止使用转义字符,并且不允许空字段,那么当转义符和分隔符相同时我的正则表达式将起作用,而在其他情况下您的正则表达式将起作用。
否则,我会推荐一个不同的解决方案,您从左到右解析字符串,在遇到转义符时解释它们,并在看到未转义的分隔符时拆分,这样 abc\,def
就会被拆分正确。
解决方案是反转操作:
我可以查找带分隔符的字符序列,而不是查找分隔符。因此,如果是 ,
定界符,我会寻找: ((\,)|[^,])([^,]*?(\,)?)*
: 转义逗号或非逗号字符,后跟任意数量(可能为空)的非逗号组(不情愿,所以它没有捕捉到转义的 \
)后跟一个可选的转义逗号。
let separator = ','; // get from sanitized input
separator = separator === '\' ? '\\' : separator;
const groups = new RegExp(`((\\${separator})|[^${separator}])([^${separator}]*?(\\${separator})?)+`, 'g');
let columns = line.match(groups);
这适用于 ,
以及 \
作为分隔符,不会分别在 \,
和 \
上拆分。
该表达式中最难的部分是正确转义。
假设我想在任何分隔符字符处拆分一个字符串,但不是转义字符,我通常可以使用负向回顾和 string.split(regex)。
例如:
const regex = /(?<!\)\,/;
'abc,def'.split(regex);
'abc\,def'.split(regex);
在 abc,def
中的 ,
处拆分,但在 abc\,def
中不拆分。这很好!
但是如果分隔符本身是反斜杠,负向回顾似乎无法按预期工作:
const regex = /(?<!\)\/;
'abc\def'.split(regex);
'abc\\def'.split(regex);
在 abc\def
和 abc\def
中的第一个 \
和 abc\def
中拆分。
我天真地以为负面回顾不会匹配前面有 \
的 \
。
参见:https://regex101.com/r/ozkZR1/1
如何在任何不与反斜杠或换行符等特殊字符分开的非转义字符处实现 string.split(正则表达式)(也应该能够转义它们)?
天真的解决方案
如果你的分隔符和你的定界符相同,你可以在分隔符之后的负面展望之上,在后面的负面展望之上:
/(?<!\)\(?!\)/
注意事项
这种方法有很多问题,我不建议用正则表达式来解决,尤其不建议让分隔符和转义符相同。
- 使用
,
作为分隔符,字段末尾的文字字符将欺骗正则表达式,例如,abc\,def
不会被拆分。 - 使用
\
作为您的分隔符和转义字符,您不能有空字段:abc,,def
将是三个字段,包括一个空字段,但abc\def
将只是一个字段。 - 那
abc\\def
呢?在第一个字段的末尾或第二个字段的开头是否有文字\
?无论哪种方式,我的正则表达式都不会分裂。
如果您愿意在边界处逐字禁止使用转义字符,并且不允许空字段,那么当转义符和分隔符相同时我的正则表达式将起作用,而在其他情况下您的正则表达式将起作用。
否则,我会推荐一个不同的解决方案,您从左到右解析字符串,在遇到转义符时解释它们,并在看到未转义的分隔符时拆分,这样 abc\,def
就会被拆分正确。
解决方案是反转操作:
我可以查找带分隔符的字符序列,而不是查找分隔符。因此,如果是 ,
定界符,我会寻找: ((\,)|[^,])([^,]*?(\,)?)*
: 转义逗号或非逗号字符,后跟任意数量(可能为空)的非逗号组(不情愿,所以它没有捕捉到转义的 \
)后跟一个可选的转义逗号。
let separator = ','; // get from sanitized input
separator = separator === '\' ? '\\' : separator;
const groups = new RegExp(`((\\${separator})|[^${separator}])([^${separator}]*?(\\${separator})?)+`, 'g');
let columns = line.match(groups);
这适用于 ,
以及 \
作为分隔符,不会分别在 \,
和 \
上拆分。
该表达式中最难的部分是正确转义。