繁体   English   中英

为什么 string.replace(/\\W*/g,'_') 将所有字符放在前面?

[英]Why does string.replace(/\W*/g,'_') prepend all characters?

我一直在学习 js 中的 regexp 遇到了一个我不明白的情况。

我使用以下正则表达式对替换函数进行了测试:

/\W*/g

并期望它在字符串的开头加上并继续替换所有非单词字符。

The Number is (123)(234)

会成为:

_The_Number_is__123___234_

这将是在字符串之前,因为它至少有零个实例,然后替换所有不间断空格和非单词字符。

相反,它在每个字符之前加上并替换了所有非单词字符。

_T_h_e__N_u_m_b_e_r__i_s__1_2_3__2_3_4__

为什么这样做?

问题是\\W*的含义。 它的意思是“0 个或多个非单词字符”。 这意味着空字符串""将匹配,因为它确实是 0 个非单词字符。

所以正则表达式在字符串中的每个字符之前和末尾匹配,因此为什么所有替换都完成了。

您需要/\\W/g (替换每个单独的非单词字符)或/\\W+/g (替换每组连续的非单词字符)。

"The Number is (123)(234)".replace(/\W/g, '_')  // "The_Number_is__123__234_"
"The Number is (123)(234)".replace(/\W+/g, '_') // "The_Number_is_123_234_"

TL; 博士

  1. 如果您的目标是替换而不是插入文本,则切勿在正则表达式替换方法中使用可以匹配空字符串的模式

  2. 要替换字符串中所有单独出现的非单词字符,请使用.replace(/\\W/g, '_') (即删除匹配零次或多次出现的量化子模式的*量词)

  3. 要将字符串中的所有非单词字符替换为单个模式,请使用.replace(/\\W+/g, '_') (即,将*量词替换为+匹配一个或多个出现的量化子模式)

    注意:下面的解决方案是为 OP 更具体的要求量身定制的。

JS 正则表达式引擎将字符串解析为一系列字符和它们之间的位置 请参阅下图,其中我用连字符标记了位置:

  -T-h-e- -N-u-m-b-e-r- -i-s- -(-1-2-3-)-(-2-3-4-)-
  |||                                             |
  ||Location between T and h, etc. .............  |
  |1st symbol                                     |
start                     ->                     end

所有这些位置都可以用正则表达式进行分析和匹配

由于/\\W*/g是匹配所有非重叠出现(由于g修饰符)的0 和更多(由于*量词)非单词字符的正则表达式,因此匹配单词字符之前的所有位置 Th ,有一个使用正则表达式测试的位置,并且由于没有非字字符( h是字字符),因此返回空匹配(因为\\W*可以匹配空字符串)。

因此,您需要用_替换字符串的开头和每个非单词字符。 天真的方法是使用.replace(/\\W|^/g, '_') 但是,有一个警告:如果字符串以非单词字符开头,则不会在字符串的开头附加_

 console.log("Hi there.".replace(/\\W|^/g, '_')); // _Hi_there_ console.log(" Hi there.".replace(/\\W|^/g, '_')); // _Hi_there_

请注意,此处\\W在交替中首先出现,并且在字符串开头匹配时“获胜”:匹配空格,然后在下一次匹配迭代中找不到起始位置。

您现在可能认为您可以匹配/^|\\W/g 看这里:

 console.log("Hi there.".replace(/^|\\W/g, '_')); // _Hi_there_ console.log(" Hi there.".replace(/^|\\W/g, '_')); // _ Hi_there_

_ Hi_there_第二个结果显示了 JS 正则表达式引擎如何在替换操作期间处理零宽度匹配:一旦找到零宽度匹配(这里是字符串开头的位置),就会发生替换,并且RegExp.lastIndex属性递增,从而进行到第一个字符之后的位置! 这就是为什么第一个空格被保留,不再与\\W匹配的原因。

一种解决方案是使用不允许零宽度匹配的消费模式:

 console.log("Hi there.".replace(/^(\\W?)|\\W/g, function($0,$1) { return $1 ? "__" : "_"; })); console.log(" Hi there.".replace(/^(\\W?)|\\W/g, function($0,$1) { return $1 ? "__" : "_"; }));

您可以使用RegExp /(^\\W*){1}|\\W(?!=\\w)/g匹配一个\\W在字符串的开头或\\W不跟\\w

 var str = "The Number is (123)(234)"; var res = str.replace(/(^\\W*){1}|\\W(?!=\\w)/g, "_"); console.log(res);

您应该使用 /\\W+/g 代替。

“*”表示所有字符本身。

这是因为您正在使用*运算符。 匹配零个或多个字符。 所以每个字符之间都匹配。 如果您用/\\W+/g替换表达式,它会按您的预期工作。

这应该适合你

查找: (?=.)(?:^\\W|\\W$|\\W|^|(.)$)
替换: $1_

案例说明:

 (?= . )       # Must be at least 1 char
 (?:           # Ordered Cases:
      ^ \W          # BOS + non-word (consumes bos)
   |  \W $          # Non-word + EOS (consumes eos)
   |  \W            # Non-word
   |  ^             # BOS
   |  ( . )         # (1), Any char + EOS
      $ 
 )

请注意,这可以在没有前瞻的情况下完成
(?:^\\W|\\W$|\\W|^$)

但是,这将在空字符串上插入一个_
所以,它最终变得更加精细。
总而言之,这是一个简单的替代品。
与 Stribnez 的解决方案不同,不需要回调逻辑
在更换方面。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM