Contents of a RegExp match - javascript

I've been trying to convert the following from Python to node.js. It's a simply program that uses regex to check if an IP address is Public or Private:
import re
def is_private_ip(ip):
"""
Returns `True` if the `ip` parameter is a private network address.
"""
c = re.compile('(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)')
if c.match(ip): return True
return False
print is_private_ip('192.168.0.1') # True
print is_private_ip('8.8.8.8') # False
print is_private_ip('109.231.231.221') # False
I implemented it in Javascript like this:
var localIp = new RegExp(/(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/);
console.log('192.168.0.1'.match(localIp));
console.log('8.8.8.8'.match(localIp));
console.log('109.231.231.221'.match(localIp));
Which gives me the following output:
[ '192.168.',
undefined,
undefined,
undefined,
undefined,
undefined,
'192.168.',
index: 0,
input: '192.168.0.1' ]
null
null
It seems to me like it works (not even sure tbh). The two IPs that should be public are returning null so I'm guessing that's right. I don't understand the output of the other match though? I've not been able to find out what it means

var localIp = new RegExp(/(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/);
console.log('192.168.0.1'.match(localIp));
gives you the output:
[ '192.168.',
undefined,
undefined,
undefined,
undefined,
undefined,
'192.168.']
That means:
'192.168.' that is the match of the regex on this string. the only one
undefined is the match for the first group in your regex: (^127\.0\.0\.1)
undefined for the group: (^10\.)
undefined for the group: (^172\.1[6-9]\.)
undefined for the group: (^172\.2[0-9]\.)
undefined for the group: (^172\.3[0-1]\.)
'192.168.' for the group: (^192\.168\.)
thats because of the parenthesis, each one of them giving a match (or undefined), plus the match that the match() function returns.

.match() gives you the number of matches possible in your String. Maybe what you're looking for is .test() method.
You need to modify the code like this:
var localIp = new RegExp(/(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/);
console.log(localIp.test('192.168.0.1'));
console.log(localIp.test('8.8.8.8'));
console.log(localIp.test('109.231.231.221'));
You may refer here for more details regarding the match method: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/match

You are using the String.prototype.match-method. According to the documentation, it returns "An Array containing the matched results or null if there were no matches."
In Javascript, an Array is truthy and null is falsy. This means that the following check would indeed correctly test if a string is a local IP:
if(someIpString.match(localIp)) {
// it is a local IP
}
else {
// it is not a local IP
}
What you are seeing in the array is the different parts of the original string that were matched by matching groups in the regular expression. The null values are the matches for groups that are not present, which you have plenty of.
But I think you can go a step further. If you want to simply check if a string matches the regular expression, I would recommend RegExp.prototype.test. This method returns a boolean (true|false), so you don't have to rely on truthy- or falsy-ness:
if(localIp.test(someIpString)) {
// it is a local IP
}
else {
// it is not a local IP
}

String.prototype.match():
If the regular expression does not include the g flag, returns the same result as RegExp.exec(). The returned Array has an extra input property, which contains the original string that was parsed. In addition, it has an index property, which represents the zero-based index of the match in the string.
RegExp.prototype.exec():
The returned array has the matched text as the first item, and then one item for each capturing parenthesis that matched containing the text that was captured.
If the match fails, the exec() method returns null.
Maybe you want to use RegExp.prototype.test() instead:
var localIp = new RegExp(/(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/);
console.log(localIp.test('192.168.0.1')); // => true
console.log(localIp.test('8.8.8.8')); // => false
console.log(localIp.test('109.231.231.221')); // => false

You don't need to use match groups, unless you want to capture the matching portion of the ip address, but that's not necessary in your case. In Javascript you can use this regex pattern (note no match groups):
var localIp = new RegExp(/^127\.0\.0\.1|^10\.|^172\.1[6-9]\.|^172\.2[0-9]\.|^172\.3[0-1]\.|^192\.168\./);
And then use it like this:
console.log('192.168.0.1'.match(localIp) != null);
console.log('8.8.8.8'.match(localIp) != null);
console.log('109.231.231.221'.match(localIp) != null);
Or, even better, use RegEx.test():
console.log(localIp.test('192.168.0.1'));
Similarly for Python, the match groups are not required.
One other thing worth noting is that your pattern will match invalid IP addresses, e.g. 10.bad.ip.address will be detected as a private IP address. Not a problem if are validating IP addresses elsewhere in your application, but you might want to tighten it up.

Related

Why JS Regexp.exec returns an array with more elements than expected?

I'm attempting to regex match various duration strings (e.g. 1d10h, 30m, 90s, etc.) and have come up with a regex string to split the string into pieces, but it seems that I'm getting two undefined results at the ends that shouldn't be there. I imagine it has to do with the greedy matching via the ? groupings, but I'm not sure how to fix it.
My code looks like this:
const regex = /^(\d+?[d])?(\d+?[h])?(\d+[m])?(\d+[s])?$/gmi
const results = regex.exec('1d10h')
and the results I get look like so:
[
"1d10h",
"1d",
"10h",
undefined,
undefined,
]
I was only expecting the first three results (and in fact, I only really want 1d and 10h) but the two remaining undefined results keep popping up.
You have 4 groups in the regular expression - each enclosed with braces ( ... ) and enumerated naturally - the earlier opening brace appear in the expression the lower order index a group has.
And, of course, the whole match that could be named a "zero" group.
So, result of regex.exec('1d10h') contains 5 items:
results[0] - the whole expression match
results[i] - match of each group, i in {1,2,3,4}
Since in this case each group is optional (followed by ?) - it is allowed to have undefined in place of any unmatched group.
It is easy to see that if you remove a ? symbol after an unmatched group, the whole expression will fail to match and hence regex.exec('1d10h') will return null.
To get rid of undefined elements just filter them out:
const result = regex.exec('1d10h').filter(x => x);

Empty value if regex match not found JavaScript

I'm attempting to extract any text characters at the beginning, and the following two numbers of a string. If the string starts with a number, I'd like to get an empty string value instead so the resulting array still contains 3 values.
String:
'M2.55X.45'
Code:
'M2.55X.45'.match(/(^[a-zA-Z]+)|((\.)?\d+[\/\d. ]*|\d)/g)
Expected:
["M", "2.55", ".45"]
Actual (correct):
["M", "2.55", ".45"]
String:
'2.55X.45'
Code:
'2.55X.45'.match(/(^[a-zA-Z]+)|((\.)?\d+[\/\d. ]*|\d)/g)
Expected:
["", "2.55", ".45"]
Actual:
["2.55", ".45"]
Use /^([a-zA-Z]?)(\d*(?:\.\d+)?)[a-zA-Z](\d*(?:\.\d+)?)$/.exec("2.55X.45") instead. This returns an array where the 1st element is the entire match, so you must access groups 1-indexed, for example, match[1] for the 1st value. You can try this out here.
Your current regex uses an alternate clause (|), which creates different types of grouping depending on which alternate is matched.
Here's an example (cleaned up a bit) that creates explicit groups and makes the individual groups optional.
const regex = /^([a-zA-Z]*)?(\d*(?:\.\d+)?)([a-zA-Z]+)(\d*(?:\.\d+)?)$/
console.log(regex.exec("2.55X.45"))
console.log(regex.exec("M2.55X.45"))
Note that I've removed the g flag, so the regex's state isn't preserved.
I've also used exec instead of match to not discard capture groups.
You can try this pattern
(\D*)(\d+(?:\.\d+))\D+(\.\d+)
let finder = (str) => {
return (str.match(/^(\D*)(\d+(?:\.\d+))\D+(\.\d+)/) || []).slice(1)
}
console.log(finder('M2.55X.45'))
console.log(finder("2.55X.45"))

Is there a method that returns only the match or first variable match of a RegEx in ActionScript or null?

Is there a method yet that returns only a match and null if there is no match?
It may be a brain freeze but is there a method that just returns the match and null if not?
For example, if I want to just return the first variable or null otherwise:
var value:String = "image/png".toLowerCase();
var result:String = value.find(/image/(png|jpg|jpeg|gif)/);
if (result=="png") {
// do something
}
I know there's replace and exec and match and they return arrays etc but I'm just looking for a method that returns my first match or null.
If there isn't this a function like this, I wish there was. How to god I wish there was.
There is no specific method that does that. The closest method is String.match or RegExp.exec and it makes little difference here since the regex has no global modifier. Once you use a regex with a global modifier, you will have no choice but to use RegExp.exec.
Using String.match or RegExp.exec you can check if the result is null and if it's not null then extract the Group 1 value to check if it is png:
var pattern:RegExp = /image\/(png|jpg|jpeg|gif)/;
var str:String = "image/png".toLowerCase();
var m:Array = pattern.exec(str);
if (m != null)
{
if (m[1] == "png") {
// Do stuff
}
}
Note that a / must be escaped in a regex literal.
You can also contract the pattern a bit to (png|jpe?g|gif), but though it will work better internally, it will become less readable.

Regex matching to exclude beginning word

How can I make the following JavaScript
'id=d41c14fb-1d42&'.match(/(?![id=])(.*)[^&]/)[0]
return "d41c14fb-1d42"? It currently returns "41c14fb-1d42", without the beginning "d".
This should do it:
'id=d41c14fb-1d42&'.match(/^id=(.*)&$/)[1]
// "d41c14fb-1d42"
~ edit
When an original question does not give any context it is hard to guess what you need exactly, in which case I just give a solution that yields the requested output. I now understand you want to be able to parse a query string, i.e., name1=value1&name2=value2.... The following regular expression yields nameX followed by an optional =valueX, as I believe it is valid to provide just the parameter in a query string without a value.
var parameters = "id=foobar&empty&p1=javascript&p2=so".match(/\w+(=([^&]+))?/g)
// ["id=foobar", "empty", "p1=javascript", "p2=so"]
You can split on "=" to obtain parameter and value separately.
var param1 = parameters[0].split("=")
// ["id", "foobar"]
For a parameter without the value that would just yield an Array with 1 value, of course.
"empty".split("=") // ["empty"]
Note this assumes a query parameter to match \w+, if there are other cases you have to expand the regular expression.
If you only want to match the id parameter specifically anywhere in the query string and obtain its value, as I infer from your comment below, you can use:
"id=donald".match(/(?:^|&)id=([^&]+)/)[1]
// "donald"
"param1=value1&id=donald&param2=value2".match(/(?:^|&)id=([^&]+)/)[1]
// "donald"

Iterate through regular expression array in Javascript

How can I use an array of Regex expressions and iterate that array with 'exec' operation. I did initialize an array with various regular expressions like this:
var arrRegex = new Array(/(http:\/\/(?:.*)\/)/g, /(http:\/\/(?:.*)\/)/g);
Now I created a for loop that does this:
for(i=0;i<arrRegex.length;i++){
arrRegex[i].exec(somestring);
}
The thing is that this doesn't seems to work. I don't want to use it hardcoded like this:
(/(http:\/\/(?:.*)\/)/g).exec(somestring);
When using the array option, the '.exec' function returns null. When I use the hardcoded option it returns the matches as I wanted.
The exec() returns the match so you should be able to capture it.
somestring = 'http://stackoverflow.com/questions/11491489/iterate-through-regular-expression-array-in-javascript';
var arrRegex = new Array(/(http:\/\/(?:.*)\/)/g, /(http:\/\/(?:.*)\/)/g);
for (i = 0; i < arrRegex.length; i++) {
match = arrRegex[i].exec(somestring);
}
match is an array, with the following structure:
{
[0] = 'string matched by the regex'
[1] = 'match from the first capturing group'
[2] = 'match from the second capturing group'
... and so on
}
Take a look at this jsFiddle http://jsfiddle.net/HHKs2/1/
You can also use test() instead of exec() as a shorthand for exec() != null. test() will return a boolean variable depending on whether the regex matches part of the string or not.
What you probably want to do is to capture the first group:
for(i=0;i<arrRegex.length;i++){
var someotherstring = arrRegex[i].exec(somestring)[1];
// do something with it ...
}
BTW: That is my guess, not sure what you are trying to do. But if you are trying to get the host name of a URL you should use /(http:\/\/(?:.?)\/)/g. The question mark after .* makes the previous quantifier (*) ungreedy.

Categories

Resources