Заменить двойные кавычки кавычками
Я ищу способ заменить кавычки "исправленными" котировками в пользовательском вводе.
Идея
Вот фрагмент, кратко демонстрирующий принцип:
Для котировок "правильные" имеют открытие "
и закрытие "
, поэтому его необходимо заменить хорошим способом.
$('#myInput').on("keyup", function(e) {
// The below doesn't work when there no space before or after.
this.value = this.value.replace(/ "/g, ' "');
this.value = this.value.replace(/" /g, '" ');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="myInput"></textarea>
Ответы
Ответ 1
У меня есть решение, которое в конечном итоге соответствует всем моим потребностям.
Я признаю, что это намного сложнее, чем TJ, что может быть идеальным для простых случаев.
Помните, моя главная проблема заключалась в невозможности использования \b
из-за акцентированных символов.
Я смог избавиться от этой проблемы, используя решение из этой темы:
Удаление акцентов/диакритических знаков в строке в JavaScript
После этого я использовал модифицированную функцию, очень вдохновленную ответом здесь...
Как заменить символ в конкретном индексе в JavaScript?
... и было очень тяжело, много играя с RegEx, чтобы наконец добраться до этого решения:
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 inspiration
String.prototype.replaceQuoteAt = function(index, shift) {
const replacers = ""‘"";
var offset = 1 * (this[index] == "'") + 2 * (shift);
return this.substr(0, index) + replacers[offset] + this.substr(index + 1);
}
// Opening quote: not after a boundary, not before a space or at the end
var 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 boundary
var 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);
Ответ 2
Он работает во многих случаях, за исключением случаев, когда "слово" находится в самом начале или в конце предложения или строки.
Чтобы решить эту проблему, вы можете использовать чередование утверждения начала и конца строки и пробела, захватить его и использовать в замене:
this.value = this.value.replace(/(^| )"/g, '$1"');
this.value = this.value.replace(/"($| )/g, '"$1');
Альтернативой является ^|
/$|
, Группа захвата будет ""
если она соответствует утверждению, или " "
если она соответствует сапсе.
$('#myInput').on("keyup", function(e) {
this.value = this.value.replace(/'/g, '');
// The below doesn't work when there no space before or after.
this.value = this.value.replace(/(^| )"/g, '$1"');
this.value = this.value.replace(/"($| )/g, '"$1');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="myInput"></textarea>
Ответ 3
Поэтому вместо того, чтобы следовать подходу regex replace, я использовал бы простой цикл с актом балансировки кавычек. Вы предполагаете, что каждая отдельная цитата будет совпадать с другой, и когда она это сделает, она будет заменена как пары.
Ниже приведена тестовая реализация для того же
String.prototype.replaceAt=function(index, replacement) {
return this.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"']
]
function isCharacterSeparator(value) {
return /", /.test(value)
}
for ([data, output] of tests) {
let qt = """"
let qtL = '"'
let qtR = '"'
let bal = 0
let pattern = /["""]/g
let 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);
} else if (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}'')
} ;
}