web-dev-qa-db-ja.com

特定の文字列にUnicode文字(特に2バイト文字)が含まれているかどうかを確認する方法

より正確に言うと、特定の文字列に2バイト文字があるかどうかを(可能であればどのように)見つけることができるかを知る必要があります。基本的に、ポップアップを開いて、中国語や日本語などの2バイト文字を含むことができる特定のテキストを表示する必要があります。この場合、英語やASCIIの場合よりもウィンドウサイズを調整する必要があります。誰か手がかりがありますか?

26
Jay

JavaScriptは、テキストを内部的にUCS-2として保持します。これにより、Unicodeのかなり広範なサブセットをエンコードできます。

しかし、それはあなたの質問に本当に密接な関係はありません。 1つの解決策は、文字列をループして、各位置の文字コードを調べることです。

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

これはあなたが望むほど速くないかもしれません。

27
pcorcoran

私はこれにmikesamuelの答えを使用しました。ただし、おそらくこのフォームのせいで、uの前にエスケープスラッシュが1つしかないことに気づきました。 \uではなく\\uこれを正しく機能させるため。

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

私のために働く:)

36
james

私は上位の回答の2つの関数のベンチマークを行い、結果を共有すると思いました。これが私が使用したテストコードです:

_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);
}
_

その場合、最初の文字が中国語の場合(テキスト全体が中国語の場合)、関数は高速になり、すぐに戻ります。そうでない場合は、正規表現を実行します。これは、各文字を個別にチェックするよりも高速です。

9
laurent

実際、少なくともJavascriptエンジンの観点からは、すべての文字はUnicodeです。

残念ながら、特定のUnicode範囲に文字が存在するだけでは、より多くのスペースが必要であると判断するのに十分ではありません。 ASCIIの範囲をはるかに超えるUnicodeコードポイントを持つ他の文字とほぼ同じ量のスペースを占める文字がいくつかあります。活字の引用符、ダイアクリティックスのある文字、特定の句読点記号、およびさまざまな通貨記号は低い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"))
6
JasonTrue

ベンチマークテストは次のとおりです。 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;
}
2
David Dehghan

ランタイムの高さ/幅に基づいてウィンドウのサイズを変更してみませんか?

ポップアップで次のようなものを実行します。

window.resizeTo(document.body.clientWidth, document.body.clientHeight);
0
Oli