Для науки это чудовище работает в 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 `__`.
regex101 demo