Длина строки в байтах в JavaScript
в моем JavaScript коде мне нужно составить сообщение на сервер в таком формате:
<size in bytes>CRLF
<data>CRLF
пример:
3
foo
данные могут содержать символы Unicode. Мне нужно отправить их как UTF-8.
Я ищу самый кросс-браузерный способ вычисления длины строки в байтах в JavaScript.
Я пробовал это, чтобы составить свою полезную нагрузку:
return unescape(encodeURIComponent(str)).length + "n" + str + "n"
но это не дает мне точных результатов для старых браузеров (или, может быть, строки в этих браузерах в UTF-16?).
какие-то зацепки?
обновление:
пример: длина строки в байтах ЭЭХ! Naïve?
в UTF-8 составляет 15 байт, но некоторые браузеры сообщают 23 байта вместо этого.
11 ответов:
нет никакого способа сделать это в JavaScript изначально.
если вы знаете кодировку, вы можете рассчитать его самостоятельно.
encodeURIComponent
предполагает UTF-8 в качестве кодировки символов, так что если вам нужна эта кодировка, вы можете сделать,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); }
это должно работать из-за того, как UTF-8 кодирует многобайтовые последовательности. Первый закодированный байт всегда начинается либо с старшего бита нуля для однобайтовой последовательности, либо с байта, первая шестнадцатеричная цифра которого равна C, D, E, или F. второй и последующие байты-это те, чьи первые два бита равны 10. Это дополнительные байты, которые вы хотите подсчитать в UTF-8.
в таблице Википедия делает его более четким
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 ...
если вместо этого вам нужно понять кодировку страницы, вы можете использовать этот трюк:
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); }
вот гораздо более быстрая версия, которая не использует регулярные выражения, ни 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; }
здесь производительность сравнение.
Он просто вычисляет длину в UTF8 каждой кодовой точки unicode, возвращаемой charCodeAt (на основе описаний Википедии UTF8, и UTF16 суррогатных символов).
следует RFC3629 (где символы UTF-8 имеют длину не более 4 байт).
прошли годы, и в настоящее время вы можете сделать это изначально
(new TextEncoder('utf-8').encode('foo')).length
обратите внимание, что он еще не поддерживается IE (или Edge) (вы можете использовать полифилл для этого).
для простого кодирования UTF-8, с немного лучшей совместимостью, чем
TextEncoder
, Blob делает трюк. Не будет работать в очень старых браузерах, хотя.new Blob([""]).size; // -> 4
эта функция возвращает размер байта любой строки UTF-8, которую вы передаете ей.
function byteCount(s) { return encodeURI(s).split(/%..|./).length - 1; }
еще один очень простой подход с использованием
Buffer
(только для NodeJS):Buffer.from(string).length
на самом деле, я понял, что случилось. Для работы кода на странице
<head>
должны иметь этот тег:<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
или, как предлагается в комментариях, если сервер отправляет HTTP
Content-Encoding
заголовок, он также должен работать.тогда результаты из разных браузеров являются последовательными.
вот пример:
<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>
примечание: Я подозреваю, что задание любой (точное) кодирование устранит проблему кодирования. Это просто совпадение, что Мне нужен UTF-8.
вот независимый и эффективный метод подсчета UTF-8 байт строки.
//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));
Примечание что метод может выдать ошибку, если входная строка UCS-2 деформирована
потребовалось некоторое время, чтобы найти решение для Реагировать Уроженца поэтому я положу его здесь:
установить
buffer
пакет:npm install --save buffer
затем пользователь метод узла:
const { Buffer } = require('buffer'); const length = Buffer.byteLength(string, 'utf-8');
вы можете попробовать это:
function getLengthInBytes(str) { var b = str.match(/[^\x00-\xff]/g); return (str.length + (!b ? 0: b.length)); }
это работает для меня.
Это будет работать для символов BMP и 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.