所以我正在尝试为JavaScript编写一个正则表达式,它允许我用标签替换**作为一种自动滚动的Markdown到HTML转换器。
e.g。
**bold**
- &gt; <strong>bold</strong>
但
\**not**
- &gt; **not**
因为*
已被转义。
我有以下正则表达式似乎运作良好:
/(?<!\\)(?:\\\\)*(\*\*)([^\\\*]+)(\*\*)/g
但是,JS不支持lookbehinds!我用lookaheads重写了它:
/(\*\*)([^\\\*]+)*(\*\*)(?!\\)(?:\\\\)*/g
但这需要我反转字符串,因为我需要支持多字节字符(see here)。我并不完全反对使用该答案中提到的库,但我更喜欢一种解决方案,如果可能的话,不要求我添加一个。
有没有办法在不使用外观的情况下重写我的正则表达式?
编辑:
在考虑了这一点之后,我甚至开始质疑正则表达式是否是解决这个问题的最佳方法,但我会提出这个问题。
答案 0 :(得分:3)
解决缺失外观的一种方法是首先匹配不需要的模式,然后使用交替匹配所需的模式。然后应用条件替换,用自己和期望的模式替换不需要的模式与你真正想要的。
在您的特定情况下,这意味着首先匹配\*
,然后才匹配**<something>**
。然后使用
input.replace(/\\\*|\*\*(.*?)\*\*/, function(m, p1) {
return m == '\\*' ? m : '<strong>' + p1 + '</strong>';
})
进行条件替换。
真正的正则表达式虽然更复杂。首先,您需要保护转义反斜杠本身(即\\**bold**
应成为\\<strong>bold</strong>
)。因此,您需要以与\\
相同的方式单独匹配\*
。
其次,**
和**
之间的表达式也可能包含一些转义的星号和斜杠。为了解决这个问题,您需要明确地匹配\\
和\**
以及(使用替换)仅在其他任何非贪婪之后。这可以表示为(?:\\\\|\\\*\*|\*(?!\*)|[\S\s])*?
。
因此最终的正则表达式转向
\\\\|\\\*|\*\*((?:\\\\|\\\*\*|\*(?!\*)|[\S\s])*?)\*\*
演示:https://regex101.com/r/Da35r5/1
JavaScript替换演示:
function convert() {
var md = document.getElementById("md").value;
var re = /\\\\|\\\*|\*\*((?:\\\\|\\\*\*|\*(?!\*)|[\S\s])*?)\*\*/g;
var html = md.replace(re, function(match, p1) {
return match.startsWith('\\') ? match : '<strong>' + p1 + '</strong>';
});
document.getElementById("html").value = html;
}
&#13;
<span style="display:inline-block">
MD
<textarea id="md" cols="20" rows="10" style="display:block">
**bold**
**foo * bar **
**foo \** bar**
**fo\\\\** bar** **
\**bold** **
\\**bold**
** multi
line**
</textarea>
</span>
<span style="display:inline-block">
HTML
<textarea id="html" cols="50" rows="10" style="display:block">
</textarea>
</span>
<button onclick="convert()" style="display:block">Convert</button>
&#13;
答案 1 :(得分:0)
答案 2 :(得分:0)
考虑以下正则表达式:
/(.*?)(\\\\|\\\*|\*\*)/g
您可以将其视为标记器。它对一些(或没有)文本进行非贪婪匹配,后跟一个特殊字符序列\\
,\*
,最后是**
。按此顺序匹配可确保正确处理**foo \** bar\\**
等奇怪的边缘情况(<strong>foo \** bar\</strong>
)。这使得一个非常简单的String.prototype.replace
在其替换函数中具有switch
。布尔bold
标记有助于我们决定是否应将**
替换为<strong>
或</strong
&gt ;.
const TOKENIZER = /(.*?)(\\\\|\\\*|\*\*)/g;
function render(str) {
let bold = false;
return str.replace(TOKENIZER, (_, text, special) => {
switch (special) {
case '\\\\':
return text + '\\';
case '\\*':
return text + '*';
case '**':
bold = !bold;
return text + (bold ? '<strong>' : '</strong>');
default:
return text + special;
}
});
}
我假设\\
应该变为\
而\*
应该变为*
,就像在正常的Markdown解析器中一样。它与德米特里的解决方案没有什么不同,但更简单。请参阅以下代码段中的操作:
const TOKENIZER = /(.*?)(\\\\|\\\*|\*\*)/g;
function render(str) {
let bold = false;
return str.replace(TOKENIZER, (_, text, special) => {
switch (special) {
case '\\\\':
return text + '\\';
case '\\*':
return text + '*';
case '**':
bold = !bold;
return text + (bold ? '<strong>' : '</strong>');
default:
return text + special;
}
});
}
// Test
const input = document.getElementById('input');
const outputText = document.getElementById('output-text');
const outputHtml = document.getElementById('output-html');
function makeOutput(str) {
const result = render(str);
outputText.value = render(str);
outputHtml.innerHTML = render(str);
}
input.addEventListener('input', evt => makeOutput(evt.target.value));
makeOutput(input.value);
&#13;
body{font-family:'Helvetica Neue',Helvetica,sans-serif}
textarea{display:block;font-family:monospace;width:100%;margin-bottom:1em}
div{padding:2px;background-color:lightgoldenrodyellow}
&#13;
<label for="input">Input</label>
<textarea id="input" rows="3">aaa **BBB** ccc \**ddd** EEE \\**fff \**ggg** HHH**</textarea>
Output HTML:
<textarea id="output-text" rows="3" disabled></textarea>
Rendered HTML:
<div id="output-html"></div>
&#13;