Có bao nhiêu byte trong một chuỗi JavaScript?


97

Tôi có một chuỗi javascript khoảng 500K khi được gửi từ máy chủ trong UTF-8. Làm cách nào để biết kích thước của nó trong JavaScript?

Tôi biết rằng JavaScript sử dụng UCS-2, điều đó có nghĩa là 2 byte cho mỗi ký tự. Tuy nhiên, nó có phụ thuộc vào việc triển khai JavaScript không? Hoặc trên mã hóa trang hoặc có thể là loại nội dung?


Khoảng câu trả lời sẽ là độ dài * ký tự, vì vậy bạn đoán gần đúng.
glasnt

1
Hiện đại JavaScript, ví dụ ES6, không chỉ sử dụng UCS-2, chi tiết hơn ở đây: stackoverflow.com/a/46735247/700206
whitneyland

Câu trả lời:


36

Stringcác giá trị không phụ thuộc vào việc triển khai, theo Thông số kỹ thuật của ECMA-262 phiên bản thứ 3 , mỗi ký tự đại diện cho một đơn vị 16 bit của văn bản UTF-16 :

4.3.16 Giá trị chuỗi

Giá trị chuỗi là thành viên của kiểu Chuỗi và là một chuỗi có thứ tự hữu hạn gồm 0 hoặc nhiều giá trị số nguyên 16 bit không dấu.

CHÚ THÍCH: Mặc dù mỗi giá trị thường đại diện cho một đơn vị 16 bit duy nhất của văn bản UTF-16, ngôn ngữ này không đặt ra bất kỳ hạn chế hoặc yêu cầu nào đối với các giá trị ngoại trừ việc chúng là số nguyên 16 bit không dấu.


8
Việc tôi đọc đoạn văn đó không ngụ ý sự độc lập trong việc triển khai.
Paul Biggar

4
UTF-16 không được đảm bảo, chỉ thực tế là các chuỗi được lưu trữ dưới dạng int 16 bit.
bjornl

Nó chỉ phụ thuộc vào việc triển khai liên quan đến UTF-16. Mô tả ký tự 16 bit là phổ biến.
Panzercrisis

1
Tôi nghĩ rằng trong nội bộ Firefox thậm chí có thể sử dụng 1 byte cho mỗi ký tự đối với một số chuỗi .... blog.mozilla.org/javascript/2014/07/21/...
Michal Charemza

1
UTF-16 rõ ràng không được phép theo cách tôi đang đọc nó. Ký tự UTF-16 có thể có tối đa 4 byte, nhưng thông số kỹ thuật cho biết "giá trị phải là số nguyên không dấu 16 bit". Điều này có nghĩa là các giá trị chuỗi JavaScript là một tập hợp con của UTF-16, tuy nhiên, bất kỳ chuỗi UTF-16 nào sử dụng ký tự 3 hoặc 4 byte sẽ không được phép.
whitneyland

71

Hàm này sẽ trả về kích thước byte của bất kỳ chuỗi UTF-8 nào mà bạn chuyển cho nó.

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

Nguồn

Các công cụ JavaScript được miễn phí sử dụng nội bộ UCS-2 hoặc UTF-16. Hầu hết các công cụ mà tôi biết đều sử dụng UTF-16, nhưng bất cứ lựa chọn nào họ đưa ra, đó chỉ là một chi tiết triển khai sẽ không ảnh hưởng đến các đặc tính của ngôn ngữ.

Tuy nhiên, bản thân ngôn ngữ ECMAScript / JavaScript hiển thị các ký tự theo UCS-2, không phải UTF-16.

Nguồn


9
Sử dụng .split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./)thay thế. Đoạn mã của bạn không thành công đối với các chuỗi mã hóa thành "% uXXXX".
Rob W

Được sử dụng để tính toán kích thước trên khung websocket, cung cấp cùng kích thước cho khung Chuỗi như các công cụ dành cho nhà phát triển chrome.
user85155

2
Được sử dụng cho các chuỗi javascript được tải lên s3, s3 hiển thị chính xác cùng kích thước [(byteCount (s)) / 1024) .toFixed (2) + "KiB"]
user85155


41

Bạn có thể sử dụng Blob để lấy kích thước chuỗi tính bằng byte.

