www whatwg what spec language español 3wc javascript unicode

whatwg - Longitud de cadena en bytes en JavaScript



whatwg español (11)

En mi código JavaScript, necesito redactar un mensaje al servidor en este formato:

<size in bytes>CRLF <data>CRLF

Ejemplo:

3 foo

Los datos pueden contener caracteres Unicode. Necesito enviarlos como UTF-8.

Estoy buscando la forma más cruzada de navegador para calcular la longitud de la cadena en bytes en JavaScript.

Intenté esto para componer mi carga útil:

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

Pero no me da resultados precisos para los navegadores más antiguos (o, tal vez, las cadenas en esos navegadores en UTF-16?).

¿Alguna pista?

Actualizar:

Ejemplo: longitud en bytes de la cadena ЭЭХ! Naïve? ЭЭХ! Naïve? en UTF-8 es de 15 bytes, pero algunos navegadores reportan 23 bytes en su lugar.


Aquí hay un método independiente y eficiente para contar los bytes UTF-8 de una cadena.

//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<l; i++){ var hi=s.charCodeAt(i); if(hi<0x0080){ //[0x0000, 0x007F] n+=1; }else if(hi<0x0800){ //[0x0080, 0x07FF] n+=2; }else if(hi<0xD800){ //[0x0800, 0xD7FF] n+=3; }else if(hi<0xDC00){ //[0xD800, 0xDBFF] var lo=s.charCodeAt(++i); if(i<l&&lo>=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));

Tenga en cuenta que el método puede arrojar un error si una cadena de entrada es UCS-2 con formato incorrecto


Aquí hay una versión mucho más rápida, que no usa expresiones regulares, ni 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; }

Aquí hay una comparación de rendimiento .

Simplemente calcula la longitud en UTF8 de cada punto de código Unicode devuelto por charCodeAt (basado en las descripciones de wikipedia de UTF8 y caracteres suplentes UTF16).

Sigue RFC3629 (donde los caracteres UTF-8 tienen como máximo 4 bytes de longitud).


En realidad, descubrí lo que está mal. Para que el código funcione, la página <head> debe tener esta etiqueta:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

O, como se sugiere en los comentarios, si el servidor envía Content-Encoding encabezado HTTP Content-Encoding , debería funcionar también.

Entonces los resultados de diferentes navegadores son consistentes.

Aquí hay un ejemplo:

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>mini string length test</title> </head> <body> <script type="text/javascript"> document.write(''<div style="font-size:100px">'' + (unescape(encodeURIComponent("ЭЭХ! Naïve?")).length) + ''</div>'' ); </script> </body> </html>

Nota: sospecho que al especificar cualquier codificación (precisa) se solucionaría el problema de codificación. Es solo una coincidencia que necesito UTF-8.


Esta función devolverá el tamaño de bytes de cualquier cadena UTF-8 que le pase.

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

Source


Esto funcionaría para los caracteres BMP y 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.


Me tomó un tiempo encontrar una solución para React Native, así que lo pondré aquí:

Primero instale el paquete de buffer :

npm install --save buffer

A continuación, use el método de nodo:

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


No hay forma de hacerlo en JavaScript de forma nativa.

Si conoce la codificación de caracteres, puede calcularla usted mismo.

encodeURIComponent asume UTF-8 como la codificación de caracteres, por lo que si necesita esa codificación, puede hacer,

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

Esto debería funcionar debido a la forma en que UTF-8 codifica secuencias de múltiples bytes. El primer byte codificado siempre comienza con un bit alto de cero para una secuencia de un solo byte, o un byte cuyo primer dígito hexadecimal es C, D, E o F. El segundo byte y los siguientes son aquellos cuyos primeros dos bits son 10 Estos son los bytes adicionales que desea contar en UTF-8.

La tabla en wikipedia hace más claro

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

Si, en cambio, necesitas entender la codificación de la página, puedes usar este truco:

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


Otro enfoque muy simple que usa Buffer (solo para NodeJS):

Buffer.from(string).length


Para la codificación UTF-8 simple, con compatibilidad ligeramente mejor que TextEncoder , Blob hace el truco. Sin embargo, no funcionará en navegadores muy antiguos.

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



Puedes intentar esto:

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

Esto funciona para mi.