Safari Regex error "invalid regular expression invalid group specifier name" [duplicate] - javascript

In my Javascript code, this regex /(?<=\/)([^#]+)(?=#*)/ works fine in Chrome, but in safari, I get:
Invalid regular expression: invalid group specifier name
Any ideas?

Looks like Safari doesn't support lookbehind yet (that is, your (?<=\/)). One alternative would be to put the / that comes before in a non-captured group, and then extract only the first group (the content after the / and before the #).
/(?:\/)([^#]+)(?=#*)/
Also, (?=#*) is odd - you probably want to lookahead for something (such as # or the end of the string), rather than a * quantifier (zero or more occurrences of #). It might be better to use something like
/(?:\/)([^#]+)(?=#|$)/
or just omit the lookahead entirely (because the ([^#]+) is greedy), depending on your circumstances.

The support for RegExp look behind assertions as been issued by web kit:
Check link: https://github.com/WebKit/WebKit/pull/7109

Regex ?<= not supported Safari iOS, we can use ?:
Note: / or 1st reference letter that comes before in a non-captured group
See detail: https://caniuse.com/js-regexp-lookbehind
let str = "Get from Slash/to Next hashtag #GMK"
let workFineOnChromeOnly = str?.match(/(?<=\/)([^#]+)(?=#*)/g)
console.log("❌ Work Fine On Chrome Only", workFineOnChromeOnly )
let workFineSafariToo = str?.match(/(?:\/)([^#]+)(?=#*)/g)
console.log("✔️ Work Fine Safari too", workFineSafariToo )

Just wanted to put this out there for anyone who stumbles across this issue and can't find anything...
I had the same issue, and it turned out to be a RegEx expression in one of my dependencies, namely Discord.js .
Luckily I no longer needed that package but if you do, consider putting an issue out there or something (maybe you shouldn't even be running discord.js in your frontend react app).

Related

Regex works on chrome but returns invalid error on safari

text = document.getElementById("p01").innerHTML;
document.getElementById("demo").innerHTML = /(?<=api-)(.*)-us(.*)/.test('api-stg-usw2b');
<h2>JavaScript Regular Expressions</h2>
<p>Search for an "e" in the next paragraph:</p>
<p id="p01">The best things in life are free!</p>
<p id="demo"></p>
This regex works on chrome but fails on safari… I tried to replace < with : but also it didn't work
This is the error I see:
SyntaxError: Invalid regular expression: invalid group specifier name
Not sure how to fix it? What is the regex cheat sheet I have to use?
This first capturing group is invalid (also doesn't work on Firefox); positive look behind (?<=.. is not supported in the javscript flavor of regex; and its browser dependent implementation; check out this SO answer for further reading; so try to use a more widely supported "pattern" instead of /(?<=api-)(.*)-us(.*)/; it definitely can be written simpler without positive look behind; I also suggest try your pattern in a online regex playground like regexr or regex101, etc...

Works in Chrome, but breaks in Safari: Invalid regular expression: invalid group specifier name /(?<=\/)([^#]+)(?=#*)/

In my Javascript code, this regex /(?<=\/)([^#]+)(?=#*)/ works fine in Chrome, but in safari, I get:
Invalid regular expression: invalid group specifier name
Any ideas?
Looks like Safari doesn't support lookbehind yet (that is, your (?<=\/)). One alternative would be to put the / that comes before in a non-captured group, and then extract only the first group (the content after the / and before the #).
/(?:\/)([^#]+)(?=#*)/
Also, (?=#*) is odd - you probably want to lookahead for something (such as # or the end of the string), rather than a * quantifier (zero or more occurrences of #). It might be better to use something like
/(?:\/)([^#]+)(?=#|$)/
or just omit the lookahead entirely (because the ([^#]+) is greedy), depending on your circumstances.
The support for RegExp look behind assertions as been issued by web kit:
Check link: https://github.com/WebKit/WebKit/pull/7109
Regex ?<= not supported Safari iOS, we can use ?:
Note: / or 1st reference letter that comes before in a non-captured group
See detail: https://caniuse.com/js-regexp-lookbehind
let str = "Get from Slash/to Next hashtag #GMK"
let workFineOnChromeOnly = str?.match(/(?<=\/)([^#]+)(?=#*)/g)
console.log("❌ Work Fine On Chrome Only", workFineOnChromeOnly )
let workFineSafariToo = str?.match(/(?:\/)([^#]+)(?=#*)/g)
console.log("✔️ Work Fine Safari too", workFineSafariToo )
Just wanted to put this out there for anyone who stumbles across this issue and can't find anything...
I had the same issue, and it turned out to be a RegEx expression in one of my dependencies, namely Discord.js .
Luckily I no longer needed that package but if you do, consider putting an issue out there or something (maybe you shouldn't even be running discord.js in your frontend react app).

Emoji typing regex messed up

I'm building a simple chat with some basic emojis. So when the user for example types :), a smiley emoji is being displayed inside the input field.
However my regex fails when it comes to the emoji :/ and the reason is that it messes up URLs. (Keep in mind the emoji detection is triggered on keyup).
Example test text:
Hello could you please visit my website at http://www.example.com or https://www.example.com :/ ://
So to sum up the regex must not replace :/ when it starts with http or https.
Currently in my mapping I have this: "[^http?s]:/{1}(?!/)": "1F615" but for some strange reason it keeps 'eating the previous character upon replace'.
Since the issue is that :/ is messing up http://, and JavaScript doesn't support lookbehinds ("check if this did/didn't occur immediately before"), I think the simplest solution would be just to check if the :/ is (not) followed by another /
":/(?!/)" : "1F615"
Notably, the lookahead (which is supported by JavaScript) does not actually match the subsequent /, it just ensures that it isn't there.
This should work
function reverse(s){
return s.split("").reverse().join("");
}
function getMatch(str){
const regex = /\/+\:(?!s{0,1}ptth)/g;
const subst = ``;
const result = reverse(reverse(str).replace(regex, subst));
console.log(result);
}
getMatch(`Hello could you please visit my website at http://www.example.com or https://www.example.com :/ ://`);
Since Javascript does not support negative lookbehind, but does support negative lookaheads, we use this to our advantage by reversing the string, replacing and then reversing again
demo for reversed string

Regex to find web addresses in short copy

Having a short copy I need to match all occurrences of links to websites. To keep things simple a need to find out addresses in this format:
www.aaaaaa.bbbbbb
http://aaaaaa.bbbb
https://aa.bbbb
but also I need to take care of longer www/http/https versions:
www.aaaaa.bbbb.ccc.ddd.eeee
etc. So basically number of subdomains is not known. Now I came up with this regex:
(www\.([a-zA-Z0-9-_]|\.(?!\s))+)[\s|,|$]|(http(s)?:\/\/(?!\.)([a-zA-Z0-9-_]|\.(?!\s))+)[\s|,|$]
If you test on:
this is some tex with www.somewIebsite.dfd.jhh.hjh inside of it or maybe http://www.ssss.com or maybe https://evenore.com hahaah blah
It works fine with exception of when address is at the very end. $ seems to work only when there is \n in the end and it fails for:
this is some tex with www.somewIebsite.dfd.jhh.hjh
I'm guessing fix is simple and I miss something obvious so how would I fix it? BTW I posted regex here if yu want to quickly play around https://regex101.com/r/eL1bI4/3
The problem is that you placed the end anchor $ inside the character group []
[\s|,|$]
It is then interpreted literally as a dollar sign, and not as the anchor (the pipe character | is also interpreted literally, it's not needed there). The solution is to move the $ anchor outside:
(?:[\s,]|$)
However, in this case it makes more sense to use a positive lookahead instead of the noncapturing group (you don't want trailing spaces, or commas):
(?=[\s,]|$)
In the result you will end up with the following regex pattern:
(www\.([a-zA-Z0-9-_]|\.(?!\s))+)(?=[\s,]|$)|(http(s)?:\/\/(?!\.)([a-zA-Z0-9-_]|\.(?!\s))+)(?=[\s,]|$)
See the working demo.
The updated version that handles trailing full stops:
(www\.([a-zA-Z0-9-_]|\.(?!\s|\.|$))+)(?=[\s,.]|$)|(http(s)?:\/\/(?!\.)([a-zA-Z0-9-_]|\.(?!\s|\.|$))+)(?=[\s,.]|$)
See the working demo.

RegExp \A \z doesnt work, but thats what Rails 4 requires

I recently switched to Rails 4 and the security requirements no longer seem to allow the use of regular expressions in the style of /^..$/. The error states that regular expressions should instead be written in the style of /\A..\z/. Making this change seems to resolve all of my server side validation issues, but unfortunately it also broke all of my client side validation in javascript.
A simple example. I want to validate a username to be letters, number, or periods.
The old regex looked like /^[0-9a-zA-Z.]+$/ and worked both server side (Rails 3.x) and client side
new RegExp( /^[0-9a-zA-Z.]+$/ ).test('myuser.name') = true
The new regex looks like /\A[0-9a-zA-Z.]+\z/ and works server side but fails client side
new RegExp( /\A[0-9a-zA-Z.]+\z/ ).test('myser.name') = false
So I'm clearly doing something wrong, but I can't seem to find any explanations. I checked that \A..\z are valid regex to make sure that its not some Rails-specific hack and it seems to be legit.
Any ideas?
JavaScript does not support \A or \z in its RegExp.
Here's some raw data, first for JavaScript:
var a = "hello\nworld"
(/^world/).test(a) // false
(/^world/m).test(a) // true
(/hello$/).test(a) // false
(/hello$/m).test(a) // true
Next, for ruby:
a = "hello\nworld"
a.match(/^world/) # => #<MatchData "world">
a.match(/\Aworld/) # => nil
a.match(/hello$/) # => #<MatchData "hello">
a.match(/hello\z/) # => nil
From this, we see that ruby's \A and \z are equivalent to JavaScript's ^ and $ as long as you don't use the multiline m modifier. If you are concerned about the input having multiple lines, you're simply going to have to translate your regular expressions between these two languages with respect to these matching characters.
As a workaround for \A\a \Z\z lack of support, you can add a "sentinel" character (or characters) to the end of the input string.
Please, note that:
the sentinel character(s) should be something which very low chances of being used in the input string.
should not be used in sensitive stuff (such as user verification or something) since a workaround like this can be easily exploitable.
In this specific case, since only [0-9a-zA-Z.] are allowed, something like ¨ or ~ is ok.
Example:
let inputString = 'myuser.name';
inputString = '¨0' + inputString + '¨1';
let result = new RegExp( /¨0[0-9a-zA-Z.]+(?=¨1)/ ).test(inputString);
inputString.replace(/^¨0/, '').replace(/¨1$/, '');
If you're worried that, for some reason, the input string might have the selected characters you're using, you can escape them.
(?<![\r\n])^ emulates \A, match absolute string start.
$(?![\r\n]) emulates \z, match absolute string end.
(source)
(?=[\r\n]?$(?![\r\n])) emulates \Z, match string end (before final newline if present).
If all of your line endings are \n, you can simplify the above to:
\A: (?<!\n)^
\z: $(?!\n)
\Z: (?=\n?$(?!\n))
Note: JavaScript has always supported lookahead (used for \z & \Z emulation above), but lookbehind (used for \A emulation above) support is newer, and still limited due to Safari / WebKit, see caniuse.com and bugs.webkit.org for details. Node.js has had lookbehind support since v9.

Categories

Resources