JavaScript字符串中有多少个字节?


97

从服务器以UTF-8发送时,我有一个大约500K的JavaScript字符串。如何用JavaScript判断其大小?

我知道JavaScript使用UCS-2,因此每个字符2个字节。但是,这取决于JavaScript实现吗?还是页面编码或内容类型?


大约 答案将是length * charsize,因此您的猜测接近了。
glasnt 2010年

1
现代JavaScript(例如ES6)不仅使用UCS-2,在这里更详细地介绍:stackoverflow.com/a/46735247/700206
whitneyland

Answers:


36

String值不依赖于实现,根据ECMA-262第三版规范,每个字符代表UTF-16文本单个16位单元

4.3.16字符串值

字符串值是String类型的成员,并且是零个或多个16位无符号整数值的有限有序序列。

注意尽管每个值通常代表UTF-16文本的单个16位单元,但是语言对这些值没有任何限制或要求,只是它们是16位无符号整数。


8
我阅读该段文章并不意味着实现独立性。
Paul Biggar 2010年

4
不保证UTF-16,仅保证将字符串存储为16位整数。
bjornl

就UTF-16而言,这仅取决于实现。16位字符描述是通用的。
Panzercrisis

1
我认为Firefox的内部甚至可以使用每个字符1个字节的一些字符串.... blog.mozilla.org/javascript/2014/07/21/...
米哈尔Charemza

1
明确不允许使用UTF-16进行阅读。UTF-16字符最多可包含4个字节,但规范指出“值必须是16位无符号整数”。这意味着JavaScript字符串值是UTF-16的子集,但是,任何使用3或4个字节字符的UTF-16字符串都是不允许的。
whitneyland '17

71

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

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

资源

JavaScript引擎可以在内部自由使用UCS-2或UTF-16。我知道的大多数引擎都使用UTF-16,但是无论他们做出什么选择,它只是实现细节,不会影响语言的特性。

但是,ECMAScript / JavaScript语言本身会根据UCS-2(而不是UTF-16)公开字符。

资源


9
使用.split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./)代替。您的代码段无法编码为“%uXXXX”的字符串。
罗布W

用于在websocket框架上计算尺寸,为String框架提供与chrome dev工具相同的尺寸。
2015年

2
s3用于上传到s3的javascript字符串,显示的大小完全相同[[(byteCount(s))/ 1024).toFixed(2)+“ KiB”]
user85155 2015年


41

您可以使用Blob获取以字节为单位的字符串大小。

例子:

console.info(
  new Blob(['😂']).size,                             // 4
  new Blob(['👍']).size,                             // 4
  new Blob(['😂👍']).size,                           // 8
  new Blob(['👍😂']).size,                           // 8
  new Blob(['I\'m a string']).size,                  // 12

  // from Premasagar correction of Lauri's answer for
  // strings containing lone characters in the surrogate pair range:
  // https://stackoverflow.com/a/39488643/6225838
  new Blob([String.fromCharCode(55555)]).size,       // 3
  new Blob([String.fromCharCode(55555, 57000)]).size // 4 (not 6)
);


2
感谢上帝的一斑!对于现代浏览器,这可能应该是公认的答案。
prasanthv

如何在Node.js中导入Blob?
亚历山大·米尔斯

4
啊,例如,在Node.js中,我们使用BufferBuffer.from('😂').length
Alexander Mills

19

使用unescape js函数尝试以下组合:

const byteAmount = unescape(encodeURIComponent(yourString)).length

完整编码过程示例:

const s  = "1 a ф № @ ®"; //length is 11
const s2 = encodeURIComponent(s); //length is 41
const s3 = unescape(s2); //length is 15 [1-1,a-1,ф-2,№-3,@-1,®-2]
const s4 = escape(s3); //length is 39
const s5 = decodeURIComponent(s4); //length is 11

4
unescapeJavaScript函数已被废弃,不应该被用来解码统一资源标识符(URI)。来源
Lauri Oherd

@LauriOherd我知道注释很旧,但是:在此答案中,unescape未使用它来解码URI。它用于将%xx序列转换为单个字符。AsencodeURIComponent将字符串编码为UTF-8,以其相应的ASCII字符或%xx序列的形式表示代码单位,调用将unescape(encodeURIComponent(...))导致包含原始字符串的UTF-8表示形式的二进制字符串。.length正确调用会给出以UTF-8编码的字符串的大小(以字节为单位)。
TS TS

