javascript - 正则表达式排除包裹在特定 bbcode 标签中的匹配项

标签 javascript php regex bbcode

我正在尝试用弯引号替换双引号,除非文本包含在某些标签中,例如 [quote] 和 [code]。

示例输入

[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>"Why no goodbye?" replied [b]Bob[/b]. "It's always Hello!"</p>

预期输出

[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>“Why no goodbye?” replied [b]Bob[/b]. “It's always Hello!”</p>

我想出了如何通过使用 (*SKIP)(*F) 在 PHP 中优雅地实现我想要的东西,但是我的代码将在 javascript 中运行,而 javascript 解决方案并不理想。

现在我在这些标签处拆分字符串,运行替换,然后将字符串放在一起:

var o = 3;
a = a
    .split(/(\[(?<first>(?:icode|quote|code))[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(?:\k<first>)\])/i)
    .map(function(x,i) {
        if (i == o-1 && x) {
            x = '';
        }
        else if (i == o && x)
        {
            x = x.replace(/(?![^<]*>|[^\[]*\])"([^"]*?)"/gi, '“$1”')
            o = o+3;
        }
        return x;
    }).join('');

Javascript 正则表达式分解

  1. 内部split() :
    • (\[(?<first>icode|quote|code)[^\]]*?\](?:.)*?\[\/(\k<first>)\]) - 捕获括号内的模式:
      • \[(?<first>quote|code|icode)[^\]]*?\] - 一个 [quote] , [code] , 或 [icode]打开标签,带或不带参数,如 =html ,例如 [code=html]
      • (?:[\s]*?.)*? - 任何字符 ( . ) 出现次数超过 0 次(尽可能少),前面有或没有空格,因此如果开始标记后跟换行符,它不会中断
      • [\s]*? - 0+ 个空格
      • \[\/(\k<first>)\] - [\quote] , [\code] , 或 [\icode]关闭标签。匹配 (?<first>) 中捕获的文本团体。例如:如果它是一个 quote 开始标签,它将是一个 quote 结束标签
  2. 内部replace() :
    • (?![^<]*>|[^\[]*\])"([^"]*?)" - 捕获双引号内的文本:
      • (?![^<]*>|[^\[]*\]) - 否定先行,查找字符(不是 <[ )后跟 >]并丢弃它们,因此它不会匹配任何 inside bbcode 和 html 标签。例如:[spoiler="Name"]<span style="color: #24c4f9"> .请注意,标签中包装的匹配项保持不变。
      • " - 文字开头的双引号字符。
      • ([^"]*?) - 任何 0+ 字符,双引号除外。
      • " - 文字结束双引号字符。

SPLIT() 正则表达式演示: https://regex101.com/r/Ugy3GG/1

那太糟糕了,因为替换被执行了多次。


与此同时,使用单个 PHP 正则表达式可以获得相同的结果。我写的正则表达式是基于 Match regex pattern that isn't within a bbcode tag .

(\[(?<first>quote|code|icode)[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(\k<first>)\])(*SKIP)(*F)|(?![^<]*>|[^\[]*\])"([^"]*?)"

PHP 正则表达式分解

  • (\[(?<first>quote|code|icode)[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(\k<first>)\])(*SKIP)(*F) - 匹配捕获括号内的模式,就像 javascript split()以上,然后 (*SKIP)(*F)使正则表达式引擎忽略匹配的文本。
  • | - 或者
  • (?![^<]*>|[^\[]*\])"([^"]*?)" - 以与 javascript replace() 相同的方式捕获双引号内的文本做

PHP 演示: https://regex101.com/r/fB0lyI/1

这个正则表达式的美妙之处在于它只需要运行一次。没有字符串的拆分和连接。有没有办法在 javascript 中实现它?

最佳答案

因为 JS 缺少回溯动词,你将需要使用那些括号中的 block ,但稍后按原样替换它们。通过从您自己的正则表达式中获取交替的第二面,最终的正则表达式将是:

\[(quote|i?code)[^\]]*\][\s\S]*?\[\/\1\]|(?![^<]*>|[^\[]*\])"([^"]*)"

但棘手的部分是使用带有 replace() 方法的回调函数:

str.replace(regex, function($0, $1, $2) {
    return $1 ? $0 : '“'  + $2 + '”';
})

如果第一个捕获组存在,上面的三元运算符返回 $0(完整匹配),否则它将第二个捕获组值括在大引号中并返回它。

注意:这可能会在不同情况下失败。

参见 live demo here

关于javascript - 正则表达式排除包裹在特定 bbcode 标签中的匹配项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54961432/

相关文章:

javascript - JQuery:如何在我自己的函数中使用 JQuery 延迟?

r - R中按段落分割

Javascript数组索引错误

javascript - Ajax函数一次只显示一个div,PHP,AJAX

php - 无法在 FPDF 中添加字体

php - 数组值意外变化

java - 如何在 Java 正则表达式中包含命名捕获组?

javascript - 正则表达式在字符串中搜索两个字符串之间的内容

javascript - 在 Audio Web API 中下载音频文件时播放音频文件

php - Drupal 以编程方式删除节点类型和/或 cck 字段