正则表达式与预期的 \n 字符不匹配

时间:2021-07-05 22:11:53

标签: javascript regex

尝试使用regex将markdown UL转成HTML。下面,我展示了一个示例输入的样子,结果应该是两个不同的 ul 元素,第一个包含三个 li 元素,第二个包含两个。

- Item 1
- Item 2
- Item 3

A second list:
- Item 1
- Item 2

遇到这个令人恼火的问题,其中以下正则表达式似乎没有按预期工作。问题是它似乎无法识别 \n 字符,因为第一个正则表达式 /((- |ー).*(\n|$))+/g 似乎仅在字符串结尾 ($) 时才获得匹配项。< /p>

.replaceAll(/((- |ー).*(\n|$))+/g, function(match) {
    return `<ul>${match}</ul>`.replaceAll(/(- |ー).*/g, function(match) {
        return `<li>${match.match(/(?<=(- |ー)).*/)}</li>`
    });
});

我不明白问题是什么,我测试了 Regexr 中的表达式,它完美地工作。

如果有帮助,这里是完整的上下文:

parse(markdown) {
    return markdown

    // Clean HTML brackets
        .replaceAll('<', '&lt')
        .replaceAll('>', '&gt')

    // Change markdown links into html links
        .replaceAll(/\[.*?\]\(.*?\)/g, function (match) {
            return `<a href='${match.match(/(?<=\().*?(?=\))/)[0]}' target='_blank'>${match.match(/(?<=\[).*?(?=\])/)[0]}</a>`;
    })

    // Headings
        .replaceAll(/(^|\n)# .*/g, function (match) {
            return `<h1>${match.match(/(?<=# ).*/)}</h1>`
        })
        .replaceAll(/(^|\n)## .*/g, function (match) {
            return `<h2>${match.match(/(?<=# ).*/)}</h2>`
        })
        .replaceAll(/(^|\n)### .*/g, function (match) {
            return `<h3>${match.match(/(?<=# ).*/)}</h3>`
        })

    // Ordered lists
        .replaceAll(/((- |ー).*($|\n))+/g, function(match) {
            return `<ul>${match}</ul>`.replaceAll(/(- |ー).*/g, function(match) {
                return `<li>${match.match(/(?<=(- |ー)).*/)}</li>`
            });
        });

请注意,\n 部分中的 // Headings 字符被完全识别。

(编辑以澄清这是在 VueJS 中,因此在组件的方法对象中使用此方法定义语法)

2 个答案:

答案 0 :(得分:1)

它根本没有忽略 \n。您的正则表达式模式只是将整个列表匹配为单个匹配项,而 \n|$ 只是匹配最后一个匹配项——即您得到的是一个长匹配项,而不是三个单独的匹配项,每个列表项一个匹配项,因为您想要。

事实上,您误认为“在 Regexr 中它可以完美运行”。去那里再试试。你会得到一场长比赛,而不是三场。

这样做的原因是正则表达式默认是贪婪。您可以通过将 ? 附加到量词来改变它,使其 lazy 而不是 greedy

/((- |ー).*(\n|$))+?/g

尝试在 Regexr 中使用和不使用 ? 以便您可以看到差异,并且还可以学习如何解释 Regexr 结果,因为您上次错过了这个。

<块引用>

ℹ️ 这不能解决您的列表项转换为 HTML 的问题;您的代码还有其他问题,但我正在回答您提出的问题。

还有另一种方法可以产生相同的结果:

/((- |ー).*($))+/gm

这种方法切换到多行模式,这意味着输入被视为单独的行。在这种模式下,您不会尝试匹配 \n,因为它们不会出现;您只需将每行的末尾与 $ 匹配。

答案 1 :(得分:-1)

您的代码过于复杂。试试这个:

1- 应用除有序列表之外的所有过滤器

2- 查找所有列表匹配

3- 将它们映射到 li

parse(markdown) {
    const cleansedString = markdown

    // Clean HTML brackets
        .replaceAll('<', '&lt')
        .replaceAll('>', '&gt')

    // Change markdown links into html links
        .replaceAll(/\[.*?\]\(.*?\)/g, function (match) {
            return `<a href='${match.match(/(?<=\().*?(?=\))/)[0]}' target='_blank'>${match.match(/(?<=\[).*?(?=\])/)[0]}</a>`;
    })

    // Headings
        .replaceAll(/(^|\n)# .*/g, function (match) {
            return `<h1>${match.match(/(?<=# ).*/)}</h1>`
        })
        .replaceAll(/(^|\n)## .*/g, function (match) {
            return `<h2>${match.match(/(?<=# ).*/)}</h2>`
        })
        .replaceAll(/(^|\n)### .*/g, function (match) {
            return `<h3>${match.match(/(?<=# ).*/)}</h3>`
        });

    const listMatches = Array.from(cleansedString.matchAll(/((- |ー)(.*)($|\n))/g));
    const listHtml = listMatches.map((matches) => `<li>${matches[3]}</li>`);

    return `<ul>${listHtml.join('')}</ul>`;
}
相关问题