用引号

时间:2018-04-13 11:35:22

标签: javascript jquery regex

我正在寻找一种方法,用用户输入中的“更正”引号替换引号。

想法

这是一段简要说明原则的片段:
对于引号,“正确”的引号具有开头和结束,因此需要以良好的方式进行替换。

$('#myInput').on("keyup", function(e) {
  // The below doesn't work when there's 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>

但上述情况并非在所有情况下都有效 例如,当“引用的单词”位于句子或行的开头或结尾时。

实施例

可能的输入(小心,法语里面!:)):
⋅我很开心! Çayest,j'ai“osé”,et mon“âmesœur”étaitaurendez-vous ...
⋅标志上写着:“有些文字”有些文字“有些文字”。并“注意这里的空间!”
⋅“Inc”或“rect”quo“tes should”不得更换 ⋅我说:“如果它也适用于'单身',我会更喜欢它!”

正确的输出:
⋅我很开心! Çayest,j'ai“osé”,et mon“âmesœur”étaitaurendez-vous ...
⋅标志上写着:“有些文字”有些文字“有些文字。”和“注意这里的空间!” ⋅“Inc”或“rect”quo“tes should”不得更换 ⋅我说:“如果它也适用于'单身',我会更喜欢它!”

输出错误:
⋅标志上写着:“有些文字”有些文字“有些文字。”和[...]
为何不正确:
→报价结尾与结束标记之间不应有空格 →在右引号和单词之间应该有一个空格 →单词和开头引号之间应该有一个空格 →开头引号与其报价之间不应有空格。

需要

在所有这些情况下,如何有效和轻松地替换报价? 如果可能的话,我也希望解决方案能够“纠正”引号,即使我们在输入整个句子之后添加

请注意,我不能(不能)在正则表达式中使用单词分隔符“\ b”,因为“重音字符,例如”é“或”ü“不幸地被视为单词分词。” (来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions

当然,如果没有其他解决方案,我会想出一个我认为是单词分隔符的列表并在正则表达式中使用它。但我更喜欢有一个很好的工作功能而不是列表!

任何想法都会受到赞赏。

3 个答案:

答案 0 :(得分:4)

  

它适用于许多情况,除了&#34;字&#34;是在一个句子或一行的开头或结尾。

要解决这个问题,你可以使用行断言的开头/结尾和空格的交替,捕获它,并在替换中使用它:

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

替换为^| / $|。如果匹配断言,则捕获组将为"";如果匹配sapce,则为" "

&#13;
&#13;
$('#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');
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="myInput"></textarea>
&#13;
&#13;
&#13;

然而,您已经说过要避免&#34;逃避&#34;用户输入的字符。我不确定你计划在哪里使用它,但是像上面这样的东西几乎不是用于解决这类描述问题的方法。

答案 1 :(得分:1)

我得到了一个最终满足我所有需求的解决方案 我承认它比T.J.的那个复杂得多,这对于简单的情况来说是完美的。

请记住,我的主要问题是由于带有重音字符而使用\b的不稳定性 通过使用本主题的解决方案,我能够摆脱这个问题:
Remove accents/diacritics in a string in JavaScript

之后,我使用了一个修改过的功能,其灵感来自于这里的答案...
How do I replace a character at a particular index in 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);

下面是一个带有textarea的工作示例的片段 我刚刚创建了第一个代码片段的代码函数,我在插入符号位置使用子字符串来过滤函数的调用(避免在每个字符输入上调用它):

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

function replaceQuotes(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 position
  var chars = this.value.substring(caretPos - 2, caretPos + 1); // Gets 2 chars before caret (just typed and the one before), and 1 char just after
  if (pasted || chars.includes(`"`) || chars.includes(`'`)) { // Filters the calling of the function
    this.value = replaceQuotes(this.value); // Calls the function
    if (pasted) {
      pasted = 0;
    } else {
      this.setSelectionRange(caretPos, caretPos); // Restores caret position
    }
  }
}
#myInput {
  width: 90%;
  height: 100px;
}
<textarea id="myInput"></textarea>

它似乎与我现在能够想象的一切有效 该函数在以下情况下正确替换引号:
⋅定期打字,
⋅输入文字后添加引号,
⋅粘贴文本。

它取代了双引号和单引号。

无论如何,由于我根本不是RegEx专家,如果您发现可能不需要的行为或改善表达方式,请随时发表评论。

答案 2 :(得分:0)

因此,我不会使用正则表达式替换方法,而是使用带引号平衡动作的简单循环。您假设显示的每个单引号都与另一个引用匹配,当它出现时它将被替换为对。

以下是相同

的测试实现

&#13;
&#13;
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}'`)
} ;
}
&#13;
&#13;
&#13;

更新-1:2018年4月20日

我更新了这个功能。仍然可能存在一些边缘情况,但您应该将所有内容都放在测试中并运行它并修复那些不符合预期的行为