Ví dụ:

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
Cảm ơn chúa vì những đốm màu! Đây có lẽ nên là câu trả lời được chấp nhận cho các trình duyệt hiện đại.
prasanthv

làm thế nào để nhập Blob trong Node.js?
Alexander Mills

4
Ahh, với Node.js chúng tôi sử dụng đệm, ví dụBuffer.from('😂').length
Alexander Mills

19

Hãy thử kết hợp này với việc sử dụng hàm js unescape :

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

Ví dụ về quy trình mã hóa đầy đủ:

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
Hàm unescapeJavaScript không được dùng nữa và không được sử dụng để giải mã Số nhận dạng tài nguyên đồng nhất (URI). Nguồn
Lauri Oherd

@LauriOherd Tôi biết nhận xét đã cũ, nhưng: Trong câu trả lời này, unescapekhông được sử dụng để giải mã các URI. Nó được sử dụng để chuyển đổi %xxchuỗi thành các ký tự đơn. Khi encodeURIComponentmã hóa một chuỗi dưới dạng UTF-8, đại diện cho các đơn vị mã dưới dạng ký tự ASCII tương ứng của nó hoặc dưới dạng một %xxchuỗi, việc gọi unescape(encodeURIComponent(...))kết quả trong một chuỗi nhị phân có chứa biểu diễn UTF-8 của chuỗi gốc. Gọi .lengthchính xác cung cấp kích thước tính bằng byte của chuỗi được mã hóa là UTF-8.
TS

Và yes ( un) escapekhông được dùng nữa kể từ năm 1999 nhưng nó vẫn có sẵn trong mọi trình duyệt ... - Điều đó nói rằng, có lý do chính đáng để không dùng nó. Về cơ bản, không có cách nào để sử dụng chúng một cách chính xác (ngoại trừ en- / decoding UTF8 kết hợp với en- / decodeURI( Component) - hoặc ít nhất tôi không biết bất kỳ ứng dụng hữu ích nào khác cho ( un) escape). Và ngày nay có nhiều lựa chọn thay thế tốt hơn để mã hóa / giải mã UTF8 ( TextEncoder, v.v.)
TS

10

Lưu ý rằng nếu bạn đang nhắm mục tiêu node.js, bạn có thể sử dụng Buffer.from(string).length:

var str = "\u2620"; // => "☠"
str.length; // => 1 (character)
Buffer.from(str).length // => 3 (bytes)

7

UTF-8 mã hóa các ký tự bằng cách sử dụng 1 đến 4 byte cho mỗi điểm mã. Như CMS đã chỉ ra trong câu trả lời được chấp nhận, JavaScript sẽ lưu trữ nội bộ mỗi ký tự bằng cách sử dụng 16 bit (2 byte).

Nếu bạn phân tích cú pháp từng ký tự trong chuỗi qua một vòng lặp và đếm số byte được sử dụng trên mỗi điểm mã, sau đó nhân tổng số với 2, bạn sẽ sử dụng bộ nhớ của JavaScript theo byte cho chuỗi được mã hóa UTF-8 đó. Có lẽ một cái gì đó như thế này:

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

Ví dụ:

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

7

Đây là 3 cách tôi sử dụng:

  1. TextEncoder ()

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

  2. Bãi

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

  3. Đệm

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


5

Kích thước của một chuỗi JavaScript là

  • Pre-ES6 : 2 byte mỗi ký tự
  • ES6 trở lên: 2 byte cho mỗi ký tự, hoặc 5 byte trở lên cho mỗi ký tự

Pre-ES6
Luôn là 2 byte cho mỗi ký tự. UTF-16 không được phép vì thông số cho biết "giá trị phải là số nguyên không dấu 16 bit". Vì chuỗi UTF-16 có thể sử dụng ký tự 3 hoặc 4 byte, nó sẽ vi phạm yêu cầu 2 byte. Điều quan trọng, mặc dù UTF-16 không thể được hỗ trợ đầy đủ, nhưng tiêu chuẩn yêu cầu hai ký tự byte được sử dụng là ký tự UTF-16 hợp lệ. Nói cách khác, các chuỗi JavaScript Pre-ES6 hỗ trợ một tập hợp con các ký tự UTF-16.

ES6 trở lên
2 byte cho mỗi ký tự hoặc 5 byte trở lên cho mỗi ký tự. Các kích thước bổ sung có tác dụng vì ES6 (ECMAScript 6) bổ sung hỗ trợ cho việc thoát điểm mã Unicode . Sử dụng lối thoát unicode trông giống như sau: \ u {1D306}

