Регулярное выражение: соответствует словам с подчеркиванием, если они не начинаются с @ / #
Я пытаюсь обойти эту ошибку в Tiptap (редактор WYSIWYG для Vue), передав настраиваемое регулярное выражение, чтобы регулярное выражение, определяющее курсивную нотацию в Markdown (_value_
) не будет применяться к строкам, начинающимся с @
или #
, например #some_tag_value
не будет преобразовано в значениетега#some.
Пока это мое регулярное выражение - /(^|[^@#_\w])(?:\w?)(_([^_]+)_)/g
Изменить: новое регулярное выражение с помощью @ Wiktor Stribiew /(^|[^@#_\w])(_([^_]+)_)/g
Хотя он удовлетворяет большинству распространенных случаев, в настоящее время он все еще не работает, когда символы подчеркивания находятся в середине слова, например, ant_farm_ следует сопоставить (муравьинаяферма)
Я также предоставил несколько случаев "должно совпадать" и "не должно совпадать" здесь https://regexr.com/50ibf для упрощения тестирования.
Должно совпадать (между подчеркиванием)
_italic text here_
police_woman_
_fire_fighter
a thousand _words_
_brunch_ on a Sunday
Не должно совпадать
@ta_g_
__value__
#some_tag_value
@some_value_here
@some_tag_
#some_val_
#_hello_
3 ответа
Вы можете использовать следующий шаблон:
(?:^|\s)[^@#\s_]*(_([^_]+)_)
См. Демонстрацию регулярного выражения
Детали
(?:^|\s)
- начало строки или пробела[^@#\s_]*
- 0 или более символов, кроме@
,#
,_
и пробелы(_([^_]+)_)
- Группа 1:_
, 1+ символов, кроме_
(захвачены в Группу 2), а затем_
.
Для науки это чудовище работает в Chrome (и Node.js).
let text = `
<strong>Should match</strong> (between underscores)
_italic text here_
police_woman_
_fire_fighter
a thousand _words_
_brunch_ on a Sunday
<strong>Should not match</strong>
@ta_g_
__value__
#some_tag_value
@some_value_here
@some_tag_
#some_val_
#_hello_
`;
let re = /(?<=(?:\s|^)(?![@#])[^_\n]*)_([^_]+)_/g;
document.querySelector('div').innerHTML = text.replace(re, '<em>$1</em>');
div { white-space: pre; }
<div/>
Это захватывает _something_
как полное совпадение, и something
как 1-я группа захвата (чтобы убрать подчеркивания). Вы не можете захватить простоsomething
, потому что тогда вы потеряете способность различать, что находится внутри подчеркивания, а что снаружи (попробуйте (?<=(?:\s|^)(?![@#])[^_\n]*_)([^_]+)(?=_)
).
Есть две вещи, которые мешают его универсальному применению:
- Просмотр назад поддерживается не всеми движками JavaScript.
- Большинство движков регулярных выражений не поддерживают просмотр назад переменной длины
РЕДАКТИРОВАТЬ: это немного сильнее и должно позволить вам дополнительно match_this_and_that_ but not @match_this_and_that
правильно:
/(?<=(?:\s|^)(?![@#])(?!__)\S*)_([^_]+)_/
Пояснение:
_([^_]+)_ Match non-underscory bit between two underscores
(?<=...) that is preceded by
(?:\s|^) either a whitespace or a start of a line/string
(i.e. a proper word boundary, since we can't use `\b`)
\S* and then some non-space characters
(?![@#]) that don't start with `@`, `#`,
(?!__) or `__`.
Вот кое-что, это не так компактно, как другие ответы, но я думаю, что легче понять, что происходит. Группа матчей\3
это то, что вы хотите.
Требуется многострочный флаг
^([a-zA-Z\s]+|_)(([a-zA-Z\s]+)_)+?[a-zA-Z\s]*?$
^
- совпадение начала строки([a-zA-Z\s]+|_)
- несколько слов или_
(([a-zA-Z\s]+)_)+?
- несколько слов, за которыми следует_
хоть один раз, но минимальное совпадение.[a-zA-Z\s]*?
- любые заключительные слова$
- конец строки
В итоге разбивка вещей, чтобы соответствовать одному из
_<words>_
<words>_<words>_
<words>_<words>_<words>
_<words>_<words>