I am looking for a JavaScript regex which will escape single quotes but it should not escape single quotes which are already escaped.
Ideally, you want every match to start exactly where the previous match ended. Otherwise it's too easy to get out of sync with the escape sequences. @outis's regex comes close, but it fails to escape the second single-quote in '\\'
. After the first match, it has to match at least one non-backslash and one single-quote, which it can't do. If there are any more characters, it skips ahead and starts matching after the second single-quote.
Try this one instead:
result = subject.replace(/([^'\\]*(?:\\.[^'\\]*)*)'/g, "$1\\'");
This is an example of Friedl's "unrolled loop" pattern:
normal * (special normal *) *
[^'\\]*
is the "normal *" part; it gobbles up any number of characters other than single-quotes or backslashes. If the next character is a backslash, \\.
("special") consumes that and the next character (backslash, single-quote, or whatever) and [^'\\]*
takes over again. Repeat as needed.
The key point is that the regex never skips ahead and it never backtracks. If it sees a backslash, it always consumes that and the next character, so it never gets out of sync.
You can write:
var escaped = original.replace(/\\['\\]|'/g, function (s) {
if (s == "'") return "\\'";
else return s;
});
If there's a contiguous sequence of escaped-escapes, it skips them all. If at the end there's a "\'", then the quote is already escaped and is also skipped. If at the end there's a "'", the quote is escaped.
Here's a solution
/[^\\]\'|^\'/g
If there are an even number of backslashes, they only quote each other. Thus a character is quoted if it has an odd number of preceding backslashes. Since JS doesn't support lookbehind, you'll need to capture the leading non-backslash and include it in the replacement.
var escquote = /((^|[^\\])(\\\\)*)'/g
"a ' b \' c \\' d".replace(escquote, "$1\\'")
However, if this is for any sort of security purposes, it's the wrong approach for a number of reasons. Firstly, if you're doing this client side, it isn't secure. Second, quoting should be handled when data is sent to a subsystem using the methods provided by the subsystem. For example, if the data is going to a relational database, you should use prepared statements and parameterize the varying data. Prepared statement parameters aren't vulnerale to injection.