Javascript 正则表达式,不包括包装在特定bbcode标记中的匹配项
我试图用卷曲引号替换双引号,除非文本被包装在某些标记中,如[quote]和[code] 样本输入Javascript 正则表达式,不包括包装在特定bbcode标记中的匹配项,javascript,php,regex,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>“
[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>
Javascript正则表达式分解
split()
:
-捕获括号内的模式:(\[(?icode | quote | code)[^\]*?\](?:)*?\[\/(\k)\])
-a\[(?quote | code | icode)[^\]*?\]
,[quote]
或[code]
打开标记,带或不带[icode]
等参数,例如=html
[code=html]
-任何字符((?:[\s]*?)*?
)的任何0+(尽可能少)次出现,前面是否有空格,因此如果开头标记后面有换行符,它不会中断
-0+空格[\s]*?
-\[\/(\k)\]
,[\quote]
,或[\code]
结束标记。匹配[\icode]
组中捕获的文本。如果它是一个报价开始标签,它将是一个报价结束标签(?)
replace()
:
-捕获双引号内的文本:(?![^ |[^\[]*\])“([^”]*?)”
-负向前看,查找字符(不是(?![^ |[^\[]*]
或
并丢弃它们,因此它不会匹配bbcode和html标记中的任何内容。例如:]
或[spoiler=“Name”]
。请注意,包装在标记中的匹配项保持不变
-文字开头双引号字符“
-任何0+字符,双引号除外([^“]*?)
-文字结尾双引号字符“
同时,使用单个PHP正则表达式也可以获得相同的结果
(\[(?quote | code | icode)[^\]*?\](?:[\s]*?)*?[\s]*?\[\/(\k)\](*跳过)(*F)|(?![^\[*])([^”]*?)
PHP正则表达式分解
-匹配捕获括号内的模式,就像上面的javascript(\[(?quote | code | icode)[^\]*?\](?:[\s]*?*?[\s]*?\[\/(\k)\])(*SKIP)(*F)
,然后split()
使正则表达式引擎忽略匹配的文本(*SKIP)(*F)
-或|
-以与javascript(?![^\[]*\])“([^”]*?)”
相同的方式捕获双引号内的文本replace()
这个正则表达式的美妙之处在于它只需要运行一次。不需要拆分和连接字符串。有没有办法在javascript中实现它?嵌套标记很难用rx解析,特别是JS的正则表达式。复杂的正则表达式也很难读取、维护和调试。如果您的需要很简单,可以使用排除一些禁用的标签,考虑基于ReXEPS的简单代码替代方案:
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
函数卷曲(str){
var不包括={
报价:1,
代码:1,,
国际编码:1
},
xpath=[];
返回str.split(/(\[^\]]+\]/)//按标记标记拆分
.map(x=>{//对于每个标记和内容:
如果(x[0]==“[”){//标记标记:
如果(x[1]==“/”{//“)关闭标记
xpath.pop();//从当前路径中删除
}else{//open标记
xpath.push(x.slice(1.split(//\W/)[0]);//添加到当前路径
}//如果打开/关闭标记,则结束
}else{//标记内容
if(xpath.every(tag=>!excludes[tag])x=x.replace(/“/g,函数repr(){
返回(repr.z=!repr.z)?“”:“”;//触发器返回值(朴素)
});
}//如果是标记或内容,则结束?
返回x;
})//结束术语映射
.加入(“”);
}/*末端卷曲()*/
var input=`[quote=“Name”][b]Alice[/b]说,“你好,世界!”[/quote]
“为什么不说再见呢?”鲍勃回答说,“总是你好!”;
var wants=`[quote=“Name”][b]Alice[/b]说,“你好,世界!”[/quote]
“为什么不说再见呢?”鲍勃回答说,“总是你好!”;
卷曲(输入)=需要;//真
在我看来,尽管代码稍微长一点,但它允许文档、缩进和显式命名,这使得这些半复杂的逻辑操作更容易理解
如果您的需求更复杂,请使用真正的BBCode JavaScript解析器,并根据需要映射/过滤/缩减其模型。因为JS缺少回溯动词,您需要使用这些括号中的块,但稍后会按原样替换它们。通过从您自己的正则表达式中获得替代的第二个方面,最终的正则表达式将是:
\[(quote|i?code)[^\]]*\][\s\S]*?\[\/\1\]|(?![^<]*>|[^\[]*\])"([^"]*)"
如果第一个捕获组存在,则上面的三元运算符返回$0
(整个匹配),否则它将第二个捕获组值括在卷曲引号中并返回它
注意:在不同的情况下,这可能会失败
参见实际上我认为这和它所得到的一样好;用正则表达式解析DOM是很难的。如果你想要一个更干净的解决方案,我会考虑解析DOM而不是使用正则表达式。在给定的输入字符串中没有卷曲的引号。请考虑从给定的输入字符串提供预期的输出。@ ReVo不应该。在输入中使用卷曲引号,因为它们要添加到输出中。它们并不重要,正则表达式的要点是用{something}替换{thing}我在问题中添加了一个简短的输入/输出示例,但鉴于正则表达式可能会以多种方式失败,为了更彻底,在演示中有一个较长的示例输入。由于此任务包括处理html,因此它不会
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
\[(quote|i?code)[^\]]*\][\s\S]*?\[\/\1\]|(?![^<]*>|[^\[]*\])"([^"]*)"
str.replace(regex, function($0, $1, $2) {
return $1 ? $0 : '“' + $2 + '”';
})