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

IT技术 javascript unicode
2021-02-09 03:39:01

在我的 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 个字节。

6个回答

几年过去了,现在你可以在本地做到这一点

(new TextEncoder().encode('foo')).length

请注意,IE 不支持它(您可以为此使用 polyfill)。

MDN 文档

标准规格

TextEncode自 Chrome 53 起仅支持utf-8
2021-03-16 03:39:01
如果您只需要长度,分配一个新字符串,进行实际转换,获取长度,然后丢弃该字符串可能会有点过分。有关仅以有效方式计算长度的函数,请参阅我上面的答案。
2021-03-26 03:39:01
请注意,根据MDN 文档,Safari (WebKit) 尚不支持 TextEncoder。
2021-03-29 03:39:01
多么奇妙的现代方法。谢谢!
2021-03-31 03:39:01

在 JavaScript 中本机无法做到这一点。(有关现代方法,请参阅Riccardo Galli 的回答。)


用于历史参考或 TextEncoder API仍然不可用的地方

如果您知道字符编码,则可以自己计算。

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 中计算的额外字节。

维基百科的表格更清楚

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);
}
这个解决方案很酷,但不考虑 utf8mb4。例如,encodeURIComponent('🍀')'%F0%9F%8D%80'
2021-03-13 03:39:01
@Alexander,当您将消息发送到服务器时,您是否通过 HTTP 标头指定了消息正文的内容编码?
2021-03-24 03:39:01
@亚历山大,很酷。如果您要建立协议,强制使用 UTF-8 是文本交换的好主意。少一个可能导致不匹配的变量。UTF-8 应该是字符编码的网络字节顺序。
2021-03-27 03:39:01
@MikeSamuel:lengthInUtf8Bytes对于非 BMP 字符,函数返回 5,str.length对于这些返回 2。我将编写此函数的修改版本到答案部分。
2021-03-30 03:39:01
那么,我怎么知道数据的字符编码呢?我需要对提供给我的 JS 库的任何字符串用户(程序员)进行编码。
2021-04-11 03:39:01

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

这是性能比较

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

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

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

new Blob(["😀"]).size; // -> 4  
这甚至比 TextEncoder 更好,需要实际回答。不需要polyfill。
2021-04-03 03:39:01

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

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

来源

@MayWeatherVN 你错误ユーザーコード的字节长度总是 21,我在不同的工具上测试过;对你的评论更友好;)
2021-03-15 03:39:01
它不适用于字符串“ユーザーコード”,预期长度为 14,但长度为 21
2021-04-01 03:39:01
我记得在 php 上测试的这个字符串是 14
2021-04-01 03:39:01