Javascript中是否存在RegExp.escape函数?


442

我只想从任何可能的字符串中创建一个正则表达式。

var usersString = "Hello?!*`~World()[]";
var expression = new RegExp(RegExp.escape(usersString))
var matches = "Hello".match(expression);

有内置的方法吗?如果没有,人们会使用什么?红宝石有RegExp.escape。我觉得我不需要自己写东西,那里肯定有一些标准。谢谢!


15
只是想更新您RegExp.escape目前正在研究的优秀人才,非常欢迎任何认为自己有宝贵意见的人做出贡献。core-js和其他polyfills提供了它。
本杰明·格林鲍姆

Answers:


573

上面链接的功能不足。它无法转义^$(字符串的开头和结尾)或-,这在字符组中用于范围。

使用此功能:

function escapeRegex(string) {
    return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

乍看起来似乎没有必要,但转义-(以及^)使该函数适合于转义要插入字符类和正则表达式主体的字符。

转义/使该函数适合转义要在JS正则表达式文字中使用的字符,以供以后评估。

由于逃避任何一个都没有不利之处,因此有理由逃避以涵盖更广泛的用例。

是的,如果它不是标准JavaScript的一部分,这是令人失望的。


16
其实,我们不需要逃避/可言
索恩

28
@Paul:Perl quotemeta\Q),Python re.escape,PHP preg_quote,Ruby Regexp.quote...
bobince 2013年

13
如果要在循环中使用此函数,则最好将RegExp对象设为自己的变量var e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;,然后将函数设为。return s.replace(e, '\\$&');这样,您只需实例化RegExp一次。
styfle

15
禁止扩展内置对象的标准参数在这里适用,不是吗?如果将来的ECMAScript版本提供RegExp.escape的实现与您的实现不同,会发生什么情况?不将此功能附加到任何东西上会更好吗?
Mark Amery

15
bobince不在乎eslint的意见
bobince

113

对于使用lodash的任何人,从v3.0.0开始,都内置了_.escapeRegExp函数:

_.escapeRegExp('[lodash](https://lodash.com/)');
// → '\[lodash\]\(https:\/\/lodash\.com\/\)'

而且,如果您不想使用完整的lodash库,则可能需要该函数


6
甚至还有一个这样的npm包!npmjs.com/package/lodash.escaperegexp
Ted Pennings

1
这将导入实际上不需要这么简单的代码的大量负载。使用bobince的答案...对我有用,它的加载字节比lodash版本少很多!
罗布·埃文斯

6
@RobEvans我的答案从“对于使用lodash的任何人”开始,我什至提到您需要该escapeRegExp功能。
gustavohenke '17

2
@gustavohenke抱歉,我应该更加清楚一些,我在“仅此功能”中包含了链接到的模块,这就是我要评论的内容。如果您看一看,实际上应该是一个带有单个正则表达式的单个函数的代码很多。同意如果您已经在使用lodash,则可以使用它,否则请使用其他答案。抱歉,不清楚的评论。
Rob Evans

2
@maddob我看不到您提到的\ x3:我的转义字符串看起来不错,正是我所期望的
Federico Fissore

42

这里的大多数表达式解决单个特定的用例。

没关系,但我更喜欢“始终有效”的方法。

function regExpEscape(literal_string) {
    return literal_string.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&');
}

对于正则表达式中的以下任何一种用法,这将“完全转义”文字字符串:

  • 插入正则表达式。例如new RegExp(regExpEscape(str))
  • 插入字符类。例如new RegExp('[' + regExpEscape(str) + ']')
  • 插入整数计数说明符。例如new RegExp('x{1,' + regExpEscape(str) + '}')
  • 在非JavaScript正则表达式引擎中执行。

涵盖的特殊字符:

  • -:在字符类中创建字符范围。
  • [/ ]:开始/结束字符类。
  • {/ }:开始/结束一个数字说明符。
  • (/ ):开始/结束组。
  • */ +/ ?:指定重复类型。
  • .:匹配任何字符。
  • \:转义字符,然后启动实体。
  • ^:指定匹配区域的开始,并否定字符类中的匹配。
  • $:指定匹配区域的结尾。
  • |:指定交替。
  • #:以自由间距模式指定注释。
  • \s:在自由间距模式下被忽略。
  • ,:分隔数值说明符中的值。
  • /:开始或结束表达式。
  • ::完成特殊的组类型以及Perl样式的字符类的一部分。
  • !:否定零宽度组。
  • </ =:零宽度组规格的一部分。

