从JS变量构建正则表达式不起作用

5

我正在尝试使用javascript从静态文本和变量构建正则表达式。显然,我漏掉了非常基本的东西,请参见下面代码中的注释。非常感谢您的帮助:

var test_string = "goodweather";

// One regexp we just set: 
var regexp1 = /goodweather/;

// The other regexp we built from a variable + static text:
var regexp_part = "good";
var regexp2 = "\/" + regexp_part + "weather\/";

// These alerts now show the 2 regexp are completely identical:
alert (regexp1);
alert (regexp2);

// But one works, the other doesn't ??
if (test_string.match(regexp1))
  alert ("This is displayed.");

if (test_string.match(regexp2))
  alert ("This is not displayed.");

5个回答

18

首先,回答这个问题:

其他答案几乎都是正确的,但它们忽略了当要匹配的文本包含一个字面上的反斜杠时会发生什么 (即当: regexp_part 包含一个字面上的反斜杠)。例如,当 regexp_part 等于:"C:\Windows" 时会发生什么?在这种情况下,建议的方法不能按预期工作(产生的正则表达式变成:/C:\Windows/,其中 \W 被错误地解释为非单词字符类)。正确的解决方案是首先转义 regexp_part 中的任何反斜杠(所需的正则表达式实际上是:/C:\\Windows/)。

以下是一个函数的示例,该函数接受一个传递的短语,并创建一个用 \b 单词边界包装的短语的正则表达式:

// Given a phrase, create a RegExp object with word boundaries.
function makeRegExp(phrase) {
    // First escape any backslashes in the phrase string.
    //  i.e. replace each backslash with two backslashes.
    phrase = phrase.replace(/\\/g, "\\\\");
    // Wrap the escaped phrase with \b word boundaries.
    var re_str = "\\b"+ phrase +"\\b";
    // Create a new regex object with "g" and "i" flags set.
    var re = new RegExp(re_str, "gi");
    return re;
}
// Here is a condensed version of same function.
function makeRegExpShort(phrase) {
    return new RegExp("\\b"+ phrase.replace(/\\/g, "\\\\") +"\\b", "gi");
}

为了更深入地理解这一点,以下是一次讨论...

深入讨论,或者说“所有这些反斜杠是怎么回事?”

JavaScript 有两种方法创建 RegExp 对象:

  1. /pattern/flags - 您可以直接指定 RegExp 字面表达式,其中模式使用一对前斜杠界定,后跟任意组合的三个模式修饰符标志:即'g' 全局匹配,'i' 忽略大小写,或 'm' 多行匹配。此类正则表达式无法动态创建。
  2. new RegExp("pattern", "flags") - 您可以通过调用 RegExp() 构造函数并将模式作为字符串(不带前斜杠分隔符)作为第一个参数以及可选的模式修饰符标志(也作为字符串)作为第二个(可选)参数来创建 RegExp 对象。此类正则表达式可以动态创建。

下面的示例演示了如何使用这两种方法之一创建一个简单的 RegExp 对象。假设我们想匹配单词"apple"。我们需要的正则表达式模式只是:apple。此外,我们希望设置所有三个修饰符标志。

示例1:简单模式没有特殊字符:apple

// A RegExp literal to match "apple" with all three flags set:
var re1 = /apple/gim;

// Create the same object using RegExp() constructor:
var re2 = new RegExp("apple", "gim");

很简单。然而,这两种方法在处理转义字符方面存在显着差异。正则表达式文字语法非常方便,因为您只需要转义正斜杠 - 所有其他字符都会直接传递给正则表达式引擎。但是,使用RegExp构造函数方法时,您将模式作为字符串传递,并且需要考虑两个级别的转义;第一个是字符串的解释,第二个是正则表达式引擎的解释。几个例子将说明这些差异。

首先考虑包含单个字面正斜杠的模式。假设我们希望以不区分大小写的方式匹配文本序列:"and/or"。所需模式为:and/or

示例2:具有一个正斜杠的模式:and/or

// A RegExp literal to match "and/or":
var re3 = /and\/or/i;

// Create the same object using RegExp() :
var re4 = new RegExp("and/or", "i");

注意,使用正则表达式文字语法时,斜杠必须转义(在前面加上一个反斜杠),因为在正则表达式文字中,斜杠具有特殊含义(它是一个特殊的元字符,用于分隔模式)。另一方面,在RegExp构造函数语法(使用字符串存储模式)中,斜杠没有任何特殊含义,不需要转义。

接下来,让我们考虑一个包含特殊\b单词边界正则表达式元序列的模式。假设我们希望创建一个正则表达式,仅匹配整个单词"apple"(这样它就不会匹配"pineapple")。模式(由正则表达式引擎看到的)需要是:\bapple\b

示例3:带有\b单词边界的模式:\bapple\b

// A RegExp literal to match the whole word "apple":
var re5 = /\bapple\b/;

// Create the same object using RegExp() constructor:
var re6 = new RegExp("\\bapple\\b");

在这种情况下,使用RegExp构造函数方法时必须转义反斜杠,因为该模式存储在字符串中,要将文本中的反斜杠转换为字符串,必须用另一个反斜杠进行转义。但是,如果使用正则表达式字面量,就不需要转义反斜杠。(请记住,在正则表达式字面量中,唯一的特殊元字符是正斜杠。)

反斜杠汤!

当我们需要匹配一个文字意义上的反斜杠时,情况变得更加有趣。假设我们想匹配文本序列:"C:\Program Files\JGsoft\RegexBuddy3\RegexBuddy.exe"。传递给正则表达式引擎处理的模式应为:C:\\Program Files\\JGsoft\\RegexBuddy3\\RegexBuddy\.exe。(请注意,匹配单个反斜杠的正则表达式模式为\\,即每个反斜杠都必须转义。)以下是如何使用两种JavaScript语法创建所需的RegExp对象的示例:

示例4:模式匹配文字意义上的反斜杠:

// A RegExp literal to match the ultimate Windows regex debugger app:
var re7 = /C:\\Program Files\\JGsoft\\RegexBuddy3\\RegexBuddy\.exe/;

// Create the same object using RegExp() constructor:
var re8 = new RegExp(
    "C:\\\\Program Files\\\\JGsoft\\\\RegexBuddy3\\\\RegexBuddy\\.exe");

因此,/ regex literal / 语法通常比 new RegExp("pattern", "flags") 方法更受欢迎-它完全避免了可能经常出现的反斜杠混乱。但是,当您需要动态创建正则表达式时(正如 OP 在此处所需),您必须使用new RegExp() 语法并处理反斜杠混乱。(一旦你掌握了它,实际上并不那么糟糕。)

RegexBuddy 能帮助解决问题!

RegexBuddy 是一款 Windows 应用程序,可以帮助解决这个反斜杠混乱的问题-它了解许多语言的正则表达式语法和转义要求,并在从应用程序粘贴时根据需要自动添加和删除反斜杠。在应用程序中,您可以以原生正则表达式格式组合和调试正则表达式。一旦正则表达式正确工作,您就可以使用许多“复制为…”选项之一导出它,以获取所需的语法。非常方便!


谈论标志,只有 g 在工作。我似乎从来没有工作过。有什么原因吗?我的正则表达式始终是大小写敏感的,即使我有“i”标志。 - Tarun

2
您应该使用RegExp构造函数来实现这一点:
var regexp2 = new RegExp(regexp_part + "weather");

这里有一个相关问题链接,可能会对你有所帮助。


1

正斜杠只是JavaScript语法,用于包含正则表达式。如果您使用普通字符串作为正则表达式,则不应将它们包含在内,因为它们将与之匹配。因此,您应该像这样构建正则表达式:

var regexp2 = regexp_part + "weather";

0

虽然这个解决方案可能对于这个具体问题来说有些过度,但如果您想以编程方式构建正则表达式,compose-regexp 可能会很有用。

使用以下方法可以解决这个具体问题:

import {sequence} from 'compose-regexp'

const weatherify = x => sequence(x, /weather/)

字符串被转义了,所以

weatherify('.')

返回

/\.weather/

但它也可以接受正则表达式

weatherify(/./u)

返回

/.weather/u

compose-regexp 支持完整的正则表达式功能,并允许从子部分构建正则表达式,这有助于代码重用和可测试性。


0

我会使用:

var regexp2 = new RegExp(regexp_part+"weather");

就像你已经做过的那样:

var regexp2 = "/goodweather/";

然后是:

test_string.match("/goodweather/")

使用字符串匹配而不是正则表达式,与您想要的匹配方式相符:

test_string.match(/goodweather/)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接