Dans mon code JavaScript, je dois composer un message au serveur dans ce format:
<size in bytes>CRLF
<data>CRLF
Exemple:
3
foo
Les données peuvent contenir des caractères unicode. Je dois les envoyer au format UTF-8.
Je cherche le moyen le plus multi-navigateur pour calculer la longueur de la chaîne en octets en JavaScript.
J'ai essayé ceci pour composer ma charge utile:
return unescape(encodeURIComponent(str)).length + "\n" + str + "\n"
Mais cela ne me donne pas des résultats précis pour les anciens navigateurs (ou, peut-être, les chaînes de ces navigateurs dans UTF-16?).
Des indices?
Mise à jour:
Exemple: longueur en octets de la chaîne ЭЭХ! Naïve?
en UTF-8 correspond à 15 octets, mais certains navigateurs signalent plutôt 23 octets.
Il n'y a aucun moyen de le faire en JavaScript de manière native. (Voir réponse de Riccardo Galli pour une approche moderne.)
Pour référence historique ou où les API TextEncoder sont toujours indisponible .
Si vous connaissez l'encodage des caractères, vous pouvez le calculer vous-même.
encodeURIComponent
suppose que UTF-8 est l'encodage de caractères, donc si vous avez besoin de cet encodage, vous pouvez le faire,
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);
}
Cela devrait fonctionner à cause de la façon dont UTF-8 code pour les séquences multi-octets. Le premier octet codé commence toujours par un bit haut de zéro pour une séquence d'octets unique ou par un octet dont le premier chiffre hexadécimal est C, D, E ou F. Les octets suivants sont ceux dont les deux premiers bits sont 10. Ce sont les octets supplémentaires que vous voulez compter dans UTF-8.
Le tableau dans wikipedia le rend plus clair
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 vous devez au contraire comprendre le codage de la page, vous pouvez utiliser cette astuce:
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);
}
Les années ont passé et aujourd'hui, vous pouvez le faire en natif
(new TextEncoder('utf-8').encode('foo')).length
Notez que ce n'est pas encore supporté par IE (ou Edge) (vous pouvez tilisez un polyfill pour cela).
Voici une version beaucoup plus rapide, qui n’utilise ni expressions régulières, 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;
}
Voici une comparaison performance .
Il calcule simplement la longueur en UTF8 de chaque code codé Unicode renvoyé par charCodeAt () (basé sur les descriptions par wikipedia de TF8 et de caractères de substitution UTF16).
Il s'ensuit RFC3629 (où les caractères UTF-8 ont une longueur d'au plus 4 octets).
Pour l'encodage UTF-8 simple, avec une compatibilité légèrement meilleure que TextEncoder
, Blob fait l'affaire. Ne fonctionnera pas dans les très vieux navigateurs cependant.
new Blob(["????"]).size; // -> 4
Cette fonction renvoie la taille en octets de toute chaîne UTF-8 que vous lui transmettez.
function byteCount(s) {
return encodeURI(s).split(/%..|./).length - 1;
}
Une autre approche très simple utilisant Buffer
(uniquement pour NodeJS):
Buffer.from(string).length
En fait, j'ai compris ce qui ne va pas. Pour que le code fonctionne la page <head>
devrait avoir cette balise:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Ou, comme suggéré dans les commentaires, si le serveur envoie HTTP Content-Encoding
en-tête, cela devrait également fonctionner.
Ensuite, les résultats de différents navigateurs sont cohérents.
Voici un exemple:
<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>
Remarque: je soupçonne que spécifier n'importe lequel (exact) encodage résoudrait le problème de codage. Ce n'est que par coïncidence que j'ai besoin de UTF-8.
Il m'a fallu un certain temps pour trouver une solution pour React Native alors je vais la mettre ici:
Commencez par installer le paquet buffer
:
npm install --save buffer
Puis utilisez la méthode du noeud:
const { Buffer } = require('buffer');
const length = Buffer.byteLength(string, 'utf-8');
Voici une méthode indépendante et efficace pour compter les octets UTF-8 d'une chaîne.
//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));
Note Que la méthode peut générer une erreur si une chaîne d'entrée est mal formée par UCS-2
Dans NodeJS, Buffer.byteLength
est une méthode spécialement conçue à cet effet:
let strLengthInBytes = Buffer.byteLength(str); // str is UTF-8
Notez que par défaut, la méthode suppose que la chaîne est en codage UTF-8. Si un autre codage est requis, transmettez-le comme second argument.
Cela fonctionnerait pour les caractères BMP et 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.
Vous pouvez essayer ceci:
function getLengthInBytes(str) {
var b = str.match(/[^\x00-\xff]/g);
return (str.length + (!b ? 0: b.length));
}
Ça marche pour moi.