笔记:

  • /在任何形式的表达中都不是必须的。但是,它可以防止有人(颤抖)这样做eval("/" + pattern + "/");
  • , 确保如果字符串在数字说明符中应为整数,则它将正确地引起RegExp编译错误,而不是无提示地编译错误。
  • #\s不需要在JavaScript中进行转义,而是在许多其他方式中进行转义。如果正则表达式以后将传递给另一个程序,它们将在此处转义。

如果您还需要对正则表达式进行将来的校对,以防可能增加JavaScript regex引擎功能,则建议使用更多偏执狂:

function regExpEscapeFuture(literal_string) {
    return literal_string.replace(/[^A-Za-z0-9_]/g, '\\$&');
}

该函数会转义每个字符,除非明确保证将来的正则表达式中不会使用这些字符。


对于真正喜欢卫生的人,请考虑以下情况:

var s = '';
new RegExp('(choice1|choice2|' + regExpEscape(s) + ')');

可以在JavaScript中很好地编译,但不能以其他方式编译。如果打算传递给其他口味,s === ''则应独立检查的空值情况,如下所示:

var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')');

1
/不需要它的转义[...]字符类。
Dan Dascalescu

1
其中大多数不需要逃脱。“在字符类中创建字符范围” -您永远不会在字符串内的字符类中。“在自由间距模式下指定注释,在自由间距模式下忽略”-JavaScript不支持。“在数字说明符中分隔值” -您永远不会在字符串内使用数字说明符。同样,您不能在命名规范中写入任意文本。“开始或结束表达式” -无需转义。评估不是一个案例,因为它将需要更多的转义。[将在下
一条

“完成特殊的组类型和Perl样式的字符类的一部分” -似乎在javascript中不可用。“否定零宽度组,零宽度组规范的一部分” -字符串中永远没有组。
Qwertiy

@Qwertiy这些额外的转义的原因是为了消除可能在某些用例中引起问题的边缘情况。例如,此函数的用户可能希望将转义的正则表达式字符串作为组的一部分插入到另一个正则表达式中,甚至可以用Java语言以外的其他语言来使用。该函数不会做出“我永远不会成为字符类的一部分”之类的假设,因为它的含义是通用的。有关YAGNI的更多方法,请参见此处的其他任何答案。
Pi Marillion

很好。为什么_无法逃脱?是什么确保它以后不会成为正则表达式语法?
madprops 17-10-29


21

在jQueryUI的自动完成小部件(版本1.9.1)中,他们使用略有不同的正则表达式(第6753行),这是将正则表达式与@bobince方法结合使用。

RegExp.escape = function( value ) {
     return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}

4
唯一的区别是它们转义,(不是元字符),#而空格仅在自由间距模式下才重要(JavaScript不支持)。但是,他们确实正确地避免了不使用正斜杠。
Martin Ender

18
如果您想重用jquery UI的实现而不是将代码粘贴到本地,请使用$.ui.autocomplete.escapeRegex(myString)
斯科特·斯塔福德,

2
lodash也有这个,_。escapeRegExp和npmjs.com/package/lodash.escaperegexp
Ted Pennings

v1.12一样,确定!
彼得·克劳斯

13

没有什么可以阻止您仅转义每个非字母数字字符的:

usersString.replace(/(?=\W)/g, '\\');

在执行操作时,您会失去一定程度的可读性,re.toString()但会获得很多简化(和安全性)。

根据ECMA-262,在一方面,正则表达式“的语法的字符”总是非字母数字,使得结果是安全的,和特殊的转义序列(\d\w\n)总是字母数字,使得没有假控制逃逸会产生。


简单有效。我喜欢这个比接受的答案好得多。对于(确实)旧的浏览器,.replace(/[^\w]/g, '\\$&')将以相同的方式工作。
Tomas Langkaas

6
这在Unicode模式下失败。例如,new RegExp('🍎'.replace(/(?=\W)/g, '\\'), 'u')抛出异常是因为分别\W匹配代理对的每个代码单元,从而导致无效的转义码。
阿列克谢·列别捷夫

1
另一种选择:.replace(/\W/g, "\\$&");
Miguel Pynto

@AlexeyLebedev答案是否已固定为处理Unicode模式?还是在保持这种简单性的同时,还有其他解决方案呢?
johny为什么


6

这是一个较短的版本。

RegExp.escape = function(s) {
    return s.replace(/[$-\/?[-^{|}]/g, '\\$&');
}

这包括非元字符%&',和,,但JavaScript的正则表达式规范允许这样做。


2
我不会使用这个“更短的”版本,因为字符范围隐藏了字符列表,这使乍看之下更难验证正确性。
nhahtdh 2014年

@nhahtdh我可能也不会,但是将其发布在此处以供参考。
kzh14 2014年

@kzh:发布“信息”比发布理解的帮助小。您是否同意我的答案更清楚?
Dan Dascalescu 2014年

至少.是错过了。和()。或不?[-^很奇怪 我不记得那里有什么。
Qwertiy

这些都在指定范围内。
kzh


3

不仅要转义会导致正则表达式出现问题的字符(例如:黑名单),还不考虑使用白名单。这样,除非每个字符都匹配,否则将被视为已污染。

对于此示例,假定以下表达式:

RegExp.escape('be || ! be');

这会将字母,数字和空格列入白名单:

RegExp.escape = function (string) {
    return string.replace(/([^\w\d\s])/gi, '\\$1');
}

返回值:

"be \|\| \! be"

这可以使不需要转义的字符转义,但这不会妨碍您的表情(也许会受到一些时间上的惩罚-但出于安全考虑,这是值得的)。


他的答案与@filip的答案不同吗?stackoverflow.com/a/40562456/209942
约翰尼为何

3
escapeRegExp = function(str) {
  if (str == null) return '';
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

1

其他答案中的函数对于转义整个正则表达式是过大的(它们对于转义正则表达式的某些部分可能有用,这些正则表达式随后将被连接成更大的正则表达式)。

如果你逃避整个正则表达式,并用它完成后,报价是要么独立的元字符(.?+*^$|\),或者开始做某件事(([{)是所有你需要:

String.prototype.regexEscape = function regexEscape() {
  return this.replace(/[.?+*^$|({[\\]/g, '\\$&');
};

是的,令人失望的是,JavaScript没有这样的内置函数。


假设您将用户输入转义(text)next并将其插入:(?:+ input + )。您的方法将给出(?:\(text)next)无法编译的结果字符串。请注意,这是相当合理的插入,而不是像re\+输入+ 这样的疯狂输入re(在这种情况下,程序员可能因做愚蠢的行为而受到指责)
nhahtdh 2014年

1
@nhahtdh:我的回答专门提到转义整个正则表达式并“完成”,而不是正则表达式的部分(或将来的部分)。请撤消反对票吗?
Dan Dascalescu 2014年

很少会逃脱整个表达式-有字符串操作,如果您要使用文字字符串,它比regex快得多。
nhahtdh 2014年

这并不是说它不正确- \应该转义,因为您的正则表达式将\w保持不变。另外,JavaScript似乎不允许尾随),至少这是Firefox抛出错误的原因。
nhahtdh 2014年

1
请说明有关关闭的部分)
nhahtdh 2014年

1

另一种(更安全)的方法是使用unicode转义格式转义所有字符(而不仅仅是我们目前知道的一些特殊字符)\u{code}

function escapeRegExp(text) {
    return Array.from(text)
           .map(char => `\\u{${char.charCodeAt(0).toString(16)}}`)
           .join('');
}

console.log(escapeRegExp('a.b')); // '\u{61}\u{2e}\u{62}'

请注意,您需要传递u标记才能使此方法起作用:

var expression = new RegExp(escapeRegExp(usersString), 'u');

1

曾经有过并且将永远有12个元字符需要转义
才能视为文字。

无关紧要的是,将转义的字符串插入平衡的
regex包装器中并附加后,该字符串做什么都没有关系。

使用此字符串替换

var escaped_string = oldstring.replace( /[\\^$.|?*+()[{]/g, '\\$&' );

]呢?
Thomasleveil
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.