我只想用任何可能的字符串创建一个正则表达式。
1 2 3
| var usersString ="Hello?!*`~World()[]";
var expression = new RegExp(RegExp.escape(usersString))
var matches ="Hello".match(expression); |
有内置的方法吗?如果没有,人们会用什么?Ruby有RegExp.escape。我觉得我不需要自己写,那里一定有标准的东西。谢谢!
- 只是想向大家介绍一下RegExp.escape目前正在研究的优秀人物,任何认为他们有价值的意见的人都非常欢迎做出贡献。核心JS和其他Polyfill提供了它。
- 根据此答案的最新更新,此建议被拒绝:请参阅问题
上面链接的函数不足。它无法退出用于范围的字符组中的^或$(字符串的开始和结束)或-。
使用此功能:
1 2 3
| RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}; |
虽然乍一看似乎不需要,但转义-和^使函数适合转义插入到字符类和regex主体中的字符。
转义/使函数适合转义在JS regex文本中使用的字符,以便以后进行eval。
由于不存在逃避其中任何一个的不利之处,所以逃避以涵盖更广泛的用例是有意义的。
是的,这不是标准JavaScript的一部分,这是一个令人失望的失败。
- $&是做什么的?
- @spiningarrow:它表示整个匹配的字符串,就像许多其他regex系统中的"group 0"。doc
- 在编辑之前,我相信原始答案是正确的。我很确定不需要在字符类中转义正斜杠。它看起来没什么害处,但不是必需的。
- @是的,这两种变体在JS语法中都很好。
- 事实上,我们根本不需要逃离/。
- @Bobince这是预期的行为吗:regexp.escape('a.b')=='a.b',我期望'a \.b'(escape""和escape".")?
- @Radu:你有字符串文字问题,'a\.b'=='a.b'—)
- 顺便说一句,注意调试器控制台:例如,firefox和chrome都以伪文字形式显示字符串a\.b,这是一种误导,因为它不是该值的有效字符串文字(应该是"a\\.b")。感谢您不必要的额外混乱,浏览器。
- "这不是标准JavaScript的一部分,这是一个令人失望的失败"。什么语言有这样的东西?
- @paul:perl-quotemeta(\Q),python-re.escape,php-preg_quote,ruby-Regexp.quote…
- 如果要在循环中使用此函数,最好将regexp对象设置为自己的变量var e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;,然后将函数设置为return s.replace(e, '\\$&');,这样您只需实例化一次regexp。
- 你不需要逃避"—"。当您转义"["时,"-"不在字符类中,并且没有特殊含义。"/"也不是必需的。
- 嗨,这可以扩展为也转义双引号字符(")?
- @Shaggydog:当然可以,但我想不出一个地方,在regex语法中"是特殊的,所以我不确定会有什么好处。
- @shaggydog:你说的是javascript字符串文字转义。这和正则表达式的转义是不同的。它们都使用反斜杠,但其他的规则则完全不同。(如果在字符串文字中的regex中有一个字符串,那么必须使用两种类型的转义,一种接一种。)
- 反对增加内置对象的标准参数适用于这里,不是吗?如果未来版本的ecmascript提供的RegExp.escape的实现与您的不同,会发生什么?这个函数不附加到任何东西上不是更好吗?
- 实际上,不需要在字符类中转义/,不过最好是转义它以容纳一些编辑器。看看这个问题。
- 这对小数点无效。RegExp("1.3")返回/1.3/完全不能接受。当输入包含小数点的数字时,pi marillion下面的答案工作得很好。
- @瑞德:????您似乎调用了RegExp构造函数而不是RegExp.escape构造函数…
- 在"敌对"的通用函数中,您可能希望通过执行String(s).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');来保护自己不受javascript输入的影响,例如,当字符串是一个数字时,这会有所帮助。
- "因为逃走他们两个都没有坏处…"可读性降低。
- eslint默认使用此regexp引发错误(no-useless-escape):不必要的转义符:/
- 波宾斯不关心埃斯林的意见。
- 表达式可以简化为:/[$(-+.\/?[-^{|}]/(保存5个字符)。你不需要逃离-,因为你已经逃离[和],这意味着不会有字符组。另外,有两个字符序列可以写为范围。一个在(和+之间(40到43),另一个在[和^之间(91到94)。
- 但也许你想转义字符,把它们放在字符范围内。在某些特殊情况下,我宁愿无害地过多利用环境,也不愿逃避环境,造成问题。就我个人而言,我更愿意在这里看到这些角色;我们不玩代码高尔夫。
- 为什么不包括双引号本身,我的意思是把"转换成"和'转换成\'?str.replace(/[\-\[\]\/\{\}\(\)"\'\*\+\?\.\\\^\$\|]/g,"\\$&");
- @jo&227;opmentelferreira很好,引号对regex没有特殊意义(见上文Shaggydog的评论)。
- @bobince对于我来说,如果您将此函数用作handlebarsjs助手或任何其他呈现引擎,则转义引号非常重要,例如,如果我使用handlebarsjs来呈现具有var JSstr = '{{{myStr}}}';其中myStr ="I'm here"的JS文件,则转义引号非常重要。如果我不逃避报价,我会得到var JSstr = 'I'm here'。但我知道这是一个非常特殊和具体的情况。
- @jo&227;opmentelferreira,这不是regex转义,这是javascript字符串文字转义。这些语法的规则是不同的,不兼容的;对myStr应用regex转义符将不会使结果正确,即使引号被转义。如果要将字符串写入字符串文字中的regex,则需要首先对其进行regex转义,然后字符串文字转义结果(例如,反斜杠以四倍反斜杠结束)。
- 因为正确地进行嵌套转义很困难,而且错误的结果非常严重(跨站点脚本漏洞),所以将数据注入到JavaScript代码中通常是一个坏主意。一般来说,最好将内容写入数据属性(如,使用手柄的普通HTML转义),然后从静态JS中读取该属性的内容。
- @bobine只是一个简短的问题:如果一切都是服务器端的,比如在HTML中嵌入标记,那么如何将数据注入到带有手柄的javascript中是一个坏主意呢?
- @jo&227;opmentelferreira如果您注入的任何数据来自应用程序外部,那么提供数据的任何人都可以让自己选择的代码在使用应用程序输出的任何其他人的浏览器上运行,从而允许他们做用户在您的站点上可以做的任何事情。这是跨站点脚本,是当今网络上最严重、最普遍的安全问题之一。
- 令人失望的是,无论是几年还是以后的许多其他改进…
对于任何使用lodash的用户,因为v3.0.0 a uuu.escaperegexp函数是内置的:
1 2
| _.escapeRegExp('[lodash](https://lodash.com/)');
// → '\[lodash\]\(https:\/\/lodash\.com\/\)' |
而且,如果您不想需要完整的lodash库,您可能只需要这个功能!
- 甚至还有一个NPM包!npmjs.com/package/lodash.escaperegexp公司
- 请注意,escapeRegexp函数lodash还将x3添加到字符串的开头,不确定原因。
- 这会导入大量的代码,而这些代码对于如此简单的事情来说确实不需要存在。用波因斯的答案…对我来说是有效的,加载的字节比lodash版本少很多!
- @Robevans我的答案是"对于任何使用lodash的人",我甚至提到您只需要escapeRegExp功能。
- @古斯塔沃恩克很抱歉,我应该稍微清楚一点,我在你的"只是那个功能"中加入了链接到的模块,这就是我所评论的。如果你看一看,它是一个相当多的代码,实际上应该是一个包含一个regexp的函数。同意,如果你已经在使用罗达什,那么使用它是有意义的,但另外使用另一个答案。对不清楚的评论深表歉意。
- @Maddob我看不到你提到的那句话:我的转义字符串看起来不错,正是我所期望的
这里的大多数表达式解决单个特定用例。
没关系,但我更喜欢"总是有效"的方法。
1 2 3
| 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引擎功能的潜在添加,我建议使用更偏执的:
1 2 3
| function regExpEscapeFuture(literal_string) {
return literal_string.replace(/[^A-Za-z0-9_]/g, '\\$&');
} |
此函数对每个字符进行转义,除了那些明确保证在以后的正则表达式风格中不用于语法的字符。
对于真正的卫生爱好者,考虑一下这个边缘案例:
1 2
| var s = '';
new RegExp('(choice1|choice2|' + regExpEscape(s) + ')'); |
这应该可以在JavaScript中很好地编译,但不会以其他一些方式编译。如果打算换口味,应单独检查s === ''的空盒,如:
1 2
| var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')'); |
- /不需要在[...]字符类中进行转义。
- 大部分都不需要逃跑。"在一个字符类中创建一个字符范围"—您永远不在字符串内的字符类中。"在自由间距模式下指定注释,在自由间距模式下忽略"—在javascript中不支持"。在数字说明符中分隔值"—在字符串内部,您永远不在数字说明符中。此外,您不能在命名规范中编写任意文本。"开始或结束表达式"—无需转义。Eval不是这样的,因为它需要更多的逃跑。[将在下一条评论中继续]
- 完成特殊的组类型和部分Perl样式的字符类-似乎在JavaScript中不可用。否定零宽度组,这是零宽度组规范的一部分"-字符串中永远没有组。
- @问题是,这些额外逃逸的原因是为了消除在某些用例中可能导致问题的边缘情况。例如,此函数的用户可能希望将转义的regex字符串作为组的一部分插入到另一个regex中,或者甚至用于除javascript之外的其他语言。该函数不做"我永远不会成为角色类的一部分"这样的假设,因为它是通用的。要了解更多的雅格尼方法,请参阅这里的任何其他答案。
- 很好。为什么没有逃走?什么确保它以后可能不会成为regex语法?
- @pimarillion:在Bobince回答的注释中,用户styfle建议在循环中使用,首先创建转义字符串的regexp对象:var e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;,然后函数类似于return s.replace(e, '\\$&');。为了避免手动转义转义字符串,我想首先使用原始函数regExpEscape来转义转义字符串(ees),然后使用e = new RegExp(ees,"g")和function regExpEscapeFast(literal_string) { return literal_string.replace(e, '\\$&');}。我做不到。如何正确地转义转义字符串?
在jqueryui的autocomplete小部件(1.9.1版)中,它们使用了稍微不同的regex(第6753行),这里是结合@bobine方法的正则表达式。
1 2 3
| RegExp.escape = function( value ) {
return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");
} |
- 唯一的区别是,它们避开了,(不是元字符)、#和空白,而空白只在自由间距模式下起作用(javascript不支持)。然而,他们确实正确地避免了正斜杠。
- 如果要重用jquery ui的实现而不是在本地粘贴代码,请使用$.ui.autocomplete.escapeRegex(myString)。
- 洛达什也有这个。escaperegexp和npmjs.com/package/lodash.escaperegexp
- 1.12同样,好的!
Mozilla Developer Network的正则表达式指南提供了这个转义函数:
1 2 3
| function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
} |
- 他们为什么要逃出江户记1〔3〕?对于Perl的lookahead正则表达式(?=)来说,这是很有用的,但是如果您不使用?,就可以使用了。
- @丹达斯卡莱苏,你说得对。MDN页面已更新,不再包含=。
没有什么可以阻止您对每个非字母数字字符进行转义:
1
| usersString.replace(/(?=\W)/g, '\'); |
在执行re.toString()时,您会失去一定程度的可读性,但您会赢得大量的简单性(和安全性)。
根据ECMA-262,一方面,正则表达式"语法字符"总是非字母数字的,这样结果是安全的,特殊转义序列(\d、\w、
总是字母数字的,这样就不会产生错误的控制转义。
- 简单有效。我更喜欢这个答案。对于(真正的)老浏览器,.replace(/[^\w]/g, '\\$&')也会以同样的方式工作。
- 这在Unicode模式下失败。例如,new RegExp('??'.replace(/(?=\W)/g, '\\'), 'u')抛出异常,因为\W分别匹配代理对的每个代码单元,从而导致无效的转义代码。
- 备选方案:.replace(/\W/g,"\\$&");。
在https://github.com/benjamingr/rexexp.escape/上有一个关于regexp.escape的ES7提案,在https://github.com/ljharb/regexp.escape上有一个polyfill。
- 看起来这并没有进入ES7。它看起来也被拒绝了,转而寻找一个模板标签。
这是一个较短的版本。
1 2 3
| RegExp.escape = function(s) {
return s.replace(/[$-\/?[-^{|}]/g, '\\$&');
} |
这包括%、&、'和,的非元字符,但javascript regexp规范允许这样做。
- 我不会使用这个"较短"的版本,因为字符范围隐藏了字符列表,这使得第一眼就很难验证正确性。
- @NHAHTDH我也可能不会,但它是发布在这里的信息。
- @kzh:发布"为了信息"比发布理解要少。你不同意我的回答更清楚吗?
- 至少,.被漏掉了。和()。或不是?[-^很奇怪。我不记得那里有什么。
- 它们在指定的范围内。
与其只转义在正则表达式中会引起问题的字符(例如黑名单),不如考虑使用白名单。这样,除非每个字符匹配,否则都被视为污点。
对于本例,假设使用以下表达式:
1
| RegExp.escape('be || ! be'); |
此白名单列出了字母、数字和空格:
1 2 3
| RegExp.escape = function (string) {
return string.replace(/([^\w\d\s])/gi, '\\$1');
} |
返回:
这可能会转义不需要转义的字符,但这不会妨碍您的表达式(可能会受到一些轻微的时间惩罚,但为了安全起见,这是值得的)。
xregexp具有一个转义功能:
XRegExp.escape('Escaped? <.>');
// -> 'Escaped\?\ <\.>'
更多信息:http://xregexp.com/api/escape
1 2 3 4
| escapeRegExp = function(str) {
if (str == null) return '';
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}; |
其他答案中的函数对于转义整个正则表达式来说是多余的(它们可能对转义正则表达式的某些部分很有用,这些部分稍后将连接到更大的regexp中)。
如果您退出整个regexp并完成它,则只需引用独立的元字符(.、?、+、*、^、$、|、\,或启动某个元字符((、[、{:
1 2 3
| String.prototype.regexEscape = function regexEscape() {
return this.replace(/[.?+*^$|({[\\]/g, '\\$&');
}; |
是的,让人失望的是,javascript没有这样的内置函数。
- 假设您退出用户输入(text)next,并将其插入:(?:+input+)。您的方法将给出无法编译的结果字符串(?:\(text)next)。注意,这是一个相当合理的插入,而不是像re\+input+re这样的疯狂插入(在这种情况下,程序员可能会因为做了一些愚蠢的事情而受到指责)。
- @nhahdh:我的答案特别提到了对整个正则表达式的转义和对它们的"执行",而不是对regexps的部分(或将来的部分)。请取消投票?
- 很少会出现这样的情况,您会转义整个表达式-有字符串操作,如果您想使用文本字符串,它比regex快得多。
- 这并没有提到这是不正确的-应该转义\,因为您的regex将保留\w的完整性。另外,javascript似乎不允许跟踪),至少这是Firefox抛出错误的原因。
- 我在回答中逃脱了埃多克斯一〔16〕。谢谢!
- 请说明关闭)的部分。
- 即使某些方言允许使用右大括号,也应该避免使用右大括号。我记得,这是一个扩展,而不是规则。