1

我正在编写一个消息格式化解析器,它能够(除其他外)解析链接。这种特定情况需要解析 from of 中的链接<url|linkname>并将该文本替换为linkname. 这里的问题是,两者urllinkname可能或可能不包含或以任何顺序在任何地方包含\1或字符(尽管每个最多一个)。\2我想匹配模式但保留“无效”字符。这个问题自己解决了,linkname因为模式的那部分是 just ([^\n+]),但是url片段匹配一个更复杂的模式,更具体地说是来自is.js的 URL 验证模式。手动修改整个模式以容忍它并非易事[\1\2]到处都是,我需要模式来保留这些字符,因为它们用于跟踪目的(所以我不能只是.replace(/\1|\2/g, "")在匹配之前)。

如果这种匹配是不可能的,是否有一些自动化的方法可以可靠地修改正则表达式以[\1\2]{0,2}在每个字符匹配之间添加、添加\1\2到所有[chars]匹配等。

这是url取自的模式is.js

/(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?/i

此模式适用于我的目的,<url|linkname>格式如下:

let namedUrlRegex = /<((?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?)\|([^\n]+)>/ig;

使用它的代码在这里:JSFiddle

澄清示例(...代表namedUrlRegex上面的变量,并且$2是捕获的捕获组linkname):

Current behavior:
"<googl\1e.com|Google>".replace(..., "$2") // "<googl\1e.com|Google>" WRONG
"<google.com|Goo\1gle>".replace(..., "$2") // "Goo\1gle"              CORRECT
"<not_\1a_url|Google>".replace(..., "$2") // "<not_\1a_url|Google>"   CORRECT

Expected behavior:
"<googl\1e.com|Google>".replace(..., "$2") // "Google" (note there is no \1)
"<google.com|Goo\1gle>".replace(..., "$2") // "Goo\1gle"
"<not_\1a_url|Google>".replace(..., "$2") // "<not_\1a_url|Google>"

\1请注意适用于\2, \1\2,\1...\2等的\2...\1相同规则

上下文:这用于将来自 WYSIWYG 编辑器的字符串规范化为它将显示的长度/内容,保留当前选择的位置(由 表示\1\2因此可以在解析后恢复)。如果“插入符号”被完全删除(例如,如果光标位于链接的 URL 中),它将选择整个字符串。一切都按预期工作,除了选择在 url 片段中开始或结束时。

编辑澄清:我只想更改字符串中的段,如果它遵循<url|linkname>whereurl匹配 URL 模式(容忍\1\2)的格式并且linkname由非\n字符组成。如果字符串中不满足此条件<...|...>,则应按照上面的示例保持不变。not_a_url

4

1 回答 1

0

我最终制作了一个匹配表达式中所有“符号”的正则表达式。一个怪癖是它期望:, =,!字符被转义,即使在(?:...), (?=...),(?!...)表达式之外。这是通过在处理之前转义它们来解决的。

Fiddle

let r = /(\\.|\[.+?\]|\w|[^\\\/\[\]\^\$\(\)\?\*\+\{\}\|\+\:\=\!]|(\{.+?\}))(?:((?:\{.+?\}|\+|\*)\??)|\??)/g;

let url = /((?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?)/

function tolerate(regex, insert) {
    let first = true;
        // convert to string
    return regex.toString().replace(/\/(.+)\//, "$1").
        // escape :=!
        replace(/((?:^|[^\\])\\(?:\\)*\(\?|[^?])([:=!]+)/g, (m, g1, g2) => g1 + (g2.split("").join("\\"))).
        // substitute string
        replace(r, function(m, g1, g2, g3, g4) {
            // g2 = {...} multiplier (to prevent matching digits as symbols)
            if (g2) return m;
            // g3 = multiplier after symbol (must wrap in parenthesis to preserve behavior)
            if (g3) return "(?:" + insert + g1 + ")" + g3;
            // prevent matching tolerated characters at beginning, remove to change this behavior
            if (first) {
                first = false;
                return m;
            }
            // insert the insert
            return insert + m;
        }
    );
}

alert(tolerate(url, "\1?\2?"));
于 2018-04-11T13:06:20.727 回答