I have the current expression:
/(?<![http://|https://|#])#([\d\w]+[^\d\s<]+[^\s<>]+)/g
However it's not compatible to run on Safari. I'm trying to handle the following cases:
#tag => match
#123 => no match
#32bit => match
##tag => no match
http://google.com/#/test => no match
tag##tag => no match
tag#tag => no match
<p>#tag</p> => match only #tag
#tag. => match only #tag
tag## => no match
tag# => no match
this is a match #tag => only #tag
I wonder how I can make a character before the match result in a negative match. E.g. # and /.
Is there any alternative to negative look behind that is compatible with Safari?
Thanks in advance.
You might use a negated character class and a capture group, and make sure that there are not only digits.
Note that \w also matches \d
(?:^|[^\w#/])(#(?!\d+\b)\w+)\b
Explanation
(?: Non capture group
^ Assert the start of the string| Or[^\w#/] Match a single non word char other than # or /) Close non capture group( Capture group 1
# Match literally(?!\d+\b) Negative lookahead, assert not only digits to the right followed by a word boundary\w+ Match 1+ word characters) Close group 1\b A word boundary to prevent a partial word matchlet regex = /(?:^|[^\w#/])(#(?!\d+\b)\w+)\b/;
[
"#tag",
"#123",
"#32bit",
"##tag",
"http://google.com/#/test",
"tag##tag",
"tag#tag",
"<p>#tag</p>",
"#tag.",
"tag##",
"tag#"
].forEach(s => {
const m = s.match(regex)
if (m) {
console.log(`${s} --> ${m[1]}`)
}
})
Using the matches in a replacement:
let regex = /((?:^|[^\w#/]))(#(?!\d+\b)\w+)\b/;
[
"#tag",
"#123",
"#32bit",
"##tag",
"http://google.com/#/test",
"tag##tag",
"tag#tag",
"<p>#tag</p>",
"#tag.",
"tag##",
"tag#",
"this is a match #tag"
].forEach(s => {
const m = s.match(regex)
if (m) {
console.log(s.replace(regex, "$1<span>$2</span>"))
}
})
This satisfies your test cases. Not sure though whether you want this or not.
It does't work on #123 but I forgot it and I'm now lazy to add it as a screenshot.
/^(?:[^#]*[^#\w])?(#[\w]*[a-zA-Z][\w]*).*$/g
If you only want to allow <tags> before the "#", you can insted use @kendle's solution for the first non-capture group (before the actual group starting with #).
(?:<\w*>)?
If you use the following pattern the second matching group contains what you want.
^(<\w*>)?(#\w+[a-zA-Z])