Как определить, содержит ли строка многобайтовые символы в Javascript?

Возможно ли в Javascript определить, содержит ли строка многобайтовые символы? Если да, можно ли сказать, какие из них?

Проблема, с которой я сталкиваюсь, - это (извинения, если Unicode char не подходит для вас)

s = "𝌆";

alert(s.length);    // '2'
alert(s.charAt(0)); // '��'
alert(s.charAt(1)); // '��'

Изменить для немного ясности здесь (надеюсь). Как я понимаю, теперь все строки в Javascript представлены как ряд кодовых точек UTF-16, а это означает, что обычные символы на самом деле возьмите 2 байта (16 бит), поэтому мое использование "multibyte" в заголовке было немного выключено. Некоторые символы не попадают в базовую многоязычную плоскость (BMP), такую ​​как строка в приведенном выше примере, и поэтому они занимают две кодовые точки (32 бита). Это вопрос, который я задавал. Я также не редактирую оригинальное название, так как кому-то, кто мало знает об этом материале (и, следовательно, будет искать SO для информации об этом), "многобайтовый" имеет смысл.

Ответ 1

Строки JavaScript кодируются в кодировке UCS-2, но могут представлять кодовые точки Unicode вне базовой многоязычной панели (U+0000 - U+D7FF и U+E000 - U+FFFF), используя два 16-битных номера (суррогатная пара UTF-16), первая из которых должна находиться в диапазоне U+D800 - U+DFFF.

Исходя из этого, легко определить, содержит ли строка какие-либо символы, которые лежат вне базовой многоязычной плоскости (что, как я думаю, вы спрашиваете: вы хотите определить, содержит ли строка любые символы, которые лежат вне диапазона кодовых точек, которые JavaScript представляет как один символ):

function containsSurrogatePair(str) {
    return /[\uD800-\uDFFF]/.test(str);
}

alert( containsSurrogatePair("foo") ); // false
alert( containsSurrogatePair("f𝌆") ); // true

Выработка точно, какие кодовые точки содержатся в вашей строке, немного сложнее и требует декодера UTF-16. Следующее преобразует строку в массив кодов Unicode:

var getStringCodePoints = (function() {
    function surrogatePairToCodePoint(charCode1, charCode2) {
        return ((charCode1 & 0x3FF) << 10) + (charCode2 & 0x3FF) + 0x10000;
    }

    // Read string in character by character and create an array of code points
    return function(str) {
        var codePoints = [], i = 0, charCode;
        while (i < str.length) {
            charCode = str.charCodeAt(i);
            if ((charCode & 0xF800) == 0xD800) {
                codePoints.push(surrogatePairToCodePoint(charCode, str.charCodeAt(++i)));
            } else {
                codePoints.push(charCode);
            }
            ++i;
        }
        return codePoints;
    }
})();

alert( getStringCodePoints("f𝌆").join(",") ); // 102,119558

Ответ 2

Это моя реализация, чтобы показать больше emojis, если сообщение не содержит текста

Разметка

<div>
    <input id="message" placeholder="Nice support for one or multiple emojis">
    <button id="post-message">Send</button>
    <ul id="messages"></ul>
</div>

Script

function jumbotron(str) {
    return /^[\uD800-\uDFFF]+$/.test(str);
}

document.getElementById('post-message').onclick = function() {
    list_element = document.createElement('li');
    message = document.getElementById('message').value;

    list_element_span = document.createElement('span');
    list_element_span.innerHTML = message;
    list_element.appendChild(list_element_span);

    if (jumbotron(message)) {
        list_element_span.style.fontSize = '2em';
        list_element_span.style.lineHeight = 'normal';
    }

    document.getElementById('messages').appendChild(list_element)
}