JavaScript toLowerCase 奇怪的行为

8

我有一个小应用程序,用于读取推文并尝试匹配关键词。我注意到一个特定字符串的行为很奇怪:

var text = "The Νіk​е D​un​k​ Ніgh ЅΒ 'Uglу Ѕwеаt​еr​' іѕ n​оw аvаіlаblе http://swoo.sh/IHVaTL";
var lowercase = text.toLowerCase()

现在小写字母的值是:

“νіk​е d​un​k​ ніgh ѕβ 'uglу ѕwеаt​еr​'”现已上市, http://swoo.sh/ihvatl

看起来这个字符串的格式很奇怪,我仔细检查了一些字母,发现:
text.charAt(4)
>"N"
text.charCodeAt(5)
>925
'N'.charCodeAt(0)
>78

即使它看起来像普通的"N",与它相关的Unicode对应于

0925 थ DEVANAGARI LETTER THA

根据Unicode字符表

因此,我对如何发生这种情况有些困惑,是否有任何方法可以“转换”为所谓的真正字母。


1
在复制和粘贴后,我可以在Firebug中重现您的错误,但是在我手动删除和重新插入有问题的字符后,就不会再出现错误了。这个字符串来自哪里?如果您在某个编辑器中输入了此内容,请检查您的区域设置/字符编码设置。 - Hans Hohenfeld
2
是的,字符串中的“N”包含看起来像普通字符的字母,但实际上是希腊字母。 - mingos
该应用程序从 Twitter 流中读取内容,这条特定信息来自 Nike 账户,我是偶然发现的。 - jasalguero
Nike中的'N'是(039D)hex,vike中的'v'是(03BD)hex,它是小写字母。 - Anto Jurković
1
因为该字符串中的所有同形异义字已经是大写的。唯一可靠的方法是通过比较图像或制作Unicode中所有同形异义字的大量列表来实现,而这些列表非常庞大。 - Dagg Nabbit
显示剩余2条评论
2个回答

2
有一个名为unidecode的Python库,我以前在Python中用它来解决这个问题,它基本上将Unicode“平铺”为ASCII。
快速搜索显示,JavaScript也有一个类似的

你会如何在浏览器中使用它?对我来说,它看起来像是一个npm包。 - Dagg Nabbit
哦!没有充分考虑您的使用情况。有一个名为browserify的npm模块,它允许您像在node中一样require模块。 - willy

1
你可以为每个拉丁字母(大写和小写)创建一个单独的画布进行比较。每当遇到不在Latin-1范围内的字符时,就为其创建一个新的画布,并使用image diff算法将其与每个拉丁字母进行比较。用最接近的匹配替换非拉丁字符。
例如:
var latinize = (function () {
    var latinLetters = [],
        canvases = [],
        size = 16,
        halfSize = size >> 1;

    function makeCanvas(chr) {
        var canvas = document.createElement('canvas'),
            context = canvas.getContext('2d');

        canvas.width = size;
        canvas.height = size;
        context.textBaseline = 'middle';
        context.textAlign = 'center';
        context.font = (halfSize) + "px sans-serif";
        context.fillText(chr, halfSize, halfSize);

        return context;
    }

    function nextChar(chr) {
        return String.fromCharCode(chr.charCodeAt(0) + 1);
    }

    function setupRange(from, to) {
        for (var chr = from; chr <= to; chr = nextChar(chr)) {
            latinLetters.push(chr);
            canvases.push(makeCanvas(chr));
        }
    }

    function calcDistance(ctxA, ctxB) {
        var distance = 0,
            dataA = ctxA.getImageData(0, 0, size, size).data,
            dataB = ctxB.getImageData(0, 0, size, size).data;

        for (var i = dataA.length; i--;) {
            distance += Math.abs(dataA[i] - dataB[i]);
        }

        return distance;
    }

    setupRange('a', 'z');
    setupRange('A', 'Z');
    setupRange('', ''); // ignore blank characters

    return function (text) {
        var result = "",
            scores, canvas;

        for (var i = 0; i < text.length; i++) {
            if (text.charCodeAt(i) < 128) {
                result += text.charAt(i);
                continue;
            }
            scores = [];
            canvas = makeCanvas(text.charAt(i));
            for (var j = 0; j < canvases.length; j++) {
                scores.push({
                    glyph: latinLetters[j],
                    score: calcDistance(canvas, canvases[j])
                });
            }
            scores.sort(function (a, b) {
                return a.score - b.score;
            });
            result += scores[0].glyph;
        }

        return result;
    }
}());

这将把你的测试字符串翻译成“Nike Dunk High SB 'Ugly Sweater'现已上市”。另一种方法是创建一个巨大的数据结构,将所有类似字符映射到它们的Latin-1等效项,就像@willy答案中的库所做的那样。这对于“浏览器JavaScript”来说非常重,可能不适合发送给客户端,正如您从该项目的源代码中看到的那样。

http://jsfiddle.net/Ly5Lt/4/


嗨Dagg,谢谢你的建议,但我看到解决方案是“返回k dunk gh 'ugl wtr' nw vlbl http://swoo.sh/ihvatl”,所以它正在删除所有同形异义词。 - jasalguero
@jasalguero,你用的是什么浏览器?在Chrome中它对我有效。问题在于这个imagediff库似乎没有一种评分匹配的方法,它只能做一个完整的差异或检查两个图像是否“相等”并带有一个容错因子(我在这个例子中省略了它,所以字形必须完全相同才能匹配;像不同的间距或抗锯齿可能会使其失效)。这段代码只是为了说明你需要做些什么才能让它工作,但如果你真的想使用它(尽管它很丑陋),我可以修复它。我怀疑你不会找到另一个解决方案。 - Dagg Nabbit
好的,我更新了代码,不再使用库。这个版本可以成功地将“Ï đōń'ţ ķńŏŵ”翻译成“I don't know”,所以对于耐克奇怪的推文应该能够很好地工作。请告诉我它的表现如何。 - Dagg Nabbit
@jasalguero,谢谢,很高兴它对你有用。我对它的表现相当满意,看起来并不像我最初想象的那么粗糙。然而,问题和这两个答案之间的赞数差异让我想知道人们希望得到什么样的答案。除了这两个答案中的技术,我相信没有其他方法可以做到这一点。如果有人给问题点赞,我很想听听他们的想法。 - Dagg Nabbit
事实上,我也感到惊讶,因为我给两个答案都点了赞,因为它们都很有帮助,但是你的最有用。顺便说一句,再次感谢! - jasalguero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接