Как узнать, содержит ли конкретная строка символы Юникода (особенно двухбайтовые символы)
Чтобы быть более точным, мне нужно знать, могу ли (и если возможно, каким образом) узнать, содержит ли данная строка двухбайтовые символы или нет. По сути, мне нужно открыть всплывающее окно для отображения заданного текста, который может содержать двухбайтовые символы, такие как китайский или японский. В этом случае нам нужно отрегулировать размер окна, чем это было бы для английского или ASCII. У кого-нибудь есть подсказка?
6 ответов
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;
}
Это может быть не так быстро, как хотелось бы.
Я использовал mikesamuel ответ на этот вопрос. Однако, возможно, из-за этой формы я заметил, что перед тем как u
например, \u
и не \\u
чтобы сделать эту работу правильно.
function containsNonLatinCodepoints(s) {
return /[^\u0000-\u00ff]/.test(s);
}
Работает для меня:)
Я сравнил две функции в верхних ответах и подумал, что поделюсь результатами. Вот тестовый код, который я использовал:
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);
}
В этом случае, если первый символ - китайский (что, вероятно, если весь текст - китайский), функция будет быстрой и сразу же вернется. Если нет, он запустит регулярное выражение, что все же быстрее, чем проверка каждого символа в отдельности.
Вот эталонный тест: 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;
}
На самом деле все символы являются Unicode, по крайней мере, с точки зрения движка Javascript.
К сожалению, простого присутствия символов в определенном диапазоне Юникода будет недостаточно, чтобы определить, что вам нужно больше места. Есть ряд символов, которые занимают примерно столько же места, что и другие символы, у которых кодовые точки Unicode намного выше диапазона ASCII. Типографские кавычки, символы с диакритическими знаками, некоторые знаки пунктуации и различные символы валюты находятся за пределами диапазона низких значений ASCII и размещаются в совершенно разных местах на базовой многоязычной плоскости Unicode.
Как правило, проекты, над которыми я работал, выбирают предоставление дополнительного пространства для всех языков, или иногда используют javascript, чтобы определить, действительно ли окно с атрибутами css для полосы прокрутки имеет содержимое с высотой, которая будет запускать полосу прокрутки или нет.
Если обнаружения наличия или количества символов CJK будет достаточно, чтобы определить, что вам нужно немного дополнительного пространства, вы можете создать регулярное выражение, используя следующие диапазоны: [\u3300-\u9fff\uf900-\ufaff], и использовать что извлечь количество символов, которые соответствуют. (Это немного грубовато и пропускает все случаи, не связанные с BMP, вероятно, исключает некоторые другие соответствующие диапазоны и, скорее всего, включает некоторые не относящиеся к делу символы, но это отправная точка).
Опять же, вы только сможете управлять грубой эвристикой без чего-либо, похожего на движок полнотекстового рендеринга, потому что вам действительно нужно что-то вроде MeasureString в GDI (или эквивалент любого другого движка рендеринга текста). Прошло много времени с тех пор, как я это сделал, но я думаю, что ближайшим эквивалентом HTML/DOM является установка ширины для div и запрос высоты (повторное использование вырезать и вставить, поэтому извиняюсь, если в нем есть ошибки):
o = document.getElementById("test");
document.defaultView.getComputedStyle(o,"").getPropertyValue("height"))
Почему бы не позволить окну изменить размер самого себя в зависимости от высоты / ширины среды выполнения?
Запустите что-то вроде этого во всплывающем окне:
window.resizeTo(document.body.clientWidth, document.body.clientHeight);