Skip to content

Instantly share code, notes, and snippets.

@getify
Last active June 25, 2022 16:26
Show Gist options
  • Star 43 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save getify/3667624 to your computer and use it in GitHub Desktop.
Save getify/3667624 to your computer and use it in GitHub Desktop.
escape all (not-already-escaped) double-quote chars in a string
// NOTE: only escapes a " if it's not already escaped
function escapeDoubleQuotes(str) {
return str.replace(/\\([\s\S])|(")/g,"\\$1$2"); // thanks @slevithan!
}
escapeDoubleQuotes(`ab`); // ab => ab (nothing escaped)
escapeDoubleQuotes(`a"b`); // a"b => a\"b
escapeDoubleQuotes(`a\\"b`); // a\"b => a\"b (nothing escaped)
escapeDoubleQuotes(`a\\\\"b`); // a\\"b => a\\\"b
escapeDoubleQuotes(`a\\\\\\"b`); // a\\\"b => a\\\"b (nothing escaped)
escapeDoubleQuotes(`a"b"c`); // a"b"c => a\"b\"c
escapeDoubleQuotes(`a""b`); // a""b => a\"\"b
escapeDoubleQuotes(`""`); // "" => \"\"
// don't unnecessarily escape:
escapeDoubleQuotes(escapeDoubleQuotes(escapeDoubleQuotes(`a"b`))); // a"b => a\"b
@mathiasbynens
Copy link

Wait… Why not just str.replace(/\x22/g, '\\\x22')?

@getify
Copy link
Author

getify commented Sep 7, 2012

@mathiasbynens i only want to escape a " if it's not already escaped.

@slevithan
Copy link

This is cleaner, more readable/obvious (IMO, and more efficient: str.replace(/\\([\s\S])|(")/, "\\$1$2").

@slevithan
Copy link

Oops, need to add the /g flag to that, of course.

@getify
Copy link
Author

getify commented Sep 7, 2012

@slevithan yeah, i think yours actually works globally, where mine does not. mine fails on """", but yours works. O_o. thanks! :)

@WebReflection
Copy link

entirely based on missing group(s) ... nice one!

@getify
Copy link
Author

getify commented Sep 7, 2012

@slevithan for posterity sake, can you, in this thread, briefly explain how that regex works? I only sorta get it, having trouble wrapping my brain around it.

@WebReflection
Copy link

however ... is this expected? '\"'.replace(/([\s\S])|(")/, "$1$2") === '"'

@WebReflection
Copy link

the regexp works that if encountered anything with a \ before nothing happen, the second match will be empty, the first one will be after the \ in the replacement so .... (escaped)|(") will match (escaped) ? will put $1 and after an empty $2 while if the char is not escaped, after the "|" (or) the escaping is placed and match $1 will be empty ... (($1)|($2)) => $1$2 ... got it? I am notsure it works with my last test thought ...

@WebReflection
Copy link

let me try again semantically ... that regexp says:

is there an escaped (something) ? escape(something) : is the char a " ? escape(") ... the replace escapes in any case but only (soemthing) OR (quote) are actually placed there: about empty, ignored, matches ... maybe better ...

@slevithan
Copy link

@WebReflection, the line should be this:

'\\"'.replace(/\\([\s\S])|(")/g, "\\$1$2") === '\"'

In which case the result is false, which is both expected and correct, due to JavaScript string escaping rules.

@getify, the regex works by matching one of two alternatives:

  1. A backslash followed by any character ([\s\S] is used instead of a dot so that escaped newlines also count), or...
  2. A double quote character, on its own.

If the first alternative matches, the matched string will always start with a backslash, and the match length is always 2. The escaped character at position 2 (not including the preceding backslash at position 1) is captured to backreference 1. In this case, capturing group 2 (surrounding the bare double-quote) did not participate. Because all escaped characters are matched this way (including escaped double quotes and escaped backslashes), that leaves us free to cleanly deal with the second regex alternative...

If a double quote is matched and stored in backreference 2, the match length is 1, and capturing group 1 (within the first regex alternative) did not participate. That also means that the double quote character is definitely unescaped, since escaped characters (including escaped double quotes) are always matched by the first regex alternative.

So at this point we know that all matches are either an escaped character and its preceding backslash, or an unescaped double quote. Either way, we want the character to be replaced by an escaped version of itself. The only concern is that we don't want to double-escape any characters that were already escaped and that were therefore matched by the first regex alternative. This ends up being easy to deal with, though, since we can simply exclude the escaping backslash that we matched using the first regex alternative from capturing group 1. That means that one of backreference 1 or 2 is always a single character that needs to be (or remain) escaped, and the other is always a nonparticipating group (which would be returned as undefined by e.g. regexp.exec, but which is an empty string when used in a replacement string). Therefore, by always including a literal backslash as the first character in the replacement string, we always get the correct output: an escaping backslash followed by the character it escapes.

@WebReflection
Copy link

@slevithan that was actually a typo, I meant '"'.replace(/([\s\S])|(")/g, "$1$2") === '"' is this correct?

@WebReflection
Copy link

actually is not a typo ... github keeps replacing stuff ... '\"'.replace(/([\s\S])|(")/g, "$1$2") === '\"' is this correct?

@slevithan
Copy link

@WebReflection, you're still misquoting both the regex and the replacement string. To mark text as code in GitHub comments, put it between grave accent characters.

@getify, for the record, my solution can easily be adapted for other characters that need to be escaped, by simply changing the character or list of characters matched within capturing group 2. E.g., to escape unescaped letters:

str.replace(/\\([\s\S])|([a-z])/ig, "\\$1$2")

@getify
Copy link
Author

getify commented Sep 7, 2012

Wow, thanks for that wonderful explanation, @slevithan. That rocks!

@WebReflection
Copy link

@slevithan correct so ... '\\"'.replace(/\\([\s\S])|(")/, "\$1$2") === '\\"' is this expected?

@WebReflection
Copy link

uhm .... '\\"'.replace(/\\([\s\S])|(")/g,"\\$1$2") === '\\"'

@shmdhussain
Copy link

shmdhussain commented Feb 5, 2019

@WebReflection, you're still misquoting both the regex and the replacement string. To mark text as code in GitHub comments, put it between grave accent characters.

@getify, for the record, my solution can easily be adapted for other characters that need to be escaped, by simply changing the character or list of characters matched within capturing group 2. E.g., to escape unescaped letters:

str.replace(/\\([\s\S])|([a-z])/ig, "\\$1$2")

thanks for explanation, so we could use the same regex to escape any character or group of character, I used for single and double quotes escape.

str.replace(/\([\s\S])|(['"])/ig, "\$1$2")

@stoufa
Copy link

stoufa commented Dec 24, 2020

Thanks for sharing @getify
However, If the escaped string would be injected into HTML markup, you need the following version of the function:

function encodeHTML(s) {
  return s.split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;').split("'").join('&#39;');
}

Source: Are single/double quotes allowed inside HTML attribute values? - Stack Overflow

@laniltee
Copy link

laniltee commented May 5, 2021

Thanks this worked !!!

@albanx
Copy link

albanx commented Nov 9, 2021

THanks this is a good one

@jhallal
Copy link

jhallal commented Apr 12, 2022

When you need to use both single and double quotes in the string you can use the following solutions to escape the quotes:

  • " becomes "
  • ' becomes '

Keep in mind that you will never need to escape more than the following five special characters:

  • & becomes &
  • < becomes <
  • becomes >
  • " becomes "
  • ' becomes '

Once you have these escaped you should never have an issue with broken values in HTML again.

Reference: https://jamilhallal.blogspot.com/2022/04/javascript-escape-quotes.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment