如何查找特定字符串是否包含Unicode字符(尤其是双字节字符)

38

更准确地说,我需要知道是否(如果可能的话,如何)找出给定字符串是否具有双字节字符。基本上,我需要打开一个弹出窗口来显示包含双字节字符(如中文或日语)的给定文本。在这种情况下,我们需要调整窗口大小,以使其比英语或ASCII所需的大小更大。

有人知道吗?


好吧,我本来以为这会起作用。但在IE中没有起作用。我猜是一些布局问题。无论如何,由于计算要显示的文本长度和高度/宽度的代码已经存在,所以我继续使用只查找是否有双字节字符的代码。这样就解决了。 - Jay
使用HTML5,您可以使用Canvas元素的上下文(var ctx = canvas.getContext('2d'))来获取文本宽度度量。 var text_width = ctx.measureText(text).width; 我不确定这种方法在Unicode字符方面的效果如何,而且很遗憾,measureText方法目前只返回宽度。 - WebWanderer
6个回答

52

我使用了mikesamuel关于这个问题的回答。但是我发现,可能由于这个表单的原因,在正确运行此代码时,应该只有一个反斜杠转义 u,例如\u而不是\\u

function containsNonLatinCodepoints(s) {
    return /[^\u0000-\u00ff]/.test(s);
}

对我来说有效 :)


3
你的函数比打钩答案好得多,正则表达式总是更好的。 - AmerllicA
这对我也有效,使用正则表达式在性能上比使用循环更好。 - Tai Vu

34

JavaScript使用UCS-2内部存储文本,它可以编码Unicode的相当广泛的子集。

但这与您的问题并不相关。一个解决方案可能是通过循环遍历字符串并检查每个位置的字符代码:

function isDoubleByte(str) {
    for (var i = 0, n = str.length; i < n; i++) {
        if (str.charCodeAt( i ) > 255) { return true; }
    }
    return false;
}

这可能不像您期望的那样快速。


我不懂JavaScript,但你是不是指的UTF-16?UCS-16并不存在;在与Unicode等价的ISO/IEC 10646标准中,曾有过UCS-x编码形式,现已过时。UCS-2正好使用两个字节,因此可以表示前2^16个Unicode字符。相反,UTF-16使用16位元组,但不一定只用一个元组来表示所有字符。所有Unicode字符都可以表示为UTF-16字节序列。 - Arthur Reutenauer

16

我已经对前两个回答中的两个函数进行了基准测试,并想与大家分享结果。以下是我使用的测试代码:

const text1 = `The Chinese Wikipedia was established along with 12 other Wikipedias in May 2001. 中文維基百科的副標題是「海納百川,有容乃大」,這是中国的清朝政治家林则徐(1785年-1850年)於1839年為`;

const regex = /[^\u0000-\u00ff]/; // Small performance gain from pre-compiling the regex
function containsNonLatinCodepoints(s) {
    return regex.test(s);
}

function isDoubleByte(str) {
    for (var i = 0, n = str.length; i < n; i++) {
        if (str.charCodeAt( i ) > 255) { return true; }
    }
    return false;
}

function benchmark(fn, str) {
    let startTime = new Date();
    for (let i = 0; i < 10000000; i++) {
        fn(str);
    }   
    let endTime = new Date();

    return endTime.getTime() - startTime.getTime();
}

console.info('isDoubleByte => ' + benchmark(isDoubleByte, text1));
console.info('containsNonLatinCodepoints => ' + benchmark(containsNonLatinCodepoints, text1));

运行时我得到了:

isDoubleByte => 2421
containsNonLatinCodepoints => 868

所以对于这个特定的字符串,正则表达式的解决方案大约快3倍。

但请注意,对于第一个字符为Unicode的字符串,isDoubleByte()会立即返回,因此比正则表达式快得多(正则表达式仍然具有正则表达式的开销)。

例如,对于字符串中国,我得到了以下结果:

isDoubleByte => 51
containsNonLatinCodepoints => 288
为了兼顾两方面的优点,最好将它们结合起来:
var regex = /[^\u0000-\u00ff]/; // Small performance gain from pre-compiling the regex
function containsDoubleByte(str) {
    if (!str.length) return false;
    if (str.charCodeAt(0) > 255) return true;
    return regex.test(str);
}

如果第一个字符是中文(如果整个文本都是中文,则很可能如此),则该函数将快速返回。否则,它将运行正则表达式,这仍然比逐个检查每个字符要快。


7
这是基准测试:http://jsben.ch/NKjKd 这个要快得多:
function containsNonLatinCodepoints(s) {
    return /[^\u0000-\u00ff]/.test(s);
}

比如这样:
function isDoubleByte(str) {
    for (var i = 0, n = str.length; i < n; i++) {
        if (str.charCodeAt( i ) > 255) { return true; }
    }
    return false;
}

太棒了!非常感谢!它帮助我们制作了一个无钠密码库。 - jolly
2
@jolly 无钠? - Cog

6
实际上,从Javascript引擎的角度来看,所有字符都是Unicode的。
不幸的是,仅仅存在于特定Unicode范围内的字符并不能足以确定您需要更多的空间。有许多字符占用的空间与ASCII范围以上的其他字符大致相同。印刷体引号、带变音符号的字符、某些标点符号和各种货币符号都在低ASCII范围之外,并且在Unicode基本多语言平面上分配在非常不同的位置。
一般来说,我参与过的项目选择为所有语言提供额外的空间,或者有时使用javascript来确定具有自动滚动条css属性的窗口实际上是否具有触发滚动条的高度内容。
如果检测CJK字符的存在或计数足以确定您需要一些额外的空间,您可以使用以下范围构造一个正则表达式:[\u3300-\u9fff\uf900-\ufaff],并使用它提取匹配的字符数量。(这有点过于粗略,错过了所有非BMP情况,可能排除了一些其他相关范围,最有可能包括一些无关字符,但这是一个起点)。
再次强调,没有类似完整文本渲染引擎的东西,您只能管理粗略的启发式算法,因为您真正想要的是类似GDI的MeasureString(或任何其他文本渲染引擎的等效物)。我已经有一段时间没有这样做了,但我认为最接近HTML/DOM等效物的是在div上设置宽度并请求高度(复制粘贴重用,如果包含错误,敬请谅解)。
o = document.getElementById("test");

document.defaultView.getComputedStyle(o,"").getPropertyValue("height"))

0
为什么不让窗口根据运行时的高度/宽度自动调整大小呢?
在您的弹出窗口中运行类似以下代码的内容:
window.resizeTo(document.body.clientWidth, document.body.clientHeight);

这种方法在非病态情况下应该是可行的;当然你需要确保不超出可用的屏幕空间,或者至少假设存在合理的限制。 - JasonTrue

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