如何使用黑名单数组和正则表达式禁止使用变音符号的单词?

时间:2016-08-25 08:39:31

标签: javascript jquery html regex

我有一个类型为text的输入,根据禁止的单词列表返回true或false。一切正常。我的问题是我不知道如何检查数组中带有变音符号的单词:

var bannedWords = ["bad", "mad", "testing", "băţ"];
var regex = new RegExp('\\b' + bannedWords.join("\\b|\\b") + '\\b', 'i');

$(function () {
  $("input").on("change", function () {
    var valid = !regex.test(this.value);
    alert(valid);
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type='text' name='word_to_check'>

现在,在单词băţ上,它返回true而不是false。例如。

5 个答案:

答案 0 :(得分:5)

Chiu的评论是正确的:'aaáaa'.match(/\b.+?\b/g) yelds反直觉[ "aa", "á", "aa" ],因为JavaScript正则表达式中的“单词字符”(\w)为just a shorthand for {{1} }('case-insensitive-alpha-numeric-and-underscore'),所以字边界[A-Za-z0-9_])匹配alpha-numerics块和任何其他字符之间的任何位置。这使得提取“Unicode单词”非常困难。

对于非unicase书写系统,可以通过其双重性质识别“单词字符”:\b,因此您的修改后的代码段可能如下所示:

ch.toUpperCase() != ch.toLowerCase()
var bannedWords = ["bad", "mad", "testing", "băţ", "bať"];
var bannedWordsRegex = new RegExp('-' + bannedWords.join("-|-") + '-', 'i');

$(function() {
  $("input").on("input", function() {
    var invalid = bannedWordsRegex.test(dashPaddedWords(this.value));
    $('#log').html(invalid ? 'bad' : 'good');
  });
  $("input").trigger("input").focus();

  function dashPaddedWords(str) {
    return '-' + str.replace(/./g, wordCharOrDash) + '-';
  };

  function wordCharOrDash(ch) {
    return isWordChar(ch) ? ch : '-'
  };

  function isWordChar(ch) {
    return ch.toUpperCase() != ch.toLowerCase();
  };
});

答案 1 :(得分:2)

让我们看看发生了什么:

alert("băţ".match(/\w\b/));

这是[ "b" ],因为字边界\b无法识别ASCII以外的字词。 JavaScript&#34;字符&#34;严格地[0-9A-Z_a-z],因此匹配\w\b\W,因为它们包含单词字符,单词边界和非单词字符

我认为你能做的最好的事情就是这样:

var bound = '[^\\w\u00c0-\u02c1\u037f-\u0587\u1e00-\u1ffe]';
var regex = new RegExp('(?:^|' + bound + ')(?:'
                       + bannedWords.join('|')
                       + ')(?=' + bound + '|$)', 'i');

其中bound是所有ASCII字符加上大多数拉丁字母字母的反转列表,与行标记的开头/结尾一起用于近似国际化\b。 (第二个是zero-width lookahead更好地模仿\b,因此适用于g正则表达式标记。)

鉴于["bad", "mad", "testing", "băţ"],这变为:

/(?:^|[^\w\u00c0-\u02c1\u037f-\u0587\u1e00-\u1ffe])(?:bad|mad|testing|băţ)(?=[^\w\u00c0-\u02c1\u037f-\u0587\u1e00-\u1ffe]|$)/i

这不需要像….join('\\b|\\b')…这样的内容,因为列表周围有括号(这会创建类似\b(?:hey\b|\byou)\b的内容,类似于\bhey\b\b|\b\byou\b,包括荒谬的\b\b - JavaScript只解释为\b)。

您还可以将var bound = '[\\s!-/:-@[-`{-~]'用于更简单的仅包含可接受的非单词字符的ASCII列表。小心那个订单!短划线表示字符之间的范围。

答案 2 :(得分:2)

您可以使用

代替字边界
(?:[^\w\u0080-\u02af]+|^)

检查单词的开头,

(?=[^\w\u0080-\u02af]|$)

检查结束。

[^\w\u0080-\u02af]匹配任何字符^)为基本拉丁字符 - \w - 或Unicode 1_Supplement,Extended-A,Extended- B和扩展。这包括一些标点符号,但只需匹配字母就会很长。如果必须包含其他字符集,则可能还需要扩展。 See for example Wikipedia

由于javascript不支持look-behinds,因此单词开头测试会消耗前面提到的任何非单词字符,但我不认为这应该是一个问题。重要的是字结束测试没有。

此外,将这些测试置于非捕获组之外,使其更加有效。

var bannedWords = ["bad", "mad", "testing", "băţ", "båt", "süß"],
    regex = new RegExp('(?:[^\\w\\u00c0-\\u02af]+|^)(?:' + bannedWords.join("|") + ')(?=[^\\w\\u00c0-\\u02af]|$)', 'i');

function myFunction() {
    document.getElementById('result').innerHTML = 'Banned = ' + regex.test(document.getElementById('word_to_check').value);
}
<!DOCTYPE html>
<html>
<body>

Enter word: <input type='text' id='word_to_check'>
<button onclick='myFunction()'>Test</button>

<p id='result'></p>

</body>
</html>

答案 3 :(得分:2)

您需要一个支持Unicode的字边界。最简单的方法是使用XRegExp包。

虽然\b仍然是基于ASCII的,但是有一个\p{L}(或更短的pL版本)构造,它匹配来自BMP平面的任何Unicode字母。使用此构造构建自定义单词边界很简单:

\b                     word            \b
  ---------------------------------------
 |                       |               |
([^\pL0-9_]|^)         word       (?=[^\pL0-9_]|$)

前导词边界可以用(非)捕获组([^\pL0-9_]|^)表示,它匹配(并消耗)来自BMP平面的Unicode字母以外的字符,数字和_word之前的字符串的开头。

尾随字边界可以用正向前瞻(?=[^\pL0-9_]|$)来表示,它需要一个字母而不是来自BMP平面的Unicode字母,一个数字和_或者word之后的字符串结尾băţ 1}}。

