Длина строки в байтах в JavaScript - PullRequest
76 голосов
/ 01 апреля 2011

В моем коде JavaScript мне нужно составить сообщение на сервер в следующем формате:

<size in bytes>CRLF
<data>CRLF

Пример:

3
foo

Данные могут содержать символы Юникода.Мне нужно отправить их как UTF-8.

Я ищу наиболее кросс-браузерный способ вычисления длины строки в байтах в JavaScript.

Я пробовал эточтобы составить мою полезную нагрузку:

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

Но это не дает точных результатов для старых браузеров (или, может быть, строк в этих браузерах в UTF-16?).

Любые подсказки?

Обновление:

Пример: длина в байтах строки ЭЭХ! Naïve? в UTF-8 составляет 15 байтов, но некоторые браузеры вместо этого сообщают о 23 байтах.

Ответы [ 12 ]

79 голосов
/ 01 апреля 2011

Нет способа сделать это в 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);
}
71 голосов
/ 17 декабря 2015

Прошли годы, и в настоящее время вы можете сделать это изначально

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

Обратите внимание, что это еще не поддерживается IE (или Edge) (вы можете использовать для этого полифилл ).

Документация MDN

Стандартные спецификации

56 голосов
/ 28 апреля 2014

Вот гораздо более быстрая версия, в которой не используются регулярные выражения, а также 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 и ВТФ16 в Википедии)суррогатные символы).

Это следует RFC3629 (где символы UTF-8 имеют длину не более 4 байтов).

37 голосов
/ 09 марта 2017

Для простой кодировки UTF-8 с немного лучшей совместимостью, чем TextEncoder, Blob добивается целиНе будет работать в очень старых браузерах.

new Blob(["?"]).size; // -> 4  
27 голосов
/ 30 августа 2012

Эта функция возвращает размер байта любой строки UTF-8, которую вы ей передаете.

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

Источник

13 голосов
/ 20 сентября 2017

Еще один очень простой подход с использованием Buffer (только для NodeJS):

Buffer.from(string).length
3 голосов
/ 01 апреля 2011

На самом деле, я понял, что не так.Чтобы код работал, страница <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.

2 голосов
/ 15 февраля 2018

Мне потребовалось время, чтобы найти решение для React Native , поэтому я поставлю его здесь:

Сначала установите пакет buffer:

npm install --save buffer

Затем используйте метод узла:

const { Buffer } = require('buffer');
const length = Buffer.byteLength(string, 'utf-8');
2 голосов
/ 21 января 2016

Вот независимый и эффективный метод подсчета байтов 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

1 голос
/ 07 мая 2019

В NodeJS Buffer.byteLength - метод, специально предназначенный для этой цели:

let strLengthInBytes = Buffer.byteLength(str); // str is UTF-8

Обратите внимание, что по умолчанию метод предполагает, что строка находится в кодировке UTF-8. Если требуется другая кодировка, передайте ее в качестве второго аргумента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...