Berapa byte dalam string JavaScript?

97

Saya memiliki string javascript sekitar 500K saat dikirim dari server dalam UTF-8. Bagaimana cara mengetahui ukurannya di JavaScript?

Saya tahu bahwa JavaScript menggunakan UCS-2, jadi apakah itu berarti 2 byte per karakter. Namun, apakah itu bergantung pada implementasi JavaScript? Atau pada pengkodean halaman atau mungkin tipe konten?

Paul Biggar
sumber
Approx. jawabannya adalah panjang * karakter, jadi tebakan Anda mendekati.
glasnt
1
JavaScript modern, misalnya ES6, tidak hanya menggunakan UCS-2, lebih detail di sini: stackoverflow.com/a/46735247/700206
whitneyland

Jawaban:

36

StringNilai tidak bergantung pada implementasi, menurut Spesifikasi ECMA-262 Edisi ke-3 , setiap karakter mewakili satu unit 16-bit teks UTF-16 :

4.3.16 Nilai String

Nilai string adalah anggota dari tipe String dan merupakan urutan berurutan terbatas dari nol atau lebih nilai integer 16-bit unsigned.

CATATAN Meskipun setiap nilai biasanya mewakili satu unit 16-bit teks UTF-16, bahasa tersebut tidak menempatkan batasan atau persyaratan apa pun pada nilai kecuali bahwa mereka adalah bilangan bulat 16-bit unsigned.

Christian C. Salvadó
sumber
8
Bacaan saya atas bagian itu tidak menyiratkan independensi implementasi.
Paul Biggar
4
UTF-16 tidak dijamin, hanya fakta string yang disimpan sebagai int 16-bit.
bjornl
Ini hanya bergantung pada implementasi terkait dengan UTF-16. Deskripsi karakter 16-bit bersifat universal.
Panzercrisis
1
Saya pikir secara internal Firefox bahkan dapat menggunakan 1 byte per karakter untuk beberapa string .... blog.mozilla.org/javascript/2014/07/21/…
Michal Charemza
1
UTF-16 secara eksplisit tidak diizinkan menurut cara saya membacanya. Karakter UTF-16 dapat memiliki hingga 4 byte, tetapi spesifikasi mengatakan "nilai harus berupa bilangan bulat 16-bit unsigned". Ini berarti nilai string JavaScript adalah bagian dari UTF-16, namun, string UTF-16 apa pun yang menggunakan karakter 3 atau 4 byte tidak akan diizinkan.
whitneyland
71

Fungsi ini akan mengembalikan ukuran byte dari setiap string UTF-8 yang Anda berikan padanya.

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

Sumber

Mesin JavaScript bebas menggunakan UCS-2 atau UTF-16 secara internal. Sebagian besar mesin yang saya tahu menggunakan UTF-16, tetapi apa pun pilihan yang mereka buat, itu hanya detail implementasi yang tidak akan memengaruhi karakteristik bahasa.

Bahasa ECMAScript / JavaScript itu sendiri, bagaimanapun, memperlihatkan karakter sesuai dengan UCS-2, bukan UTF-16.

Sumber

Lauri Oherd
sumber
9
Gunakan .split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./)sebagai gantinya. Cuplikan Anda gagal untuk string yang dienkode menjadi "% uXXXX".
Rob W
Digunakan untuk penghitungan ukuran pada bingkai websocket, memberikan ukuran yang sama untuk bingkai String seperti alat dev chrome.
pengguna85155
2
Digunakan untuk string javascript yang diunggah ke s3, s3 menampilkan ukuran yang persis sama [(byteCount (s)) / 1024) .toFixed (2) + "KiB"]
user85155
42

Anda dapat menggunakan Blob untuk mendapatkan ukuran string dalam byte.

Contoh:

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

P. Roitto
sumber
2
Terima kasih Tuhan untuk gumpalan! Ini mungkin jawaban yang diterima untuk browser modern.
prasanthv
bagaimana cara mengimpor Blob di Node.js?
Alexander Mills
4
Ahh, dengan Node.js kami menggunakan Buffer, misalnyaBuffer.from('😂').length
Alexander Mills
19

Coba kombinasi ini dengan menggunakan fungsi unescape js:

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

Contoh proses encode penuh:

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
Kinjeiro
sumber
4
Fungsi unescapeJavaScript tidak digunakan lagi dan tidak boleh digunakan untuk mendekode Uniform Resource Identifiers (URI). Sumber
Lauri Oherd
@LauriOherd Saya tahu komentarnya sudah lama, tetapi: Dalam jawaban ini, unescapetidak digunakan, untuk memecahkan kode URI. Ini digunakan untuk mengubah %xxurutan menjadi karakter tunggal. Saat encodeURIComponentmengenkode string sebagai UTF-8, mewakili unit kode baik sebagai karakter ASCII yang sesuai atau sebagai %xxurutan, memanggil unescape(encodeURIComponent(...))hasil dalam string biner yang berisi representasi UTF-8 dari string asli. Memanggil .lengthdengan benar memberikan ukuran dalam byte dari string yang dikodekan sebagai UTF-8.
TS
Dan yes ( un) escapetidak digunakan lagi sejak 1999 tetapi masih tersedia di setiap browser ... - Artinya, ada alasan bagus untuk menghentikannya. Pada dasarnya tidak ada cara, untuk menggunakannya dengan benar (kecuali untuk en- / decoding UTF8 dalam kombinasi dengan en- / decodeURI( Component) - atau setidaknya saya tidak tahu aplikasi lain yang berguna untuk ( un) escape). Dan hari ini ada alternatif yang lebih baik untuk menyandikan / mendekode UTF8 ( TextEncoder, dll.)
TS
10

