I've seen some posts about JSLint "bad escapement" warnings, but I just wanted to see if I'm doing this Regex correctly. (Note - I'm dabbler programmer).
I have a function (below) that attempts to parse out a variable from it's name in a long message. The regex is working well, but should I change something in response to the JSLint warning?
A very simplified version of msg could look like this essentially:
VariableName1 = Value1
VariableName2 = Value2
VariableName3 = Value3
The actual msg has different unstructured data above and below. I had to use a strange Regex since even though a more simple one worked on all the testing websites, it didn't work within the server application we are using, so this is the only way I could get it to work. The regular expression incorporates a variable.
Here is the parsing function I'm using:
function parseValue(msg, strValueName) {
var myRegexp = new RegExp(strValueName + ' = ([A-Z3][a-zA-Z\. 3]+)[\\n\\r]+', 'gm');
log('parseValue', 'myRegexp = ' + myRegexp.toString());
var match = myRegexp.exec(msg);
log('parseValue', 'returning match = ' + match[1] );
return match[1];
}
There is probably something much simpler that a 'real' programmer can come up with pretty easily. Any help would be appreciated.
THanks.
The problem that JSLint didn't like was the '.' character in the character class as pointed out by 'Explosion Pills'.
When I removed the '.' all was good.
Thanks.
Related
I am trying to write a prototype method that first needs to escape backslashes \ when the input contains a plain single quote '. (I am aware that extending the prototype is bad practice in almost any other circumstance - this is merely a practice problem I'm trying to solve.)
I've checked out the Regex wiki and tried implementing the solutions to several regex-related questions, but I still seem to be missing something. In all of my attempts, I've been unable to 'escape the escape' as shown below:
String.prototype.escapeQuote = function () {
const regex = /\'/g;
const str = `${this}`;
const subst = `\\'`;
const result = str.replace(regex, subst);
return result;
};
var str = "this method doesn't work...";
str.escapeQuote();
When I run this code, I expect the output to be:
this method doesn\'t work...
But the output I get when I run it on repl.it is:
'this method doesn\\\'t work...'
binding subst to \' or just ' doesn't work either (perhaps it goes without saying) - either way the result is:
'this method doesn\'t work...'
I am pretty fuzzy on Regex, but trying to improve, so I'd appreciate any help you could provide - and, for that matter, any relevant answers I might have missed.
That's a rendering artifact of how the REPL you are using represents a string when it displays a string as the result of evaluating your code.
Note that it also wraps it in ' to indicate it is a string.
There are no slashes in the string itself, which you can see in this example:
String.prototype.escapeQuote = function() {
const regex = /(')/g;
const subst = `\\'`;
const result = this.replace(regex, subst);
return result;
};
const str = `doesn't this sound awesome`;
alert(str + "\n\n" + str.escapeQuote());
You should use the unescaped string inside a regular expression.
Like this:
String.prototype.escapeQuote = function(){
return this.replace(/'/g, '\\\'');
}
console.log("Try it, it's easier!".escapeQuote());
If you don't like that nasty '\\\'', you can use "\\'" instead.
Hope this works for you.
If you want to escape both single and double quotes, you can use this:
String.prototype.escapeQuotes = function(){
return this.replace(/["']/g, '\\$&');
}
console.log("Try it, it's easier!".escapeQuotes());
I'm doing some more advanced automation on iOS devices and simulators for an enterprise application. The automation is written in browserless Javascript. One of the methods works on the device but not on the simulator, so I need to code a workaround. For the curious, it's UIATarget.localTarget().frontMostApp().preferencesValueForKey(key).
What we need to do is read a path to a server (which varies) from a plist file on disk. As a workaround on the simulator, I've used the following lines to locate the plist file containing the preferences:
// Get the alias of the user who's logged in
var result = UIATarget.localTarget().host().performTaskWithPathArgumentsTimeout("/usr/bin/whoami", [], 5).stdout;
// Remove the extra newline at the end of the alias we got
result = result.replace('\n',"");
// Find the location of the plist containing the server info
result = UIATarget.localTarget().host().performTaskWithPathArgumentsTimeout("/usr/bin/find", ["/Users/"+result+"/Library/Application Support/iPhone Simulator", "-name", "redacted.plist"], 100);
// For some reason we need a delay here
UIATarget.localTarget().delay(.5);
// Results are returned in a single string separated by newline characters, so we can split it into an array
// This array contains all of the folders which have the plist file under the Simulator directory
var plistLocations = result.stdout.split("\n");
...
// For this example, let's just assume we want slot 0 here to save time
var plistBinaryLocation = plistLocations[0];
var plistXMLLocation = plistLocations[i] + ".xml";
result = UIATarget.localTarget().host().performTaskWithPathArgumentsTimeout("/usr/bin/plutil", ["-convert","xml1", plistBinaryLocation,"-o", plistXMLLocation], 100);
From here, I think the best way to get the contents is to cat or grep the file, since we can't read the file directly from disk. However, I'm having trouble getting the syntax down. Here's an edited snippet of the plist file I'm reading:
<key>server_url</key>
<string>http://pathToServer</string>
There are a bunch of key/string pairs in the file, where the server_url key is unique. Ideally I'd do something like a lookback, but because JavaScript doesn't appear to support it, I figured I'd just get the pair from the file and whittle it down a bit later.
I can search for the key with this:
// This line works
var expression = new RegExp(escapeRegExp("<key>server_url</key>"));
if(result.stdout.match(expression))
{
UIALogger.logMessage("FOUND IT!!!");
}
else
{
UIALogger.logMessage("NOPE :(");
}
Where the escapeRegExp method looks like this:
function escapeRegExp(str)
{
var result = str.replace(/([()[{*+.$^\\|?])/g, '\\$1');
UIALogger.logMessage("NEW STRING: " + result);
return result;
}
Also, this line returns a value (but gets the wrong line):
var expression = new RegExp(escapeRegExp("<string>(.*?)</string>"));
However, when you put the two together, it (the Regex syntax) works on the terminal but doesn't work in code:
var expression = new RegExp(escapeRegExp("<key>server_url</key>[\s]*<string>(.*?)</string>"));
What am I missing? I also tried grep and egrep without any luck.
There are two problems affecting you here getting the regex to work in your JavaScript code.
First, you are escaping the whole regex expression string, which means that your capturing (.*?) and your whitespace ignoring [\s]* will also be escaped and won't be evaluated the way you're expecting. You need to escape the XML parts and add in the regex parts without escaping them.
Second, the whitespace ignoring part, [\s]* is falling prey to JavaScript's normal string escaping rules. the "\s" is turning into "s" in the output. You need to escape that backslash with "\s" so that it stays as "\s" in the string that you pass to construct the regular expression.
I've built a working script that I've verified in the UI Automation engine itself. It should extract and print out the expected URL:
var testString = "" +
"<plistExample>\n" +
" <key>dont-find-me</key>\n" +
" <string>bad value</string>\n" +
" <key>server_url</key>\n" +
" <string>http://server_url</string>\n" +
"</plistExample>";
function escapeRegExp(str)
{
var result = str.replace(/([()[{*+.$^\\|?])/g, '\\$1');
UIALogger.logMessage("NEW STRING: " + result);
return result;
}
var strExp = escapeRegExp("<key>server_url</key>") + "[\\s]*" + escapeRegExp("<string>") + "(.*)" + escapeRegExp("</string>");
UIALogger.logMessage("Expression escaping only the xml parts:" + strExp);
var exp = new RegExp(strExp);
var match = testString.match(exp);
UIALogger.logMessage("Match: " + match[1]);
I should point out, though, that the only thing you need to escape in the regex is the forward slashes in the XML closing tags. That means that you don't need your escapeRegExp() function and can write the expression you want like this:
var exp = new RegExp("<key>server_url<\/key>[\\s]*<string>(.*)<\/string>");
Hey all, having an issue doing string replacement for template engine code I am writing. If my tokens are 1 level deep everything works fine. Example {someProperty}. But if I try searching for a nested object it never does the replace. Example {myobj.deep.test}. I've attached the code I am playing around with. Thanks for the help!
function replaceStuff(content, fieldName, fieldValue) {
var regexstr = "{" + fieldName + "}";
console.log("regexstr: ", regexstr);
//var regex = new RegExp("{myobj\.deep\.test}", "g"); //this works as expected
var regex = new RegExp(regexstr, "g"); //this doesn't
return content.replace(regex, fieldValue);
}
replaceStuff("test: {myobj.deep.test}", "myobj.deep.test", "my value");
See this SO question about curly braces. Perhaps your browser of choice isn't as understanding as chrome is?
You need to escape the '.' characters in the string you're passing in as the fieldName parameter. Ditto for any other special regex characters that you want to be interpreted literally. Basically, fieldName is being treated as part of a regex pattern.
If you don't want fieldName to be evaluated as regex code, you might want to consider using string manipulation for this instead.
Edit: I just ran your code in FireFox and it worked perfectly. You may have something else going on here.
I have an HTML page with a text box. This text box has an id of "myTextBox". I am trying to use a regular expression in JavaScript to replace certain values in the text box with another value. Currently, I am trying the following
function replaceContent()
{
var tb = document.getElementById("myTextBox");
if (tb != null)
{
var current = new String(tb.value);
var pattern = new RegExp("(ft.)|(ft)|(foot)", "ig");
current = current.replace(pattern, "'");
alert(current);
}
}
Based on this code, if I had the value "2ft" in the myTextBox, I would expect the current variable to be "2'". However, it always shows an empty string. I fear there is something that I am misunderstanding in relation to regular expressions in JavaScript. What am I doing wrong?
Thank you!
Rewrite your pattern to:
(ft\.?|foot|feet)
This will match "ft" or "ft." and "foot" or "feet".
When writing code that uses regular expressions that won't behave, assume (first) that it is your regex that is the problem. Thanks to the compact and esoteric "programming" of regular expressions, it's quite easy to make a mistake you don't see.
If you test the following in Firebug to yield a correct result:
"2ft".replace(/(ft\.?|foot|feet)/ig, "'")
You will get "2'" in your console.
So this answer ought to solve your problem, if the regex is your problem in the first place. Like Rubens said, please check the ID of your textbox and ensure that the item is correctly retrieved.
As The Wicked Flea says, the literal '.' in your regex needs to be escaped. If it's in a character class it can be written un-escaped, but outside of one it must be escaped.
The following works for me:
<script>
var current = "2ft";
var pattern = new RegExp("(ft.)|(ft)|(foot)", "ig");
current = current.replace(pattern, "'");
alert(current);
</script>
Are you sure that tb.value is evaluating properly?
I ran your code using IE8,Chrome and FF3 with no problems.
Please check if your textbox id is correct.
The problem is that you're alternating explicit groupings. Instead you can just do something like the following:
function replaceContent()
{
var tb = document.getElementById("myTextBox");
if (tb != null) {
current = tb.value.replace(/\s*(ft\.|ft|foot|feet)\b/ig, "'");
alert(current);
}
}
Also, note the \s* which will strip leading spaces and the \b which marks the beginning/end of a word. I also added feet.
I'm performing this on a string:
var poo = poo
.replace(/[%][<]/g, "'<")
.replace(/[>][%]/g, ">'")
.replace(/[%]\s*[+]/g, "'+")
.replace(/[+]\s*[%]/g, "+'");
Given the similar if these statements, can these regexs be comebined somehow?
No, I don't think so. At least, I suspect for any transformation involving fewer replaces I can come up with a string that your original and the proposed alternative treat differently. However, it may be that the text you're working with wouldn't trigger the differences, and so for practical purposes a shorter transformation would work as well. Depends on the text.
You can simplify it a little bit. You don't need all the range syntax
poo
.replace(/%</g, "'<")
.replace(/>%/g, ">'")
.replace(/%\s*\+/g, "'+")
.replace(/\+\s*%/g, "+'");
Since in either case, the replacement only turns % into ' and removes spaces:
var poo = 'some annoying %< string >% with some % + text + %';
poo = poo.replace(/%<|>%|%\s*\+|\+\s*%/g, function(match) {
return match.replace('%', '\'').replace(/\s/g,'');
});
// "some annoying '< string >' with some ' + text + '"
Although that's not much simpler...
Using lookahead assertions and capturing:
var poo = poo.replace(/%(?=<)|(>)%|%\s*(?=\+)|(\+)\s*%/g, "$1$2'");
Using capturing alone:
var poo = poo.replace(/(>)%|(\+)\s*%|%(<)|%\s*(\+)/g, "$1$2'$3$4");
If JS's RegExp supported lookbehind assertions:
var poo = poo.replace(/%(?=<)|(?<=>)%|%\s*(?=\+)|(?<=\+)\s*%/g, "'");
but it doesn't.