JS RegExp不使用字母字符

时间:2015-12-14 11:25:53

标签: javascript regex

作为自定义WYSIWYG编辑器的一部分,我们已被要求实现自动表情符号解析(如果已启用)。为此,我们使用正则表达式将字符组合替换为其关联的PNG文件。

以下是处理此问题的代码的相关部分(它由onkeyup元素上的contenteditable事件触发;我已将其修剪回相关部分):

// Parse emjoi:
this.parseEmoji = function()
{
    if( ! this.settings.parseSmileys )
    {
        return;
    }
    var _self    = this,
        url      = 'http://cdn.jsdelivr.net/emojione/assets/png/',
        $html    = this.$editor.html();

    // Loop through:
    for( var i in _self.emoji )
    {
        var re = new RegExp( '\\B' + _self.regexpEscape(i) + '\\B', 'g' ),
            em = _self.emoji[i];

        if( re.test($html) )
        {
            var replace = '<img class="lw-emoji" height="16" src="'+(url + em[0] + '.png')+'" alt="'+em[1]+'" />';
            this.insertAtCaret( replace );

            _self.$editor.html(function() { return $(this).html().replace(re, ''); });
        }
    }

};

这是regexpEscape()函数:

// Escape a string so that it's RegExp safe!
this.regexpEscape = function( txt )
{
    return txt.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
};

我们定义系统中使用的所有表情符号,这些表情符号由char组合本身引用,如下所示:

this.emoji = {
    ':)'  : [ '1F642', 'Smiling face' ],
    ':-)' : [ '1F642', 'Smiling face' ],
    ':D'  : [ '1F601', 'Happy face' ],
    ':-D' : [ '1F601', 'Happy face' ],
    ':\'(': [ '1F622', 'Crying face' ],
    ':('  : [ '1F614', 'Sad face' ],
    ':-(' : [ '1F614', 'Sad face' ],
    ':P'  : [ '1F61B', 'Cheeky' ],
    ':-P' : [ '1F61B', 'Cheeky' ],
    ':/'  : [ '1F615', 'Unsure face' ],
    ':-/' : [ '1F615', 'Unsure face' ],
    'B)'  : [ '1F60E', 'Too cool face' ],
    'B-)' : [ '1F60E', 'Too cool face' ]
};

现在,奇怪的是,任何包含字母字符的字符组合都不会被替换,并且re.test()函数失败。例如::):-):(:'(都可以毫无问题地被替换。但是,:DB)没有。

有人可以解释为什么alpha字符会导致RegExp内部出现问题吗?

Paired-back jsFiddle Demo

1 个答案:

答案 0 :(得分:1)

问题是\B是依赖于上下文的,如果有一个单词字符开始模式,则字符必须出现在匹配的输入字符串之前。在模式结尾处的方式相同,模式结尾处的\B将需要出现相同类型的符号。

为避免此问题,通常使用基于环视的解决方案:(?<!\w)YOUR_PATTERN(?!\w)。但是,在JS中,不支持lookbehind。它可以解决捕获组和后续替换功能中的反向引用。

因此,要正确替换这些情况,您需要将代码部分更改为

var re = new RegExp( '(^|\\W)' + _self.regexpEscape(i) + '(?!\\w)' ),
   em = _self.emoji[i]; // match the pattern when not preceded and not followed by a word character

if( re.test($html) )
{
   var replace = '<img class="lw-emoji" height="16" src="'+(url + em[0] + '.png')+'" alt="'+em[1]+'" />';
   this.insertAtCaret( replace );

   _self.$editor.html(function() { return $(this).html().replace(re, '$1'); }); // restore the matched symbol (the one \W matched) with $1
}

这是updated fiddle