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

Ale*_*ysh 85 javascript unicode

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

<size in bytes>CRLF
<data>CRLF
Run Code Online (Sandbox Code Playgroud)

例:

3
foo
Run Code Online (Sandbox Code Playgroud)

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

我正在寻找最流行的浏览器方式来计算JavaScript中字符串的长度(以字节为单位).

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

return unescape(encodeURIComponent(str)).length + "\n" + str + "\n"
Run Code Online (Sandbox Code Playgroud)

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

有线索吗?

更新:

示例:???! Naïve?UTF-8中字符串的长度(以字节为单位)为15个字节,但某些浏览器报告的是23个字节.

Ric*_*lli 90

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

(new TextEncoder().encode('foo')).length
Run Code Online (Sandbox Code Playgroud)

请注意,IE(或Edge)尚不支持它(您可以使用polyfill).

MDN文档

标准规格

  • 多么奇妙的现代方法.谢谢! (4认同)
  • 如果您只需要长度,那么分配一个新字符串,进行实际转换,获取长度,然后丢弃该字符串可能有点过分了。请参阅我上面的答案,了解一个仅以有效方式计算长度的函数。 (2认同)

Mik*_*uel 81

在本机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);
}
Run Code Online (Sandbox Code Playgroud)

这应该有效,因为UTF-8编码多字节序列的方式.对于单字节序列,第一个编码字节始终以高位0开始,或者以第一个十六进制数字为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
...
Run Code Online (Sandbox Code Playgroud)

如果你需要了解页面编码,你可以使用这个技巧:

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);
}
Run Code Online (Sandbox Code Playgroud)

  • @MikeSamuel:`lengthInUtf8Bytes`函数为非BMP字符返回5作为这些返回2的`str.length`.我将把这个函数的修改版本写入答案部分. (4认同)
  • 这个解决方案很酷,但不考虑 utf8mb4。例如,`encodeURIComponent('')` 是 `'%F0%9F%8D%80'`。 (3认同)

lov*_*soa 60

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

这是性能比较.

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

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


sim*_*map 40

对于简单的UTF-8编码,兼容性稍好一些TextEncoder,Blob可以解决问题.但是在旧的浏览器中不起作用.

new Blob([""]).size; // -> 4  
Run Code Online (Sandbox Code Playgroud)


Lau*_*erd 29

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

function byteCount(s) {
    return encodeURI(s).split(/%..|./).length - 1;
}
Run Code Online (Sandbox Code Playgroud)

资源

  • @MayWeatherVN 你错了 ```ユーザーコード``` 字节长度始终是 21,我在不同的工具上测试了它;对您的评论更加友善;) (2认同)

Ivá*_*rez 15

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

Buffer.from(string).length
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用 Buffer.byteLength(string, 'utf8') 跳过创建缓冲区。 (4认同)

All*_*sso 11

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

\n

我使用的字符串包含以下字符:\n\xc5\x93\xc2\xb4\xc2\xae\xe2\x80\xa0\xc2\xa5\xc2\xa8\xcb\x86\xc3\xb8\xcf\x80\ xc2\xac\xcb\x9a\xe2\x88\x86\xcb\x99\xc2\xa9\xc6\x92\xe2\x88\x82\xc3\x9f\xc3\xa5\xce\xa9\xe2\x89\x88\ xc3\xa7\xe2\x88\x9a\xe2\x88\xab\xcb\x9c\xc2\xb5\xe2\x89\xa4

\n

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

\n

虽然根据字符串的大小存在一些波动,但 charCode 方法(lovasoa 和 fuweichin)的性能相似且总体速度最快,其中 fuweichin 的 charCode 方法最快。Blob 和 TextEncoder 方法的执行方式类似。一般来说,charCode 方法比 Blob 和 TextEncoder 方法快约 75%。URIEncoding方法基本上是不可接受的。

\n

这是我得到的结果:

\n

大小 6.4 * 10^6 字节:

\n
Lauri Oherd \xe2\x80\x93 URIEncoding:     6400000    et: 796\nlovasoa \xe2\x80\x93 charCode:            6400000    et: 15\nfuweichin \xe2\x80\x93 charCode2:         6400000    et: 16\nsimap \xe2\x80\x93 Blob:                  6400000    et: 26\nRiccardo Galli \xe2\x80\x93 TextEncoder:  6400000    et: 23\n
Run Code Online (Sandbox Code Playgroud)\n

大小 19.2 * 10^6 字节:\nBlob 在这里做了一些奇怪的事情。