Perhatikan bahwa jika Anda menargetkan node.js, Anda dapat menggunakan Buffer.from(string).length:

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

UTF-8 mengkodekan karakter menggunakan 1 hingga 4 byte per titik kode. Seperti yang ditunjukkan CMS dalam jawaban yang diterima, JavaScript akan menyimpan setiap karakter secara internal menggunakan 16 bit (2 byte).

Jika Anda mengurai setiap karakter dalam string melalui loop dan menghitung jumlah byte yang digunakan per titik kode, lalu mengalikan jumlah total dengan 2, Anda harus memiliki penggunaan memori JavaScript dalam byte untuk string berenkode UTF-8 tersebut. Mungkin sesuatu seperti ini:

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

Contoh:

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

Ini adalah 3 cara saya menggunakan:

  1. TextEncoder ()

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

  2. Gumpal

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

  3. Penyangga

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

Hong Ly
sumber
5

Ukuran string JavaScript adalah

  • Pra-ES6 : 2 byte per karakter
  • ES6 dan yang lebih baru: 2 byte per karakter, atau 5 atau lebih byte per karakter

Pre-ES6
Selalu 2 byte per karakter. UTF-16 tidak diizinkan karena spesifikasi mengatakan "nilai harus berupa bilangan bulat 16-bit unsigned". Karena string UTF-16 dapat menggunakan 3 atau 4 karakter byte, itu akan melanggar persyaratan 2 byte. Yang terpenting, meskipun UTF-16 tidak dapat sepenuhnya didukung, standarnya mengharuskan dua karakter byte yang digunakan adalah karakter UTF-16 yang valid. Dengan kata lain, string JavaScript Pre-ES6 mendukung subset karakter UTF-16.

ES6 dan yang lebih baru
2 byte per karakter, atau 5 atau lebih byte per karakter. Ukuran tambahan mulai berlaku karena ES6 (ECMAScript 6) menambahkan dukungan untuk pelolosan titik kode Unicode . Menggunakan pelolosan unicode terlihat seperti ini: \ u {1D306}

Catatan praktis

  • Ini tidak terkait dengan implementasi internal mesin tertentu. Misalnya, beberapa mesin menggunakan struktur dan pustaka data dengan dukungan UTF-16 penuh, tetapi apa yang mereka sediakan secara eksternal tidak harus dukungan penuh UTF-16. Selain itu, mesin juga dapat memberikan dukungan UTF-16 eksternal tetapi tidak diwajibkan untuk melakukannya.

  • Untuk ES6, praktis berbicara karakter tidak akan pernah lebih dari 5 byte (2 byte untuk titik keluar + 3 byte untuk titik kode Unicode) karena versi terbaru Unicode hanya memiliki 136.755 karakter yang mungkin, yang dengan mudah dapat dimasukkan ke dalam 3 byte. Namun ini secara teknis tidak dibatasi oleh standar sehingga pada prinsipnya satu karakter dapat digunakan katakanlah, 4 byte untuk titik kode dan total 6 byte.

  • Sebagian besar contoh kode di sini untuk menghitung ukuran byte tampaknya tidak memperhitungkan pelolosan titik kode Unicode ES6, sehingga hasilnya mungkin salah dalam beberapa kasus.

whitneyland
sumber
1
Hanya bertanya-tanya, jika ukurannya 2 byte per karakter, mengapa Buffer.from('test').lengthdan Buffer.byteLength('test')sama dengan 4 (dalam Node) dan new Blob(['test']).sizejuga sama dengan 4?
pengguna1063287
Pra-ES6: UTF-16 diperbolehkan: Lihat ECMA-262 edisi ke-3 (dari 1999) : Halaman yang mengatakan UCS2 atau UTF-16 diperbolehkan. Halaman 5, definisi nilai string: "... Meskipun setiap nilai biasanya mewakili satu unit 16-bit teks UTF-16, ...". Di halaman 81 adalah tabel, yang menunjukkan bagaimana pasangan pengganti yang cocok harus dikodekan sebagai empat byte UTF-8.
TS
"per karakter" - Jika yang Anda maksud, per "karakter yang dirasakan pengguna" ( spesifikasi , penjelasan yang lebih sederhana ) dapat berupa sejumlah unit kode 16bit. Jika yang Anda maksud adalah per "titik kode ", itu bisa berupa satu atau dua unit kode 16bit dalam UTF-16 . (Tidak boleh 2,5 unit kode (atau bagaimana Anda mendapatkan 5 byte?))
TS
Apakah setiap elemen dalam string javascript ( nilai integer 16-bit unsigned ("elemen") ) sebenarnya secara internal diwakili oleh dua byte tidak ditentukan dalam standar. (Dan bagaimana bisa - Selama antarmuka yang disediakan untuk program javascript mengikuti standar, semuanya berfungsi sebagaimana mestinya.) Mozilla misalnya dapat menggunakan hanya satu byte per titik kode jika string hanya berisi latin1
TS
Pelolosan titik kode unicode tidak ada hubungannya dengan panjang string - ini hanya cara baru untuk merepresentasikan string dalam kode sumber. ( '\u{1F600}'.length===2, '\u{1F600}'==='\uD83D\uDE00', '\u{1F600}'==='😀')
TS
3