是的(unescape自1999年起就已弃用,但仍在所有浏览器中都可用...-就是说,有充分的理由弃用它。基本上没有办法正确使用它们(除了将UTF8与en-/ decodeURIComponent)结合进行en //解码,或者至少我不知道(un)有任何其他有用的应用程序escape)。今天,有更好的替代方法来编码/解码UTF8(TextEncoder等)
TS


7

UTF-8使用每个代码点1到4个字节来编码字符。就像CMS在接受的答案中指出的那样,JavaScript会使用16位(2个字节)在内部存储每个字符。

如果您通过循环解析字符串中的每个字符并计算每个代码点使用的字节数,然后将总数乘以2,则该UTF-8编码字符串的JavaScript内存使用量应以字节为单位。也许是这样的:

      getStringMemorySize = function( _string ) {
        "use strict";

        var codePoint
            , accum = 0
        ;

        for( var stringIndex = 0, endOfString = _string.length; stringIndex < endOfString; stringIndex++ ) {
            codePoint = _string.charCodeAt( stringIndex );

            if( codePoint < 0x100 ) {
                accum += 1;
                continue;
            }

            if( codePoint < 0x10000 ) {
                accum += 2;
                continue;
            }

            if( codePoint < 0x1000000 ) {
                accum += 3;
            } else {
                accum += 4;
            }
        }

        return accum * 2;
    }

例子:

getStringMemorySize( 'I'    );     //  2
getStringMemorySize( '❤'    );     //  4
getStringMemorySize( '𠀰'   );     //  8
getStringMemorySize( 'I❤𠀰' );     // 14

6

这是我使用的3种方式:

  1. TextEncoder()

    (new TextEncoder().encode("myString")).length)

  2. 斑点

    new Blob(["myString"]).size)

  3. 缓冲

    Buffer.byteLength("myString", 'utf8'))


4

JavaScript字符串的大小为

  • ES6之前的版本:每个字符2个字节
  • ES6及更高版本:每个字符2个字节,或者每个字符5个或更多字节

ES6之前
始终每个字符2个字节。不允许使用UTF-16,因为规范指出“值必须是16位无符号整数”。由于UTF-16字符串可以使用3个或4个字节的字符,因此将违反2个字节的要求。至关重要的是,虽然不能完全支持UTF-16,但该标准确实要求使用的两个字节字符为有效的UTF-16字符。换句话说,ES6之前的JavaScript字符串支持UTF-16字符的子集。

ES6及更高版本,
每个字符2个字节,或者每个字符5个或更多字节。由于ES6(ECMAScript 6)添加了对Unicode代码点转义的支持,因此其他大小起作用。使用Unicode转义看起来像这样:\ u {1D306}

实用笔记

  • 这与特定引擎的内部实现无关。例如,某些引擎使用具有完全UTF-16支持的数据结构和库,但是它们从外部提供的内容不一定是完全UTF-16支持。同样,引擎也可以提供外部UTF-16支持,但并非必须如此。

  • 对于ES6,实际上来说字符长度不会超过5个字节(转义点为2个字节,Unicode码点为3个字节),因为最新版本的Unicode仅有136,755个可能的字符,很容易就可以容纳3个字节。但是,这在技术上不受标准的限制,因此原则上单个字符可以使用4个字节作为代码点,总共6个字节。

  • 此处用于计算字节大小的大多数代码示例似乎都未考虑ES6 Unicode代码点转义,因此在某些情况下结果可能不正确。


1
只是想知道,如果尺寸为每个字符2个字节,为什么Buffer.from('test').lengthBuffer.byteLength('test')4(在节点)相等,new Blob(['test']).size也等于4?
user1063287

ES6之前的版本:允许使用UTF-16:请参阅ECMA-262第3版(自1999年起):第1页指出允许使用UCS2或UTF-16。第5页,字符串值的定义:“ ...尽管每个值通常代表UTF-16文本的单个16位单元,但...”。在第81页上的表格显示了如何将匹配的代理对必须编码为四个UTF-8字节。
TS TS