\n
Lauri Oherd \xe2\x80\x93 URIEncoding:     19200000    et: 2322\nlovasoa \xe2\x80\x93 charCode:            19200000    et: 42\nfuweichin \xe2\x80\x93 charCode2:         19200000    et: 45\nsimap \xe2\x80\x93 Blob:                  19200000    et: 169\nRiccardo Galli \xe2\x80\x93 TextEncoder:  19200000    et: 70\n
Run Code Online (Sandbox Code Playgroud)\n

大小 64 * 10^6 字节:

\n
Lauri Oherd \xe2\x80\x93 URIEncoding:     64000000    et: 12565\nlovasoa \xe2\x80\x93 charCode:            64000000    et: 138\nfuweichin \xe2\x80\x93 charCode2:         64000000    et: 133\nsimap \xe2\x80\x93 Blob:                  64000000    et: 231\nRiccardo Galli \xe2\x80\x93 TextEncoder:  64000000    et: 211\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
lovasoa \xe2\x80\x93 charCode:            192000000    et: 754\nfuweichin \xe2\x80\x93 charCode2:         192000000    et: 480\nsimap \xe2\x80\x93 Blob:                  192000000    et: 701\nRiccardo Galli \xe2\x80\x93 TextEncoder:  192000000    et: 654\n
Run Code Online (Sandbox Code Playgroud)\n

大小 640 * 10^6 字节:

\n
lovasoa \xe2\x80\x93 charCode:            640000000    et: 2417\nfuweichin \xe2\x80\x93 charCode2:         640000000    et: 1602\nsimap \xe2\x80\x93 Blob:                  640000000    et: 2492\nRiccardo Galli \xe2\x80\x93 TextEncoder:  640000000    et: 2338\n
Run Code Online (Sandbox Code Playgroud)\n

大小 1280 * 10^6 字节:\nBlob 和 TextEncoder 方法在这里开始遇到困难。

\n
lovasoa \xe2\x80\x93 charCode:            1280000000    et: 4780\nfuweichin \xe2\x80\x93 charCode2:         1280000000    et: 3177\nsimap \xe2\x80\x93 Blob:                  1280000000    et: 6588\nRiccardo Galli \xe2\x80\x93 TextEncoder:  1280000000    et: 5074\n
Run Code Online (Sandbox Code Playgroud)\n

大小 1920 * 10^6 字节:

\n
lovasoa \xe2\x80\x93 charCode:            1920000000    et: 7465\nfuweichin \xe2\x80\x93 charCode2:         1920000000    et: 4968\nJavaScript error: file:///Users/xxx/Desktop/test.html, line 74: NS_ERROR_OUT_OF_MEMORY:\n
Run Code Online (Sandbox Code Playgroud)\n

这是代码:

\n

\r\n
\r\n
Lauri Oherd \xe2\x80\x93 URIEncoding:     6400000    et: 796\nlovasoa \xe2\x80\x93 charCode:            6400000    et: 15\nfuweichin \xe2\x80\x93 charCode2:         6400000    et: 16\nsimap \xe2\x80\x93 Blob:                  6400000    et: 26\nRiccardo Galli \xe2\x80\x93 TextEncoder:  6400000    et: 23\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n


lau*_*ent 7

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

首先安装buffer软件包:

npm install --save buffer
Run Code Online (Sandbox Code Playgroud)

然后使用节点方法:

const { Buffer } = require('buffer');
const length = Buffer.byteLength(string, 'utf-8');
Run Code Online (Sandbox Code Playgroud)


Ale*_*ysh 5

事实上,我想出了什么问题。为了使代码工作,页面<head>应该有这个标签:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Run Code Online (Sandbox Code Playgroud)

或者,正如评论中所建议的,如果服务器发送 HTTPContent-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>
Run Code Online (Sandbox Code Playgroud)

注意:我怀疑指定任何(准确的)编码都会解决编码问题。我需要UTF-8只是一个巧合。

  • `unescape` JavaScript 函数 [不应该](http://msdn.microsoft.com/en-us/library/dz4x90hk(v=vs.94).aspx) 用于解码统一资源标识符 (URI)。 (2认同)
  • @LauriOherd `unescape` 确实不应该用于解码 URI。但是,要将文本转换为 UTF-8,它可以[很好](/sf/ask/155366851/#comment101351898_2858850) (2认同)

Boa*_*oaz 5

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

let strLengthInBytes = Buffer.byteLength(str); // str is UTF-8
Run Code Online (Sandbox Code Playgroud)

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