Délka řetězce v bajtech v JavaScriptu

V kódu JavaScript potřebuji napsat zprávu serveru v tomto formátu:

<size in bytes>CRLF
<data>CRLF

Příklad:

3
foo

Data mohou obsahovat znaky Unicode. Potřebuji je poslat jako UTF-8.

Hledám nejvhodnější způsob výpočtu délky řetězce v bajtech v JavaScriptu v různých prohlížečích.

Při sestavování užitečného zatížení jsem zkusil toto:

return unescape(encodeURIComponent(str)).length + "n" + str + "n"

Ale nedává mi přesné výsledky pro starší prohlížeče (nebo možná řetězce v těchto prohlížečích v UTF-16?).

Nějaké stopy?

Aktualizace:

Příklad:délka řetězce ЭЭХ! Naïve? v bajtech v UTF-8 je 15 bajtů, ale některé prohlížeče místo toho hlásí 23 bajtů.

Odpověď

Neexistuje žádný způsob, jak to udělat v JavaScriptu nativně. (Viz odpověď Riccarda Galliho pro moderní přístup.)

Pro historické reference nebo tam, kde jsou rozhraní API TextEncoder stále nedostupná.

Pokud znáte kódování znaků, můžete si ho spočítat sami.

encodeURIComponent předpokládá UTF-8 jako kódování znaků, takže pokud toto kódování potřebujete, můžete to udělat,

function lengthInUtf8Bytes(str) {
  // Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
  var m = encodeURIComponent(str).match(/%[89ABab]/g);
  return str.length + (m ? m.length : 0);
}

To by mělo fungovat kvůli způsobu, jakým UTF-8 kóduje vícebajtové sekvence. První zakódovaný bajt vždy začíná buď vysokým bitem nuly pro jednobajtovou sekvenci, nebo bytem, ​​jehož první hexadecimální číslice je C, D, E nebo F. Druhý a následující bajty jsou ty, jejichž první dva bity jsou 10 Toto jsou bajty navíc, které chcete počítat v UTF-8.

Tabulka ve wikipedii je přehlednější

Bits        Last code point Byte 1          Byte 2          Byte 3
  7         U+007F          0xxxxxxx
 11         U+07FF          110xxxxx        10xxxxxx
 16         U+FFFF          1110xxxx        10xxxxxx        10xxxxxx
...

Pokud místo toho potřebujete porozumět kódování stránky, můžete použít tento trik:

function lengthInPageEncoding(s) {
  var a = document.createElement('A');
  a.href = '#' + s;
  var sEncoded = a.href;
  sEncoded = sEncoded.substring(sEncoded.indexOf('#') + 1);
  var m = sEncoded.match(/%[0-9a-f]{2}/g);
  return sEncoded.length - (m ? m.length * 2 : 0);
}