正则表达式排除包裹在特定 bbcode 标签中的匹配项
Regex excluding matches wrapped in specific bbcode tags
我正在尝试用弯引号替换双引号,除非文本包含在某些标签中,例如 [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, '“”')
o = o+3;
}
return x;
}).join('');
Javascript 正则表达式分解
- 内部
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 结束标签
- 里面
replace()
:
(?![^<]*>|[^\[]*\])"([^"]*?)"
- 捕获双引号内的文本:
(?![^<]*>|[^\[]*\])
- 否定前瞻,查找字符(不是 <
或 [
)后跟 >
或 ]
并丢弃它们,所以它不会匹配任何 inside bbcode 和 html 标签。例如:[spoiler="Name"]
或 <span style="color: #24c4f9">
。请注意,标签中 wrapped 的匹配项保持不变。
"
- 文字左双引号字符。
([^"]*?)
- 任何 0+ 字符,双引号除外。
"
- 文字结束双引号字符。
SPLIT() 正则表达式演示: https://regex101.com/r/Ugy3GG/1
太糟糕了,因为替换被执行了多次。
同时,使用单个 PHP 正则表达式可以实现相同的结果。我写的正则表达式是基于 .
(\[(?<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中实现它?
嵌套标记很难用 rx 解析,尤其是 JS 的 RegExp。复杂的正则表达式也难以阅读、维护和调试。如果你的需求很简单,一个标签内容替换排除了一些被禁止的标签,考虑一个简单的基于代码的替代 运行-on RegExps:
function curly(str) {
var excludes = {
quote: 1,
code: 1,
icode: 1
},
xpath = [];
return str.split(/(\[[^\]]+\])/) // breakup by tag markup
.map(x => { // for each tag and content:
if (x[0] === "[") { // tag markup:
if (x[1] === "/") { // close tag
xpath.pop(); // remove from current path
} else { // open tag
xpath.push(x.slice(1).split(/\W/)[0]); // add to current path
} //end if open/close tag
} else { // tag content
if (xpath.every(tag =>!excludes[tag])) x = x.replace(/"/g, function repr() {
return (repr.z = !repr.z) ? "“" : "”"; // flip flop return value (naive)
});
} //end if markup or content?
return x;
}) // end term map
.join("");
} /* end curly() */
var input = `[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>"Why no goodbye?" replied [b]Bob[/b]. "It's always Hello!"</p>`;
var wants = `[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>“Why no goodbye?” replied [b]Bob[/b]. “It's always Hello!”</p>`;
curly(input) == wants; // true
在我看来,尽管代码有点长,但代码允许文档、缩进和显式命名,这使得这些半复杂的逻辑操作更容易理解。
如果您的需求更复杂,请为 JavaScript 和 map/filter/reduce 使用真正的 BBCode 解析器,它是需要的模型。
因为 JS 缺少回溯动词,你将需要使用那些括号中的块,但稍后按原样替换它们。通过从您自己的正则表达式中获取交替的第二面,最终的正则表达式将是:
\[(quote|i?code)[^\]]*\][\s\S]*?\[\/\]|(?![^<]*>|[^\[]*\])"([^"]*)"
但棘手的部分是使用带有 replace()
方法的回调函数:
str.replace(regex, function([=11=], , ) {
return ? [=11=] : '“' + + '”';
})
上面的三元运算符 returns [=13=]
(整个匹配)如果第一个捕获组存在,否则它将第二个捕获组值用大引号括起来,returns 它。
注意:这可能会在不同情况下失败。
我正在尝试用弯引号替换双引号,除非文本包含在某些标签中,例如 [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, '“”')
o = o+3;
}
return x;
}).join('');
Javascript 正则表达式分解
- 内部
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 结束标签
- 里面
replace()
:(?![^<]*>|[^\[]*\])"([^"]*?)"
- 捕获双引号内的文本:(?![^<]*>|[^\[]*\])
- 否定前瞻,查找字符(不是<
或[
)后跟>
或]
并丢弃它们,所以它不会匹配任何 inside bbcode 和 html 标签。例如:[spoiler="Name"]
或<span style="color: #24c4f9">
。请注意,标签中 wrapped 的匹配项保持不变。"
- 文字左双引号字符。([^"]*?)
- 任何 0+ 字符,双引号除外。"
- 文字结束双引号字符。
SPLIT() 正则表达式演示: https://regex101.com/r/Ugy3GG/1
太糟糕了,因为替换被执行了多次。
同时,使用单个 PHP 正则表达式可以实现相同的结果。我写的正则表达式是基于
(\[(?<first>quote|code|icode)[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(\k<first>)\])(*SKIP)(*F)|(?![^<]*>|[^\[]*\])"([^"]*?)"
PHP 正则表达式分解
(\[(?<first>quote|code|icode)[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(\k<first>)\])(*SKIP)(*F)
- 匹配捕获括号内的模式,就像上面的 javascriptsplit()
一样,然后(*SKIP)(*F)
让正则表达式引擎忽略匹配的文本。|
- 或(?![^<]*>|[^\[]*\])"([^"]*?)"
- 以与 javascriptreplace()
相同的方式捕获双引号内的文本
PHP 演示: https://regex101.com/r/fB0lyI/1
这个正则表达式的美妙之处在于它只需要 运行 一次。没有字符串的拆分和连接。有没有办法在javascript中实现它?
嵌套标记很难用 rx 解析,尤其是 JS 的 RegExp。复杂的正则表达式也难以阅读、维护和调试。如果你的需求很简单,一个标签内容替换排除了一些被禁止的标签,考虑一个简单的基于代码的替代 运行-on RegExps:
function curly(str) {
var excludes = {
quote: 1,
code: 1,
icode: 1
},
xpath = [];
return str.split(/(\[[^\]]+\])/) // breakup by tag markup
.map(x => { // for each tag and content:
if (x[0] === "[") { // tag markup:
if (x[1] === "/") { // close tag
xpath.pop(); // remove from current path
} else { // open tag
xpath.push(x.slice(1).split(/\W/)[0]); // add to current path
} //end if open/close tag
} else { // tag content
if (xpath.every(tag =>!excludes[tag])) x = x.replace(/"/g, function repr() {
return (repr.z = !repr.z) ? "“" : "”"; // flip flop return value (naive)
});
} //end if markup or content?
return x;
}) // end term map
.join("");
} /* end curly() */
var input = `[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>"Why no goodbye?" replied [b]Bob[/b]. "It's always Hello!"</p>`;
var wants = `[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>“Why no goodbye?” replied [b]Bob[/b]. “It's always Hello!”</p>`;
curly(input) == wants; // true
在我看来,尽管代码有点长,但代码允许文档、缩进和显式命名,这使得这些半复杂的逻辑操作更容易理解。
如果您的需求更复杂,请为 JavaScript 和 map/filter/reduce 使用真正的 BBCode 解析器,它是需要的模型。
因为 JS 缺少回溯动词,你将需要使用那些括号中的块,但稍后按原样替换它们。通过从您自己的正则表达式中获取交替的第二面,最终的正则表达式将是:
\[(quote|i?code)[^\]]*\][\s\S]*?\[\/\]|(?![^<]*>|[^\[]*\])"([^"]*)"
但棘手的部分是使用带有 replace()
方法的回调函数:
str.replace(regex, function([=11=], , ) {
return ? [=11=] : '“' + + '”';
})
上面的三元运算符 returns [=13=]
(整个匹配)如果第一个捕获组存在,否则它将第二个捕获组值用大引号括起来,returns 它。
注意:这可能会在不同情况下失败。