JavaScript中字符串长度(以字节为单位

时间:2011-04-01 15:59:37

标签: javascript unicode

在我的JavaScript代码中,我需要以这种格式向服务器撰写消息:

<size in bytes>CRLF
<data>CRLF

示例:

3
foo

数据可能包含unicode字符。我需要将它们作为UTF-8发送。

我正在寻找最常用的跨浏览器方式来计算JavaScript中字符串的长度。

我试过这个来组成我的有效载荷:

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

但它没有给我准确的旧浏览器结果(或者,这些浏览器中的字符串是UTF-16吗?)。

任何线索?

更新

示例:UTF-8中字符串ЭЭХ! Naïve?的字节长度为15个字节,但有些浏览器报告的是23个字节。

14 个答案:

答案 0 :(得分:79)

本机无法在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编码多字节序列的方式。对于单字节序列,第一个编码字节始终以高位0开始,或者以第一个十六进制数字为C,D,E或F的字节开始。第二个和后续字节是前两个字节为10的字节那些是你想用UTF-8计算的额外字节。

wikipedia中的表格更清晰

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

答案 1 :(得分:68)

多年过去了,现在你可以原生地做到这一点

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

请注意,IE(或Edge)尚未支持它(您可能会use a polyfill)。

MDN documentation

Standard specifications

答案 2 :(得分:56)

这是一个更快的版本,它不使用正则表达式,也不使用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;
}

这是performance comparison

它只计算charCodeAt返回的每个unicode代码点的UTF8长度(基于维基百科对UTF8和UTF16代理字符的描述)。

它遵循 RFC3629 (其中UTF-8字符最多为4个字节长)。

答案 3 :(得分:36)

对于简单的UTF-8编码,兼容性略高于TextEncoder,Blob可以解决这个问题。但是,在非常老的浏览器中工作不会。

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

答案 4 :(得分:27)

此函数将返回传递给它的任何UTF-8字符串的字节大小。

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

Source

答案 5 :(得分:13)

使用Buffer的另一种非常简单的方法(仅适用于NodeJS):

Buffer.from(string).length

答案 6 :(得分:3)

实际上,我发现了什么是错的。要使代码生效,页面<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就是巧合。

答案 7 :(得分:2)

这是一种计算字符串的UTF-8字节的独立且有效的方法。

&#13;
&#13;
//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));
&#13;
&#13;
&#13;

注意如果输入字符串是UCS-2格式错误,该方法可能会抛出错误

答案 8 :(得分:2)

花了一些时间找到 React Native 的解决方案,所以我会把它放在这里:

首先安装buffer包:

npm install --save buffer

然后使用节点方法:

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

答案 9 :(得分:1)

你可以试试这个:

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

它对我有用。

答案 10 :(得分:1)

这适用于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. 

答案 11 :(得分:1)

我比较了Firefox中为提高速度而建议的一些方法。

我使用的字符串包含以下字符: ?œ´®†¥¨ˆøπ¬˚∆˙©ƒ∂ßåΩ≈ç√∫〜µ≤

所有结果均为3次运行的平均值。时间以毫秒为单位。请注意,所有URIEncoding方法的行为都相似,并且具有极端的结果,因此我只包括其中一种。

虽然基于字符串的大小会有一些波动,但是charCode方法(lovasoa和fuweichin)的执行效果相似且总体上最快,而fuweichin的charCode方法最快。 Blob和TextEncoder方法的执行彼此相似。通常,charCode方法比Blob和TextEncoder方法快约75%。 URIEncoding方法基本上是不可接受的。

这是我得到的结果:

大小6.4 * 10 ^ 6字节:

Lauri Oherd – URIEncoding:     6400000    et: 796
lovasoa – charCode:            6400000    et: 15
fuweichin – charCode2:         6400000    et: 16
simap – Blob:                  6400000    et: 26
Riccardo Galli – TextEncoder:  6400000    et: 23

大小19.2 * 10 ^ 6字节: Blob在这里有点奇怪。

Lauri Oherd – URIEncoding:     19200000    et: 2322
lovasoa – charCode:            19200000    et: 42
fuweichin – charCode2:         19200000    et: 45
simap – Blob:                  19200000    et: 169
Riccardo Galli – TextEncoder:  19200000    et: 70

大小64 * 10 ^ 6字节:

Lauri Oherd – URIEncoding:     64000000    et: 12565
lovasoa – charCode:            64000000    et: 138
fuweichin – charCode2:         64000000    et: 133
simap – Blob:                  64000000    et: 231
Riccardo Galli – TextEncoder:  64000000    et: 211

大小192 * 10 ^ 6字节: URIEncoding方法此时会冻结浏览器。

lovasoa – charCode:            192000000    et: 754
fuweichin – charCode2:         192000000    et: 480
simap – Blob:                  192000000    et: 701
Riccardo Galli – TextEncoder:  192000000    et: 654

大小640 * 10 ^ 6字节:

lovasoa – charCode:            640000000    et: 2417
fuweichin – charCode2:         640000000    et: 1602
simap – Blob:                  640000000    et: 2492
Riccardo Galli – TextEncoder:  640000000    et: 2338

大小1280 * 10 ^ 6字节: Blob和TextEncoder方法在这里开始碰壁。

lovasoa – charCode:            1280000000    et: 4780
fuweichin – charCode2:         1280000000    et: 3177
simap – Blob:                  1280000000    et: 6588
Riccardo Galli – TextEncoder:  1280000000    et: 5074

大小1920 * 10 ^ 6字节:

lovasoa – charCode:            1920000000    et: 7465
fuweichin – charCode2:         1920000000    et: 4968
JavaScript error: file:///Users/xxx/Desktop/test.html, line 74: NS_ERROR_OUT_OF_MEMORY:

代码如下:

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

function byteLengthCharCode(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;
}

function byteLengthCharCode2(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;
}

function byteLengthBlob(str) {
  return new Blob([str]).size;
}

function byteLengthTE(str) {
  return (new TextEncoder().encode(str)).length;
}

var sample = "?œ´®†¥¨ˆøπ¬˚∆˙©ƒ∂ßåΩ≈ç√∫˜µ≤i";
var string = "";

// Adjust multiplier to change length of string.
let mult = 1000000;

for (var i = 0; i < mult; i++) {
  string += sample;
}

let t0;

try {
  t0 = Date.now();
  console.log("Lauri Oherd – URIEncoding:   " + byteLengthURIEncoding(string) + "    et: " + (Date.now() - t0));
} catch(e) {}

t0 = Date.now();
console.log("lovasoa – charCode:            " + byteLengthCharCode(string) + "    et: " + (Date.now() - t0));

t0 = Date.now();
console.log("fuweichin – charCode2:         " + byteLengthCharCode2(string) + "    et: " + (Date.now() - t0));

t0 = Date.now();
console.log("simap – Blob:                  " + byteLengthBlob(string) + "    et: " + (Date.now() - t0));

t0 = Date.now();
console.log("Riccardo Galli – TextEncoder:  " + byteLengthTE(string) + "    et: " + (Date.now() - t0));

答案 12 :(得分:0)

在NodeJS中,Buffer.byteLength是专门用于此目的的方法:

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

请注意,默认情况下,该方法假定字符串采用UTF-8编码。如果需要其他编码,请将其作为第二个参数传递。

答案 13 :(得分:0)

遇到这个How to emulate mb_strlen in javascript with strings containing HTML

该字符串与之前的答案不匹配的地方。

我在这里得到了 8 的预期长度:

const str = 'X&nbsp;&#34;FUEL&#34;'
const div = document.createElement("div");
div.innerHTML = str
console.log(div.textContent.length)