Lunghezza della stringa in byte in JavaScript

Nel mio codice JavaScript ho bisogno di comporre un messaggio al server in questo formato:

CRLF CRLF 

Esempio:

 3 foo 

I dati possono contenere caratteri Unicode. Devo inviarli come UTF-8.

Sto cercando il modo più cross-browser per calcolare la lunghezza della stringa in byte in JavaScript.

Ho provato questo per comporre il mio carico utile:

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

Ma non mi dà risultati precisi per i browser più vecchi (o forse le stringhe in quei browser in UTF-16?).

Qualche indizio?

Aggiornare:

Esempio: lunghezza in byte della stringa ЭЭХ! Naïve? ЭЭХ! Naïve? in UTF-8 è 15 byte, ma alcuni browser riportano invece 23 byte.

Non c’è modo di farlo in JavaScript in modo nativo.

Se conosci la codifica dei caratteri, puoi comunque calcolarla tu stesso.

encodeURIComponent assume UTF-8 come codifica dei caratteri, quindi se hai bisogno di quella codifica, puoi farlo,

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

Questo dovrebbe funzionare a causa del modo in cui UTF-8 codifica sequenze multi-byte. Il primo byte codificato inizia sempre con un bit elevato di zero per una singola sequenza di byte o un byte la cui prima cifra esadecimale è C, D, E o F. Il secondo e i byte successivi sono quelli i cui primi due bit sono 10 Questi sono i byte in più che vuoi contare in UTF-8.

La tabella in wikipedia lo rende più chiaro

 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 ... 

Se invece hai bisogno di capire la codifica della pagina, puoi usare questo trucco:

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

Ecco una versione molto più veloce, che non usa espressioni regolari, né encodeURIComponent:

 function byteLength(str) { // returns the byte length of an utf8 string var s = str.length; for (var i=str.length-1; i>=0; i--) { var code = str.charCodeAt(i); if (code > 0x7f && code < = 0x7ff) s++; else if (code > 0x7ff && code < = 0xffff) s+=2; if (code >= 0xDC00 && code < = 0xDFFF) i--; //trail surrogate } return s; } 

Ecco un confronto delle prestazioni .

Calcola solo la lunghezza in UTF8 di ciascun codepoint unicode restituito da charCodeAt (in base alle descrizioni di wikipedia di UTF8 e dei caratteri surrogati UTF16).

Segue RFC3629 (dove i caratteri UTF-8 sono lunghi al massimo 4 byte).

Gli anni passano e al giorno d’oggi puoi farlo in modo nativo

 (new TextEncoder('utf-8').encode('foo')).length 

Nota che non è ancora supportato da IE (o Edge) (puoi usare un polyfill per quello).

Documentazione MDN

Specifiche standard

Per la semplice codifica UTF-8, con una compatibilità leggermente migliore rispetto a TextEncoder , Blob fa il trucco. Tuttavia, non funzionerà con browser molto vecchi.

 new Blob(["😀"]).size; // -> 4 

Questa funzione restituirà la dimensione in byte di qualsiasi stringa UTF-8 che gli passi.

 function byteCount(s) { return encodeURI(s).split(/%..|./).length - 1; } 

fonte

Un altro approccio molto semplice che utilizza Buffer (solo per NodeJS):

 Buffer.from(string).length 

In realtà, ho capito cosa c’è che non va. Perché il codice funzioni la pagina dovrebbe avere questo tag:

  

Oppure, come suggerito nei commenti, se il server invia l’intestazione Content-Encoding HTTP, dovrebbe funzionare altrettanto bene.

Quindi i risultati di diversi browser sono coerenti.

Ecco un esempio:

    mini string length test      

Nota: sospetto che la specifica di qualsiasi codifica (accurata) possa risolvere il problema di codifica. È solo una coincidenza che ho bisogno di UTF-8.

Puoi provare questo:

 function getLengthInBytes(str) { var b = str.match(/[^\x00-\xff]/g); return (str.length + (!b ? 0: b.length)); } 

Per me funziona.

Ecco un metodo indipendente ed efficiente per contare i byte UTF-8 di una stringa.

 //count UTF-8 bytes of a string function byteLengthOf(s){ //assuming the String is UCS-2(aka UTF-16) encoded var n=0; for(var i=0,l=s.length; i=0xDC00&&lo< =0xDFFF){ //followed by [0xDC00, 0xDFFF] n+=4; }else{ throw new Error("UCS-2 String malformed"); } }else if(hi<0xE000){ //[0xDC00, 0xDFFF] throw new Error("UCS-2 String malformed"); }else{ //[0xE000, 0xFFFF] n+=3; } } return n; } var s="\u0000\u007F\u07FF\uD7FF\uDBFF\uDFFF\uFFFF"; console.log("expect byteLengthOf(s) to be 14, actually it is %s.",byteLengthOf(s)); 

Questo funzionerebbe per i caratteri BMP e SIP / SMP.

  String.prototype.lengthInUtf8 = function() { var asciiLength = this.match(/[\u0000-\u007f]/g) ? this.match(/[\u0000-\u007f]/g).length : 0; var multiByteLength = encodeURI(this.replace(/[\u0000-\u007f]/g)).match(/%/g) ? encodeURI(this.replace(/[\u0000-\u007f]/g, '')).match(/%/g).length : 0; return asciiLength + multiByteLength; } 'test'.lengthInUtf8(); // returns 4 '\u{2f894}'.lengthInUtf8(); // returns 4 'سلام علیکم'.lengthInUtf8(); // returns 19, each Arabic/Persian alphabet character takes 2 bytes. '你好,JavaScript 世界'.lengthInUtf8(); // returns 26, each Chinese character/punctuation takes 3 bytes. 

Ci ho messo un po ‘a trovare una soluzione per React Native, quindi la metto qui:

Per prima cosa installa il pacchetto buffer :

 npm install --save buffer 

Quindi usa il metodo del nodo:

 const { Buffer } = require('buffer'); const length = Buffer.byteLength(string, 'utf-8');