Skip to content Skip to sidebar Skip to footer

Replace Double Quotes By Quotation Marks

I am looking for a way to replace the quotes with “corrected” quotations marks in an user input. The idea Here is a snippet briefly showing the principle: For quotes, the “co

Solution 1:

It is working for many of the cases, at the exception of when the "word" is at the very beginning or the very end of a sentence or a line.

To solve that problem, you can use an alternation of a beginning/end of line assertion and the space, capture that, and use it in the replacement:

this.value = this.value.replace(/(^| )"/g, '$1“');
this.value = this.value.replace(/"($| )/g, '”$1');

The alternation is ^| / $|. The capture group will be "" if it matched the assertion, or " " if it matched the sapce.

$('#myInput').on("keyup", function(e) {
  this.value = this.value.replace(/'/g, '’');
  // The below doesn't work when there's no space before or after.this.value = this.value.replace(/(^| )"/g, '$1“');
  this.value = this.value.replace(/"($| )/g, '”$1');
});
<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><textareaid="myInput"></textarea>

However, you've said you want to avoid "escaping" characters on user input. I'm not sure where you're planning to use it, but something like the above is almost never the approach to use to a problem with that sort of description.

Solution 2:

I got a solution that finally fits all my needs. I admit it is a lot more complicated than T.J.'s one, which can be perfect for simple cases.

Remember, my main problem was the impossilibity to use \b because of the accented characters. I was able to get rid of that issue by using the solution from this topic: Remove accents/diacritics in a string in JavaScript

After that, I used a modified function highly inspired from the answer here… How do I replace a character at a particular index in JavaScript?

… and had a very hard time, playing a lot with RegEx to finally get to that solution:

var str_orig = `· I'm "happy" ! Ça y est, j'ai "osé", et mon "âme sœur" était au rendez-vous…
· The sign says: "Some text "some text" some text." and "Note the space here !"
⋅ "Inc"or"rect" quo"tes should " not be replaced.
· I said: "If it works on 'singles' too, I'd love it even more!"
word1" word2"
word1 word2"
"word1 word2
"word1" word2
"word1" word2"
"word1 word2"`;

// Thanks, exactly what I needed!var str_norm = str_orig.normalize('NFD').replace(/[\u0300-\u036f]/g, '');

// Thanks for inspirationString.prototype.replaceQuoteAt = function(index, shift) {
  const replacers = "“‘”’";
  var offset = 1 * (this[index] == "'") + 2 * (shift);
  returnthis.substr(0, index) + replacers[offset] + this.substr(index + 1);
}

// Opening quote: not after a boundary, not before a space or at the endvar re_start = /(?!\b)["'](?!(\s|$))/gi;
while ((match = re_start.exec(str_norm)) != null) {
  str_orig = str_orig.replaceQuoteAt(match.index, false);
}

// Closing quote: not at the beginning or after a space, not before a boundaryvar re_end = /(?<!(^|\s))["'](?!\b)/gi;
while ((match = re_end.exec(str_norm)) != null) {
  str_orig = str_orig.replaceQuoteAt(match.index, true);
}

console.log("Corrected: \n", str_orig);

And below is a snippet of a working example with a textarea. I've just created a function of the code of the first snippet, and I'm using a substring around the caret position to filter the calling of the function (that avoids calling it on every character input):

String.prototype.replaceQuoteAt = function(index, offset) {
  const replacers = "“‘”’";
  var i = 2 * (offset) + 1 * (this[index] == "'");
  returnthis.substr(0, index) + replacers[i] + this.substr(index + 1);
}

functionreplaceQuotes(str) {
  var str_norm = str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
  var re_quote_start = /(?!\b)["'](?!(\s|$))/gi;
  while ((match = re_quote_start.exec(str_norm)) != null) {
    str = str.replaceQuoteAt(match.index, false);
  }
  var re_quote_end = /(?<!(^|\s))["'](?!\b)./gi;
  while ((match = re_quote_end.exec(str_norm)) != null) {
    str = str.replaceQuoteAt(match.index, true);
  }
  return str;
}

var pasted = 0;
document.getElementById("myInput").onpaste = function(e) {
  pasted = 1;
}

document.getElementById("myInput").oninput = function(e) {
  var caretPos = this.selectionStart; // Gets caret positionvar chars = this.value.substring(caretPos - 2, caretPos + 1); // Gets 2 chars before caret (just typed and the one before), and 1 char just afterif (pasted || chars.includes(`"`) || chars.includes(`'`)) { // Filters the calling of the functionthis.value = replaceQuotes(this.value); // Calls the functionif (pasted) {
      pasted = 0;
    } else {
      this.setSelectionRange(caretPos, caretPos); // Restores caret position
    }
  }
}
#myInput {
  width: 90%;
  height: 100px;
}
<textareaid="myInput"></textarea>

It seems to work with all I can imagine right now. The function correctly replaces the quotes when: ⋅ typing regularly, ⋅ adding quotes after we typed the text, ⋅ pasting text.

It replaces both the double and the singles quotes.

Anyway, as I am not a RegEx expert at all, please feel free to comment if you notice a behaviour that may be unwanted, or a way to improve the expressions.

Solution 3:

So instead of following a regex replace approach, I would use a simple looping with a quotes balancing act. You assume the every single quote that appears will match with another one and when it does it will be replaced as pairs.

Below is a test implementation for the same

String.prototype.replaceAt=function(index, replacement) {
returnthis.substr(0, index) + replacement+ this.substr(index + replacement.length);
}

tests  =[
// [`I'm "happy"! J'ai enfin "osé". La rencontre de mon "âme-sœur" a "été" au rendez-vous…// and how it should look after correction:`, `I'm "happy"! J'ai enfin "osé". La rencontre de mon "âme-sœur" a "été" au rendez-vous…// and how it should look after correction:`],
[`tarun" lalwani"`, `tarun” lalwani”`],
[`tarun lalwani"`, `tarun lalwani”`],
[`"tarun lalwani`,`“tarun lalwani`],
[`"tarun" lalwani`,`“tarun” lalwani`],
[`"tarun" lalwani"`,`“tarun” lalwani”`],
[`"tarun lalwani"`, `“tarun lalwani”`]
]

functionisCharacterSeparator(value) {
return/“, /.test(value)
}

for ([data, output] of tests) {
let qt = "“”"let qtL = '“'let qtR = '”'let bal = 0let pattern = /["“”]/glet data_new = data
while (match = pattern.exec(data)) {
    if (bal == 0) {
        if (match.index == 0) {
            data_new = data_new.replaceAt(match.index, qt[bal]);
            bal = 1
        } else {
            if (isCharacterSeparator(data_new[match.index-1])) {
                data_new = data_new.replaceAt(match.index, qtL);
            } else {
                data_new = data_new.replaceAt(match.index, qtR);
            }
        }
    } else {
        if (match.index == data.length - 1) {
            data_new = data_new.replaceAt(match.index, qtR);
        } elseif (isCharacterSeparator(data_new[match.index-1])) {
            if (isCharacterSeparator(data_new[match.index+1])) {
                //previous is separator as well as next one too// "tarun " lalwani"// take a call what needs to be done here?

            } else {
                data_new = data_new.replaceAt(match.index, qtL);
            }
        } else {
            if (isCharacterSeparator(data_new[match.index+1])) {
                data_new = data_new.replaceAt(match.index, qtL);
            } else {
                data_new = data_new.replaceAt(match.index, qtR);
            }
        }
    }


}

console.log(data_new)
if (data_new != output) {
  console.log(`Failed to parse '${data}' Actual='${data_new}' Expected='${output}'`)
} ;
}

Update-1: 20-Apr-2018

I have updated the function. There still may be some edge cases, but you should put everything in the test and run it and fix the ones that don't behave as expected

Post a Comment for "Replace Double Quotes By Quotation Marks"