代码之家  ›  专栏  ›  技术社区  ›  Andrew Ensley

javascript:negative lookback等价物?

  •  117
  • Andrew Ensley  · 技术社区  · 16 年前

    有没有办法达到 negative lookbehind 在javascript正则表达式中?我需要匹配一个不以特定字符集开头的字符串。

    如果在字符串的开头找到匹配的部分,那么我似乎找不到一个这样做而不会失败的regex。否定的lookbehinds似乎是唯一的答案,但javascript没有。

    编辑: 这是我想工作的regex,但它不:

    (?<!([abcdefg]))m

    所以它会与“吉姆”或“M”中的“M”匹配,但不会与“果酱”匹配。

    11 回复  |  直到 6 年前
        1
  •  30
  •   wp78de    6 年前

    Lookbehind Assertions 得到了 accepted 进入 ECMAScript specification 2018。这已在中实现 V8 shipped without flags with Google Chrome v62 而在 Node.js v6 behind a flag and v9 without a flag . 因此,如果您是为一个只使用Chrome的环境(例如 Electron ) Node ,你今天就可以开始使用lookbehinds了!

    正查找用法:

    console.log(
      "$9.99  €8.47".match(/(?<=\$)\d+(\.\d*)?/) // Matches "9.99"
    );

    负查找用法:

    console.log(
      "$9.99  €8.47".match(/(?<!\$)\d+(?:\.\d*)/) // Matches "8.47"
    );

    其他平台上的支持:

        2
  •  77
  •   neaumusic    6 年前

    因为javascript支持 negative lookahead 一种方法是:

    1. 反转输入字符串

    2. 与反向正则表达式匹配

    3. 反转并重新格式化匹配项


    const reverse = s => s.split('').reverse().join('');
    
    const test = (stringToTests, reversedRegexp) => stringToTests
      .map(reverse)
      .forEach((s,i) => {
        const match = reversedRegexp.test(s);
        console.log(stringToTests[i], match, 'token:', match ? reverse(reversedRegexp.exec(s)[0]) : 'Ø');
      });
    

    例1:

    以下是安德鲁·恩斯利的问题:

    test(['jim', 'm', 'jam'], /m(?!([abcdefg]))/)
    

    输出:

    jim true token: m
    m true token: m
    jam false token: Ø
    

    例2:

    在@neaumusic注释后(匹配 max-height 但不是 line-height ,象征是 height ):

    test(['max-height', 'line-height'], /thgieh(?!(-enil))/)
    

    输出:

    max-height true token: height
    line-height false token: Ø
    
        3
  •  48
  •   nhahtdh Pankaj Wadhwa    10 年前

    假设你想找到所有 int 没有先于 unsigned :

    支持负面观察:

    (?<!unsigned )int
    

    不支持负面观察:

    ((?!unsigned ).{9}|^.{0,8})int
    

    基本上的想法是抓住前面的n个字符,排除与前面的负向匹配,但也要匹配没有前面n个字符的情况。(其中n是后面看的长度)。

    所以有问题的正则表达式是:

    (?<!([abcdefg]))m
    

    将转换为:

    ((?!([abcdefg])).|^)m
    

    您可能需要利用捕获组来找到您感兴趣的字符串的确切位置,或者您希望用其他内容替换特定的部分。

        4
  •  41
  •   Jason S    10 年前

    Mijoja的策略适用于您的特定案例,但不适用于一般情况:

    js>newString = "Fall ball bill balll llama".replace(/(ba)?ll/g,
       function($0,$1){ return $1?$0:"[match]";});
    Fa[match] ball bi[match] balll [match]ama
    

    这里有一个例子,目标是匹配一个双L,但如果前面加上“ba”,则不匹配。注意单词“balll”--真正的lookback应该抑制前2个l,但匹配第2对。但是,通过匹配前2个l,然后将该匹配忽略为假阳性,regexp引擎将从 结束 并忽略假阳性中的任何字符。

        5
  •  34
  •   Mijoja    16 年前

    使用

    newString = string.replace(/([abcdefg])?m/, function($0,$1){ return $1?$0:'m';});
    
        6
  •  9
  •   Alan Moore Chris Ballance    8 年前

    可以通过取消角色集来定义非捕获组:

    (?:[^a-g])m
    

    …每一个 m 不是 前面有任何一个字母。

        7
  •  1
  •   Homer Simpson    9 年前

    按照mijoja的想法,并借鉴jasons所暴露的问题,我有这个想法;我检查了一下,但对自己不太确定,所以由比我更精通JS regex的人进行验证会很好:)

    var re = /(?=(..|^.?)(ll))/g
             // matches empty string position
             // whenever this position is followed by
             // a string of length equal or inferior (in case of "^")
             // to "lookbehind" value
             // + actual value we would want to match
    
    ,   str = "Fall ball bill balll llama"
    
    ,   str_done = str
    ,   len_difference = 0
    ,   doer = function (where_in_str, to_replace)
        {
            str_done = str_done.slice(0, where_in_str + len_difference)
            +   "[match]"
            +   str_done.slice(where_in_str + len_difference + to_replace.length)
    
            len_difference = str_done.length - str.length
                /*  if str smaller:
                        len_difference will be positive
                    else will be negative
                */
    
        }   /*  the actual function that would do whatever we want to do
                with the matches;
                this above is only an example from Jason's */
    
    
    
            /*  function input of .replace(),
                only there to test the value of $behind
                and if negative, call doer() with interesting parameters */
    ,   checker = function ($match, $behind, $after, $where, $str)
        {
            if ($behind !== "ba")
                doer
                (
                    $where + $behind.length
                ,   $after
                    /*  one will choose the interesting arguments
                        to give to the doer, it's only an example */
                )
            return $match // empty string anyhow, but well
        }
    str.replace(re, checker)
    console.log(str_done)
    

    我的个人输出:

    Fa[match] ball bi[match] bal[match] [match]ama
    

    原则是呼叫 checker 在字符串中任意两个字符之间的每个点上,只要该位置是以下内容的起始点:

    ---任何不需要的大小的子字符串(这里 'ba' 因此 .. )(如果知道这个尺寸,否则可能会更难做到)

    ------或者比这个小,如果它是字符串的开头: ^.?

    接下来,

    ——实际需要寻找的东西(这里 'll' )

    在每次呼叫时 棋盘格 ,将有一个测试来检查之前的值 ll 不是我们不想要的( !== 'ba' );如果是这样,我们调用另一个函数,它必须是这个函数。( doer )这将对str进行更改,如果目的是进行更改,或者更一般地说,将输入必要的数据,以手动处理扫描结果。 str .

    这里我们更改字符串,因此我们需要保留长度差的跟踪,以便偏移 replace ,所有计算依据 STR 它本身不会改变。

    因为基元字符串是不可变的,所以我们可以使用变量 STR 为了存储整个操作的结果,但是我认为已经被替换复杂的示例将更清晰地显示为另一个变量。( str_done )

    我想,就性能而言,这肯定是相当苛刻的:所有那些毫无意义的“换成”, this str.length-1 时代,再加上这里的手工替代实干,这意味着很多切片… 可能在上面这个特定的例子中,我们可以通过将字符串只切割一次,在我们想要插入的地方将字符串切割成片段来进行分组。 [match] .join() 用它 [火柴] 本身。

    另一件事是,我不知道它将如何处理更复杂的情况,也就是说,伪查找的复杂值…长度可能是最有问题的数据。

    而且,在 棋盘格 ,如果$behind存在多种可能的非保证值,我们将不得不使用另一个regex(将在外部缓存(创建)对其进行测试。 棋盘格 最好避免在每次调用时创建相同的regex对象 棋盘格 )知道这是否是我们想要避免的。

    希望我已经清楚了,如果不犹豫的话,我会尽力的。:)

        8
  •  0
  •   Curtis Yallop    8 年前

    这有效地做到了

    "jim".match(/[^a-g]m/)
    > ["im"]
    "jam".match(/[^a-g]m/)
    > null
    

    搜索和替换示例

    "jim jam".replace(/([^a-g])m/g, "$1M")
    > "jiM jam"
    

    请注意,负查找字符串的长度必须为1个字符,这样才能工作。

        9
  •  0
  •   Traxo    7 年前

    用你的箱子, 如果你想替换 m 使用某些内容,例如将其转换为大写 M ,可以取消捕获组中的设置。

    比赛 ([^a-g])m $1M

    "jim jam".replace(/([^a-g])m/g, "$1M")
    \\jiM jam
    

    ([^a-g]) 将匹配任何字符而不是( ^ a-g 范围,并将其存储在第一个捕获组中,以便您可以使用 $1 .

    所以我们发现 im 在里面 jim 换成 iM 结果是 jiM .

        10
  •  -1
  •   Techsin    11 年前

    /(?![abcdefg])[^abcdefg]m/gi 是的,这是个骗局。

        11
  •  -1
  •   Curtis Yallop    8 年前

    这可能会有所帮助,具体取决于上下文:

    这与Jim中的M相匹配,但不是Jam:

    "jim jam".replace(/[a-g]m/g, "").match(/m/g)