请参阅下面的代码段,它会将băţy视为禁止的字词,var bannedWords = ["bad", "mad", "testing", "băţ"]; var regex = new XRegExp('(?:^|[^\\pL0-9_])(?:' + bannedWords.join("|") + ')(?=$|[^\\pL0-9_])', 'i'); $(function () { $("input").on("change", function () { var valid = !regex.test(this.value); //alert(valid); console.log("The word is", valid ? "allowed" : "banned"); }); });作为允许的字词。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xregexp/3.1.1/xregexp-all.min.js"></script>
<input type='text' name='word_to_check'>
CREATE TABLE #test
(
    [data] VARBINARY(MAX),
)


CREATE TABLE #temp
(
    [data] VARCHAR(MAX),
)

INSERT INTO #temp
EXEC sp_execute_external_script
@language=N'R',
@script=N'OutputDataSet <- as.data.frame("ABCDEF")' 

INSERT INTO #test SELECT CONVERT(VARBINARY(MAX), data, 2) FROM #temp;

答案 4 :(得分:0)

当处理我的基本集之外的字符(可以随时显示)时,我将它们转换为适当的基本等价物(8位,16位,32位)。在运行与之匹配的任何字符之前。

var bannedWords = ["bad", "mad", "testing", "băţ"];
var bannedWordsBits = {};
bannedWords.forEach(function(word){
  bannedWordsBits[word] = "";
  for (var i = 0; i < word.length; i++){
    bannedWordsBits[word] += word.charCodeAt(i).toString(16) + "-";
  }
});
var bannedWordsJoin = []
var keys = Object.keys(bannedWordsBits);
keys.forEach(function(key){
  bannedWordsJoin.push(bannedWordsBits[key]);
});
var regex = new RegExp(bannedWordsJoin.join("|"), 'i');

function checkword(word) {
  var wordBits = "";
  for (var i = 0; i < word.length; i++){
    wordBits += word.charCodeAt(i).toString(16) + "-";
  }
  return !regex.test(wordBits);
};

分隔符“ - ”用于确保唯一字符不会混合在一起,从而产生不需要的匹配。

非常有用,因为它将所有角色都归结为一切可以与之交互的共同基础。并且可以将其重新编码回原始版本,而无需将其以密钥/值对方式发送。

对我而言,关于它的最好的事情是我不必知道我可能与之交叉的所有字符集的所有规则,因为我可以把它们全部拉到一个共同的竞争环境中。

作为旁注:

为了加快速度,而不是传递你可能拥有的大型正则表达式语句,这需要指数级更长的时间来传递你禁止的单词的长度,我会通过过滤器传递句子中的每个单独的单词。并将过滤器分解为基于长度的段。等;

  1. checkword3Chars();
  2. checkword4Chars();
  3. checkword5chars();
  4. 你可以系统地生成哪些功能,甚至可以在需要时动态创建。