“每个字符”-如果您是说每个“用户感知的字符”(规范更简单的说明),则它可以是任意数量的16位代码单元。如果按“代码点”表示,则它可以是UTF-16中的一个或两个16位代码单元。(它不能是2.5个代码单位(或如何获得5个字节?)
TS

标准中未定义JavaScript字符串中的每个元素(16位无符号整数值(“元素”))实际上是否在内部由两个字节表示。(以及怎么可能-只要提供给javascript程序的接口遵循该标准,一切都会按预期工作。)例如,如果字符串仅包含latin1,则
TS

Unicode代码点转义与字符串长度无关-这只是一种在源代码中表示字符串的新方法。('\u{1F600}'.length===2'\u{1F600}'==='\uD83D\uDE00''\u{1F600}'==='😀'
TS

3

JavaScript字符串中的单个元素被视为单个UTF-16代码单元。也就是说,字符串字符以16位(1个代码单位)存储,并且16位等于2个字节(8位= 1个字节)。

charCodeAt()方法可用于返回0到65535之间的整数,该整数表示给定索引处的UTF-16代码单元。

codePointAt()可用于为Unicode字符返回整个码点值,例如UTF-32。

如果无法在单个16位代码单元中表示UTF-16字符,它将具有一个代理对,因此使用两个代码单元(2 x 16位= 4字节)

有关不同的编码及其代码范围,请参见Unicode编码


您关于代理人的说法似乎违反了ECMA脚本规范。正如我在上面评论的那样,规范每个字符需要两个字节,并且允许代理对会违反此规定。
whitneyland '17

Javascript ES5引擎在内部可以自由使用USC-2或UTF-16,但实际上使用的是带有代理的UCS-2。这是因为它允许将代理半部分公开为单独的字符,即单个UTF-16无符号整数。如果您在源代码中使用一个Unicode字符,需要用一个以上的16位代码单元表示,则将使用一个代理对。这种行为并不违反规范,请参阅第6章源文本:ecma-international.org/ecma-262/5.1
holmberd

2

Lauri Oherd的答案适用于在野外看到的大多数字符串,但如果字符串包含在代理对范围(0xD800至0xDFFF)中的孤单字符,则该答案将失败。例如

byteCount(String.fromCharCode(55555))
// URIError: URI malformed

此较长的函数应处理所有字符串:

function bytes (str) {
  var bytes=0, len=str.length, codePoint, next, i;

  for (i=0; i < len; i++) {
    codePoint = str.charCodeAt(i);

    // Lone surrogates cannot be passed to encodeURI
    if (codePoint >= 0xD800 && codePoint < 0xE000) {
      if (codePoint < 0xDC00 && i + 1 < len) {
        next = str.charCodeAt(i + 1);

        if (next >= 0xDC00 && next < 0xE000) {
          bytes += 4;
          i++;
          continue;
        }
      }
    }

    bytes += (codePoint < 0x80 ? 1 : (codePoint < 0x800 ? 2 : 3));
  }

  return bytes;
}

例如

bytes(String.fromCharCode(55555))
// 3

它将正确计算包含代理对的字符串的大小:

bytes(String.fromCharCode(55555, 57000))
// 4 (not 6)

可以将结果与Node的内置函数进行比较Buffer.byteLength

Buffer.byteLength(String.fromCharCode(55555), 'utf8')
// 3

Buffer.byteLength(String.fromCharCode(55555, 57000), 'utf8')
// 4 (not 6)

1

我正在使用V8引擎的嵌入式版本。我已经测试了一个字符串。每个步骤推1000个字符。UTF-8。

第一次测试使用单字节(8位,ANSI)字符“ A”(十六进制:41)。第二个测试使用两个字节字符(16位)“Ω”(十六进制:CE A9),第三个测试使用三个字节字符(24位)“☺”(十六进制:E2 98 BA)。

在这三种情况下,设备都将以888 000个字符打印出内存,并且使用ca。RAM中的26348 kb

结果:字符不是动态存储的。而且不是只有16位。-好的,也许仅就我而言(嵌入式128 MB RAM设备,V8引擎C ++ / QT)-字符编码与javascript引擎的ram大小无关。例如encodingURI等仅对高级数据传输和存储有用。

是否嵌入,事实是字符不仅以16bit存储。不幸的是,我没有100%的答案,即Javascript在低级别区域的作用。顺便说一句。我已经用字符“ A”的数组测试了相同的(上面的第一个测试)。每步推送1000个项目。(完全相同的测试。只是将字符串替换为数组),使用10 416 KB且数组长度为1 337 000后,系统导致内存不足(需要)(数组)。因此,javascript引擎不是简单的限制。这有点复杂。


0

您可以尝试以下方法:

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

它为我工作。


1
当然,这假定所有字符最大为2个字节吗?如果有3个或4个字节的字符(在UTF-8中是可能的),那么此函数将它们仅计为2个字节的字符吗?
亚当·伯利
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.