Ghi chú thực tế

  • Điều này không liên quan đến việc triển khai bên trong của một công cụ cụ thể. Ví dụ: một số công cụ sử dụng cấu trúc dữ liệu và thư viện với hỗ trợ UTF-16 đầy đủ, nhưng những gì chúng cung cấp bên ngoài không nhất thiết phải là hỗ trợ UTF-16 đầy đủ. Ngoài ra, một động cơ cũng có thể cung cấp hỗ trợ UTF-16 bên ngoài nhưng không bắt buộc phải làm như vậy.

  • Đối với ES6, các ký tự nói trên thực tế sẽ không bao giờ dài quá 5 byte (2 byte cho điểm thoát + 3 byte cho điểm mã Unicode) vì phiên bản mới nhất của Unicode chỉ có 136.755 ký tự khả thi, dễ dàng vừa với 3 byte. Tuy nhiên, về mặt kỹ thuật điều này không bị giới hạn bởi tiêu chuẩn nên về cơ bản, một ký tự đơn lẻ có thể sử dụng, nói là 4 byte cho điểm mã và tổng cộng 6 byte.

  • Hầu hết các ví dụ mã ở đây để tính toán kích thước byte dường như không tính đến điểm mã Unicode ES6 thoát ra, vì vậy kết quả có thể không chính xác trong một số trường hợp.


1
Chỉ cần tự hỏi, nếu kích thước là 2 byte cho mỗi nhân vật, tại sao Buffer.from('test').lengthBuffer.byteLength('test')bằng 4 (trong Node) và new Blob(['test']).sizecũng bằng 4?
user1063287,

Pre-ES6: UTF-16 được phép: Xem ECMA-262 phiên bản thứ 3 (từ năm 1999) : Trang một cho biết UCS2 hoặc UTF-16 được phép. Trang 5, định nghĩa giá trị chuỗi: "... Mặc dù mỗi giá trị thường đại diện cho một đơn vị 16 bit duy nhất của văn bản UTF-16, ...". Trên trang 81 là một bảng cho biết cách các cặp thay thế phù hợp phải được mã hóa dưới dạng bốn byte UTF-8.
TS

"per character" - Nếu theo ý bạn, mỗi "ký tự do người dùng cảm nhận" ( thông số kỹ thuật , giải thích đơn giản hơn ) thì nó có thể là bất kỳ số đơn vị mã 16bit nào. Nếu bạn muốn nói mỗi "codepoint", nó có thể là một hoặc hai đơn vị mã 16bit trong UTF-16 . (Nó không thể là 2,5 đơn vị mã (hoặc làm thế nào để bạn nhận được 5 byte?))
TS

Việc mỗi phần tử trong một chuỗi javascript ( giá trị số nguyên không dấu 16-bit (“phần tử”) ) có thực sự được biểu thị bên trong bằng hai byte hay không vẫn chưa được xác định trong tiêu chuẩn. (Và làm thế nào nó có thể là - Chừng nào giao diện cung cấp cho các chương trình javascript sau các công trình tất cả mọi thứ tiêu chuẩn như dự định.) Mozilla ví dụ có thể sử dụng chỉ một byte cho mỗi điểm mã nếu chuỗi chỉ chứa latin1
TS

Việc thoát điểm mã Unicode không liên quan gì đến độ dài chuỗi - nó chỉ là một cách mới để biểu diễn chuỗi trong mã nguồn. ( '\u{1F600}'.length===2, '\u{1F600}'==='\uD83D\uDE00', '\u{1F600}'==='😀')
TS

3

Một phần tử trong Chuỗi JavaScript được coi là một đơn vị mã UTF-16 duy nhất. Có nghĩa là, các ký tự chuỗi được lưu trữ trong 16-bit (1 đơn vị mã), và 16-bit tương đương với 2 byte (8-bit = 1 byte).

Các charCodeAt() phương pháp có thể được sử dụng để trả về một số nguyên giữa 0 và 65535 đại diện cho các đơn vị mã UTF-16 tại các chỉ số nhất định.

Các codePointAt() thể được sử dụng để trả về toàn bộ giá trị điểm mã cho các ký tự Unicode, ví dụ: UTF-32.

