Терпеть определенные символы в RegEx
Я пишу синтаксический анализатор форматирования сообщений, который имеет возможность (среди прочего) анализировать ссылки. Этот конкретный случай требует синтаксического анализа ссылки из <url|linkname>
и заменить этот текст только linkname
, Проблема здесь в том, что оба url
или же linkname
может содержать или не содержать \1
или же \2
символы в любом месте в любом порядке (но не более одного). Я хочу соответствовать шаблону, но оставляю "недействительные" символы. Эта проблема решает сама для linkname
так как эта часть шаблона просто ([^\n+])
, но url
фрагмент сопоставляется с гораздо более сложным шаблоном, в частности с шаблоном проверки URL из is.js. Не было бы тривиально изменить весь шаблон вручную, чтобы терпеть [\1\2]
везде, и мне нужен шаблон для сохранения этих символов, так как они используются для целей отслеживания (поэтому я не могу просто .replace(/\1|\2/g, "")
до сопоставления).
Если такой тип сопоставления невозможен, существует ли какой-либо автоматизированный способ надежного изменения RegExp для добавления [\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>
гдеurl
соответствует шаблону URL (допускается\1
,\2
) а такжеlinkname
состоит из\n
персонажи. Если это условие не выполняется в течение<...|...>
строка должна быть оставлена без изменений в соответствии сnot_a_url
пример выше.
1 ответ
Я закончил тем, что сделал RegEx, который соответствует всем "символам" в выражении. Одним из причуд этого является то, что он ожидает :
, =
, !
символы, которые нужно экранировать, даже вне (?:...)
, (?=...)
, (?!...)
выражение. Это решается путем экранирования их перед обработкой.
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?"));