Satu elemen dalam JavaScript String dianggap sebagai satu unit kode UTF-16. Artinya, karakter Strings disimpan dalam 16-bit (1 unit kode), dan 16-bit sama dengan 2 byte (8-bit = 1 byte).

Itu charCodeAt() metode dapat digunakan untuk mengembalikan integer antara 0 dan 65535 mewakili unit kode UTF-16 di indeks yang diberikan.

Itu codePointAt() dapat digunakan untuk mengembalikan seluruh nilai titik kode untuk karakter Unicode, misalnya UTF-32.

Ketika karakter UTF-16 tidak dapat direpresentasikan dalam satu unit kode 16-bit, itu akan memiliki pasangan pengganti dan oleh karena itu menggunakan dua unit kode (2 x 16-bit = 4 byte)

Lihat pengkodean Unicode untuk berbagai pengkodean dan rentang kodenya.

holmberd.dll
sumber
Apa yang Anda katakan tentang pengganti tampaknya melanggar spesifikasi skrip ECMA. Seperti yang saya komentari di atas, spesifikasi membutuhkan dua byte per karakter, dan mengizinkan pasangan pengganti akan melanggar ini.
whitneyland
Mesin Javascript ES5 secara internal bebas untuk menggunakan USC-2 atau UTF-16, tetapi yang sebenarnya digunakan adalah semacam UCS-2 dengan pengganti. Itu karena memungkinkan mengekspos bagian pengganti sebagai karakter terpisah, satu bilangan bulat UTF-16 unsigned. Jika Anda menggunakan karakter unicode dalam kode sumber Anda yang membutuhkan lebih dari satu unit kode 16-bit untuk diwakili, pasangan pengganti akan digunakan. Perilaku ini tidak melanggar spesifikasi, lihat bab 6 teks sumber: ecma-international.org/ecma-262/5.1
holmberd
2

Jawaban dari Lauri Oherd berfungsi dengan baik untuk sebagian besar string yang terlihat di alam liar, tetapi akan gagal jika string berisi karakter tunggal dalam rentang pasangan pengganti, 0xD800 hingga 0xDFFF. Misalnya

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

Fungsi yang lebih panjang ini harus menangani semua string:

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

Misalnya

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

Ini akan menghitung dengan benar ukuran string yang berisi pasangan pengganti:

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

Hasilnya dapat dibandingkan dengan fungsi bawaan Node Buffer.byteLength:

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

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

Saya bekerja dengan versi tertanam dari Mesin V8. Saya telah menguji satu string. Mendorong setiap langkah 1000 karakter. UTF-8.

Tes pertama dengan byte tunggal (8bit, ANSI) Karakter "A" (hex: 41). Tes kedua dengan karakter dua byte (16bit) "Ω" (hex: CE A9) dan tes ketiga dengan karakter tiga byte (24bit) "☺" (hex: E2 98 BA).

Dalam ketiga kasus, perangkat mencetak keluar memori pada 888.000 karakter dan menggunakan ca. 26 348 kb dalam RAM.

Hasil: Karakter tidak disimpan secara dinamis. Dan tidak hanya dengan 16bit. - Ok, mungkin hanya untuk kasus saya (Perangkat RAM 128 MB yang Disematkan, V8 Engine C ++ / QT) - Pengkodean karakter tidak ada hubungannya dengan ukuran dalam ram mesin javascript. Misalnya encodingURI, dll. Hanya berguna untuk transmisi dan penyimpanan data tingkat tinggi.

Disematkan atau tidak, faktanya karakter tidak hanya disimpan dalam 16bit. Sayangnya saya tidak punya jawaban 100%, apa yang Javascript lakukan di area level rendah. Btw. Saya telah menguji yang sama (tes pertama di atas) dengan array karakter "A". Mendorong 1000 item setiap langkah. (Tes yang persis sama. Baru saja mengganti string ke array) Dan sistem mengeluarkan memori (diinginkan) setelah 10 416 KB menggunakan dan panjang array 1 337.000. Jadi, mesin javascript tidak mudah dibatasi. Ini lebih kompleks.

Dominik
sumber
0

Anda dapat mencoba ini:

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

Itu berhasil untuk saya.

pengguna3728331
sumber
1
Tentunya ini mengasumsikan bahwa semua karakter maksimal 2 byte? Jika ada 3 atau 4 karakter byte (yang dimungkinkan dalam UTF-8) maka fungsi ini hanya akan menghitungnya sebagai karakter 2-byte?
Adam Burley