Khi một ký tự UTF-16 không thể được biểu diễn trong một đơn vị mã 16 bit, nó sẽ có một cặp thay thế và do đó sử dụng hai đơn vị mã (2 x 16-bit = 4 byte)

Xem bảng mã Unicode để biết các bảng mã khác nhau và phạm vi mã của chúng.


Những gì bạn nói về người thay thế dường như vi phạm đặc điểm tập lệnh ECMA. Như tôi đã nhận xét ở trên, thông số yêu cầu hai byte cho mỗi ký tự và việc cho phép các cặp thay thế sẽ vi phạm điều này.
whitneyland

Các công cụ Javascript ES5 được sử dụng nội bộ miễn phí USC-2 hoặc UTF-16, nhưng những gì nó thực sự đang sử dụng là loại UCS-2 với các đại diện. Đó là vì nó cho phép hiển thị các nửa thay thế dưới dạng các ký tự riêng biệt, các số nguyên không dấu UTF-16 duy nhất. Nếu bạn sử dụng một ký tự unicode trong mã nguồn của mình mà cần nhiều hơn một đơn vị mã 16 bit duy nhất để được đại diện, một cặp thay thế sẽ được sử dụng. Hành vi này là không vi phạm với các thông số kỹ thuật, xem chương 6 nguồn văn bản: ecma-international.org/ecma-262/5.1
holmberd

2

Câu trả lời từ Lauri Oherd hoạt động tốt đối với hầu hết các chuỗi được thấy trong tự nhiên, nhưng sẽ không thành công nếu chuỗi chứa các ký tự đơn lẻ trong phạm vi cặp thay thế, 0xD800 đến 0xDFFF. Ví dụ

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

Hàm dài hơn này sẽ xử lý tất cả các chuỗi:

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

Ví dụ

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

Nó sẽ tính toán chính xác kích thước cho các chuỗi chứa các cặp thay thế:

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

Kết quả có thể được so sánh với chức năng tích hợp của Node Buffer.byteLength:

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

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

1

Tôi đang làm việc với phiên bản nhúng của Động cơ V8. Tôi đã thử nghiệm một chuỗi đơn. Đẩy mỗi bước 1000 ký tự. UTF-8.

Thử nghiệm đầu tiên với byte đơn (8bit, ANSI) Ký tự "A" (hex: 41). Thử nghiệm thứ hai với hai ký tự byte (16bit) "Ω" (hex: CE A9) và thử nghiệm thứ ba với ký tự ba byte (24bit) "☺" (hex: E2 98 BA).

Trong cả ba trường hợp, thiết bị sẽ in ra khỏi bộ nhớ ở 888 000 ký tự và sử dụng ca. 26 348 kb trong RAM.

Kết quả: Các ký tự không được lưu trữ động. Và không chỉ với 16bit. - Ok, có lẽ chỉ đối với trường hợp của tôi (Thiết bị RAM nhúng 128 MB, V8 Engine C ++ / QT) - Việc mã hóa ký tự không liên quan gì đến kích thước ram của công cụ javascript. Ví dụ: mã hóaURI, v.v. chỉ hữu ích cho việc truyền và lưu trữ dữ liệu cấp cao.

Có nhúng hay không, thực tế là các ký tự không chỉ được lưu trữ trong 16bit. Thật không may, tôi không có câu trả lời 100%, Javascript làm gì ở khu vực cấp thấp. Btw. Tôi đã thử nghiệm tương tự (thử nghiệm đầu tiên ở trên) với một mảng ký tự "A". Đẩy 1000 mặt hàng mỗi bước. (Chính xác là cùng một bài kiểm tra. Chỉ cần thay thế chuỗi thành mảng) Và hệ thống sẽ đưa ra khỏi bộ nhớ (muốn) sau 10 416 KB sử dụng và độ dài mảng là 1 337 000. Vì vậy, công cụ javascript không đơn giản bị hạn chế. Nó là một loại phức tạp hơn.


0

Bạn có thể thử điều này:

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

Nó đã làm việc cho tôi.


1
Chắc chắn điều này giả định rằng tất cả các ký tự là tối đa 2 byte? Nếu có 3 hoặc 4 ký tự byte (có thể có trong UTF-8) thì hàm này sẽ chỉ tính chúng là ký tự 2 byte?
Adam Burley
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.