Разделить строку JavaScript в массив кодовых точек? (с учетом "суррогатных пар", но не "графемных кластеров" )

Разделение строки JavaScript на "символы" можно сделать тривиально, но есть проблемы, если вы заботитесь о Unicode (и вы должны заботиться о Unicode).

JavaScript изначально обрабатывает символы как 16-битные объекты (UCS-2 или UTF-16), но это не позволяет использовать символы Unicode вне BMP (базовая многоязычная плоскость).

Чтобы обрабатывать символы Unicode за пределами BMP, JavaScript должен учитывать "суррогатные пары", которые он не делает изначально.

Я ищу, как разбить строку js на код, независимо от того, требуется ли кодовым точкам один или два символа JavaScript (единицы кода).

В зависимости от ваших потребностей расщепление codepoint может быть недостаточно, и вы можете захотеть разделить на " grapheme cluster", где кластер является базовым кодом, за которым следуют все его кодовые точки с модификатором нераспределенного интервала, такие как сочетающий акценты и диакритические знаки.

Для целей этого вопроса мне не требуется разбиение кластером графем.

Ответ 1

@bobince ответ (к счастью) немного устарел; теперь вы можете просто использовать

chrs = Array.from( text )

чтобы получить список строк с одним кодовым номером, который соблюдает астральные/32bit/суррогатные символы Unicode.

Ответ 2

В ECMAScript 6 вы сможете использовать строку как итератор для получения кодовых точек, или вы можете искать строку для /./ug, или вы можете называть getCodePointAt(i) несколько раз.

К сожалению, for.. of флаги синтаксиса и регулярного выражения не могут быть заполнены, а вызов полилизованного getCodePoint() будет очень медленным (O (n²)), поэтому мы не можем реалистично использовать этот подход для пока еще.

Итак, сделайте это вручную:

String.prototype.toCodePoints= function() {
    chars = [];
    for (var i= 0; i<this.length; i++) {
        var c1= this.charCodeAt(i);
        if (c1>=0xD800 && c1<0xDC00 && i+1<this.length) {
            var c2= this.charCodeAt(i+1);
            if (c2>=0xDC00 && c2<0xE000) {
                chars.push(0x10000 + ((c1-0xD800)<<10) + (c2-0xDC00));
                i++;
                continue;
            }
        }
        chars.push(c1);
    }
    return chars;
}

Для обратного к нему см. fooobar.com/info/43038/...