Bir JavaScript dizesinde kaç bayt var?


97

UTF-8'de sunucudan gönderilirken yaklaşık 500K olan bir javascript dizgim var. Boyutunu JavaScript'te nasıl anlayabilirim?

JavaScript'in UCS-2 kullandığını biliyorum, bu da karakter başına 2 bayt anlamına geliyor. Ancak, JavaScript uygulamasına bağlı mı? Veya sayfa kodlamasında veya belki içerik türünde?


Yaklaşık. cevap uzunluk * karakter boyutu olacaktır, bu nedenle tahmininiz yakındır.
glasnt

1
Modern JavaScript, örneğin ES6, sadece UCS-2'yi kullanmakla kalmaz, daha fazla ayrıntı burada: stackoverflow.com/a/46735247/700206
whitneyland

Yanıtlar:


36

Stringdeğerler uygulamaya bağlı değildir, ECMA-262 3. Sürüm Spesifikasyonuna göre , her karakter tek bir 16 bit UTF-16 metnini temsil eder :

4.3.16 Dize Değeri

Bir dize değeri, String türünün bir üyesidir ve sıfır veya daha fazla 16 bitlik işaretsiz tamsayı değerinin sonlu sıralı bir dizisidir.

NOT Her değer genellikle 16 bitlik tek bir UTF-16 metnini temsil etse de, dil değerlere 16 bitlik işaretsiz tamsayılar dışında herhangi bir kısıtlama veya gereksinim getirmez.


8
Bu pasajı okumam, uygulama bağımsızlığı anlamına gelmez.
Paul Biggar

4
UTF-16 garanti edilmez, yalnızca 16 bitlik girişler olarak depolanan dizelerin gerçeği.
bjornl

UTF-16 ile ilgili olarak yalnızca uygulamaya bağlıdır. 16 bitlik karakter tanımı evrenseldir.
Panzercrisis

1
Sanırım dahili olarak Firefox bazı dizeler için karakter başına 1 bayt bile kullanabilir .... blog.mozilla.org/javascript/2014/07/21/…
Michal Charemza

1
UTF-16'yı okuduğum şekilde açıkça izin verilmiyor . UTF-16 karakterleri en fazla 4 bayta sahip olabilir, ancak belirtim "değerler 16 bitlik işaretsiz tam sayı olmalıdır" diyor. Bu, JavaScript dize değerlerinin UTF-16'nın bir alt kümesi olduğu anlamına gelir, ancak 3 veya 4 bayt karakter kullanan herhangi bir UTF-16 dizesine izin verilmez.
whitneyland

71

Bu işlev, kendisine ilettiğiniz herhangi bir UTF-8 dizesinin bayt boyutunu döndürür.

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

Kaynak

JavaScript motorları, dahili olarak UCS-2 veya UTF-16'yı kullanmakta özgürdür. Bildiğim çoğu motor UTF-16 kullanıyor, ancak seçim ne olursa olsun, dilin özelliklerini etkilemeyecek bir uygulama ayrıntısı.

ECMAScript / JavaScript dilinin kendisi ise karakterleri UTF-16'ya değil UCS-2'ye göre gösterir.

Kaynak


9
.split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./)Bunun yerine kullanın . Snippet'iniz, "% uXXXX" olarak kodlayan dizeler için başarısız oluyor.
Rob W

Websocket çerçevelerinde boyut hesaplaması için kullanılır, bir String çerçevesi için chrome dev araçlarıyla aynı boyutu verir.
user85155

2
S3'e yüklenen javascript dizeleri için kullanılır, s3 tam olarak aynı boyutta görüntüler [(byteCount (s)) / 1024) .toFixed (2) + "KiB"]
user85155


42

Dize boyutunu bayt cinsinden almak için Blob'u kullanabilirsiniz .

Örnekler:

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
Lekeler için Tanrıya şükür! Bu muhtemelen modern tarayıcılar için kabul edilen cevap olmalıdır.
prasanthv

Node.js'de Blob nasıl içe aktarılır?
Alexander Mills

4
Ahh, Node.js ile Buffer kullanıyoruz, örneğinBuffer.from('😂').length
Alexander Mills

19

Unescape js işlevini kullanarak bu kombinasyonu deneyin :

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

Tam kodlama işlemi örneği:

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 işlevi kullanımdan kaldırılmıştır ve Tekdüzen Kaynak Tanımlayıcılarını (URI) çözmek için kullanılmamalıdır. Kaynak
Lauri Oherd

@LauriOherd Yorumun eski olduğunu biliyorum, ancak: Bu cevapta URI'lerin kodunu çözmekunescape için kullanılmıyor . Dizileri tek karakterlere dönüştürmek için kullanılır . Olarak karşılık gelen ASCII karakter ya da bir şekilde ya codeunits temsil kodlar UTF-8 gibi bir dizi, çağrı dizisi, bir sonuçları ikili bir dizgi orijinal dize UTF-8 temsilini ihtiva etmektedir. Doğru arama , UTF-8 olarak kodlanmış dizenin bayt cinsinden boyutunu verir. %xxencodeURIComponent%xxunescape(encodeURIComponent(...)).length
TS

Ve evet ( un) escape1999'dan beri kullanımdan kaldırıldı, ancak yine de her tarayıcıda mevcut ... - Bununla birlikte, kullanımdan kaldırılması için iyi bir neden var. Temelde bunları doğru bir şekilde kullanmanın bir yolu yoktur (UTF8'i en- / decodeURI( Component) ile kombinasyon halinde kodlama / kod çözme dışında - veya en azından ( un) için başka yararlı bir uygulama bilmiyorum escape). Ve bugün UTF8'i ( TextEncodervb.) Kodlamak / çözmek için daha iyi alternatifler var
TS

10

Node.js'yi hedefliyorsanız şunları kullanabileceğinizi unutmayın Buffer.from(string).length:

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

7

UTF-8, kod noktası başına 1 ila 4 bayt kullanarak karakterleri kodlar. Kabul edilen yanıtta CMS'nin belirttiği gibi, JavaScript her bir karakteri 16 bit (2 bayt) kullanarak dahili olarak depolayacaktır.

Dizedeki her karakteri bir döngü aracılığıyla ayrıştırır ve kod noktası başına kullanılan bayt sayısını sayarsanız ve ardından toplam sayıyı 2 ile çarparsanız, JavaScript'in UTF-8 kodlu dizge için bayt cinsinden bellek kullanımına sahip olmanız gerekir. Belki bunun gibi bir şey:

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

Örnekler:

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

7

Bunlar kullandığım 3 yöntem:

  1. TextEncoder ()

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

  2. Blob

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

  3. Tampon

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


5

JavaScript dizesinin boyutu

  • ES6 öncesi : karakter başına 2 bayt
  • ES6 ve sonrası: Karakter başına 2 bayt veya karakter başına 5 veya daha fazla bayt

Pre-ES6
Her zaman karakter başına 2 bayt. Spesifikasyonda "değerler 16 bitlik işaretsiz tamsayılar olmalıdır" dediğinden UTF-16'ya izin verilmez. UTF-16 dizeleri 3 veya 4 baytlık karakterler kullanabildiğinden, 2 bayt gereksinimini ihlal eder. En önemlisi, UTF-16 tam olarak desteklenemezken, standart kullanılan iki bayt karakterin geçerli UTF-16 karakterleri olmasını gerektirir. Başka bir deyişle, Pre-ES6 JavaScript dizeleri UTF-16 karakterlerinin bir alt kümesini destekler.

ES6 ve sonrası
karakter başına 2 bayt veya karakter başına 5 veya daha fazla bayt. ES6 (ECMAScript 6) Unicode kod noktası çıkışları için destek eklediğinden ek boyutlar devreye girer . Unicode kaçışının kullanılması şuna benzer: \ u {1D306}

Pratik notlar

  • Bu, belirli bir motorun dahili uygulamasıyla ilgili değildir. Örneğin, bazı motorlar tam UTF-16 desteğine sahip veri yapıları ve kitaplıkları kullanır, ancak harici olarak sağladıkları şeyin tam UTF-16 desteği olması gerekmez. Ayrıca bir motor, harici UTF-16 desteği de sağlayabilir, ancak bunu yapmak zorunda değildir.

  • ES6 için, pratikte karakterler asla 5 bayttan uzun olmayacaktır (kaçış noktası için 2 bayt + Unicode kod noktası için 3 bayt) çünkü Unicode'un en son sürümü yalnızca 136.755 olası karaktere sahiptir ve bu da 3 bayta kolayca sığar. Bununla birlikte, bu teknik olarak standartla sınırlı değildir, bu nedenle prensipte tek bir karakter, örneğin kod noktası için 4 bayt ve toplam 6 bayt kullanabilir.

  • Bayt boyutunu hesaplamak için buradaki kod örneklerinin çoğu, ES6 Unicode kod noktası kaçışlarını hesaba katmıyor gibi görünüyor, bu nedenle sonuçlar bazı durumlarda yanlış olabilir.


1
Boyut karakter başına 2 bayt ise sadece niçin, merak Buffer.from('test').lengthve Buffer.byteLength('test')eşit 4 (Node) ve new Blob(['test']).sizeayrıca 4 eşittir?
user1063287

ES6 öncesi: UTF-16'ya izin verilir: Bkz. ECMA-262 3. baskı (1999'dan itibaren) : Birinci sayfada UCS2 veya UTF-16'ya izin verildiğini söylüyor. Sayfa 5, dize değerinin tanımı: "... Her değer genellikle 16 bitlik tek bir UTF-16 metni birimini temsil etse de, ...". 81. sayfada, eşleşen vekil çiftlerinin nasıl dört UTF-8 bayt olarak kodlanması gerektiğini gösteren bir tablodur.
TS

"karakter başına" - Bununla "kullanıcı tarafından algılanan karakter" ( spec , daha basit açıklama ) başına kastettiğinizde, herhangi bir sayıda 16 bit kod birimi olabilir. " Kod noktası" başına demek istediyseniz, UTF-16'da bir veya iki 16 bit kod birimi olabilir . (2,5 kod birimi olamaz (veya nasıl 5 bayt elde edersiniz?))
TS

Bir javascript dizesindeki her bir öğenin ( 16 bitlik işaretsiz tamsayı değerleri ("öğeler") ) aslında dahili olarak iki bayt ile temsil edilip edilmediği standartta tanımlanmamıştır. (Ve nasıl olabilir - JavaScript programına sağlanan arayüz standardı izlediği sürece, her şey amaçlandığı gibi çalışır.) Örneğin Mozilla , dizge yalnızca latin1
TS

Unicode kod noktası çıkışlarının dize uzunluğu ile ilgisi yoktur - bu, kaynak kodda dizeleri temsil etmenin yeni bir yoludur. ( '\u{1F600}'.length===2, '\u{1F600}'==='\uD83D\uDE00', '\u{1F600}'==='😀')
TS

3

Bir JavaScript Dizesindeki tek bir öğe, tek bir UTF-16 kod birimi olarak kabul edilir. Yani, Dizeler karakterleri 16 bit (1 kod birimi) olarak saklanır ve 16 bit 2 bayta eşittir (8 bit = 1 bayt).

charCodeAt()Yöntem, belirli bir dizin UTF-16 kod birimi temsil eden 0 ile 65535 arasında bir tamsayı dönmek için de kullanılabilir.

codePointAt()Örneğin UTF-32 Unicode karakterleri için bütün kod noktası değeri döndürmek için kullanılabilir.

Bir UTF-16 karakteri tek bir 16-bit kod biriminde temsil edilemediğinde, bir vekil çifti olacaktır ve bu nedenle iki kod birimi kullanacaktır (2 x 16-bit = 4 bayt)

Farklı kodlamalar ve bunların kod aralıkları için Unicode kodlamalarına bakın .


Vekiller hakkında söyledikleriniz, ECMA komut dosyası spesifikasyonunu ihlal ediyor gibi görünüyor. Yukarıda yorumladığım gibi, özellik karakter başına iki bayta ihtiyaç duyar ve vekil çiftlere izin vermek bunu ihlal eder.
whitneyland

Javascript ES5 motorları dahili olarak USC-2 veya UTF-16'yı kullanmakta özgürdür, ancak gerçekte kullandığı şey, vekiller ile bir tür UCS-2'dir. Bunun nedeni, yedek yarıları ayrı karakterler, tek UTF-16 işaretsiz tamsayılar olarak göstermeye izin vermesidir. Kaynak kodunuzda tek bir 16 bitlik kod biriminden fazlasının temsil edilmesi gereken bir unicode karakter kullanırsanız, bir vekil çifti kullanılacaktır. Bu davranış, bkz değil gözlük ile ihlal olduğu bölüm 6 kaynak metin: ecma-international.org/ecma-262/5.1
holmberd

2

Lauri Oherd'den gelen yanıt, vahşi ortamda görülen çoğu dizge için iyi çalışıyor, ancak dize, 0xD800 ila 0xDFFF vekil çift aralığında yalnız karakterler içeriyorsa başarısız olacaktır. Örneğin

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

Bu daha uzun işlev tüm dizeleri işlemelidir:

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

Örneğin

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

Vekil çiftleri içeren dizelerin boyutunu doğru bir şekilde hesaplayacaktır:

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

Sonuçlar Node'un yerleşik işlevi ile karşılaştırılabilir Buffer.byteLength:

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

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

1

V8 Motorunun yerleşik bir sürümüyle çalışıyorum. Tek bir dizeyi test ettim. Her adıma 1000 karakter basmak. UTF-8.

Tek baytlı (8 bit, ANSI) Karakter "A" (onaltılık: 41) ile ilk test. İki bayt karakterli (16 bit) "Ω" (onaltılık: CE A9) ikinci test ve üç bayt karakterli (24 bit) "☺" (onaltılık: E2 98 BA) üçüncü test.

Her üç durumda da aygıt 888 000 karakter ve ca. RAM'de 26 348 kb.

Sonuç: Karakterler dinamik olarak depolanmaz. Ve sadece 16bit ile değil. - Tamam, belki sadece benim durumum için (Gömülü 128 MB RAM Aygıtı, V8 Motoru C ++ / QT) - Karakter kodlamanın javascript motorunun ram boyutuyla hiçbir ilgisi yoktur. Örneğin, encodingURI, vb. Yalnızca yüksek düzeyli veri iletimi ve depolaması için kullanışlıdır.

Gömülü olsun ya da olmasın, gerçek şu ki, karakterler sadece 16bit'te saklanmaz. Ne yazık ki Javascript düşük seviyeli alanda ne yapıyor,% 100 cevabım yok. Btw. Aynı şeyi (yukarıdaki ilk test) bir dizi "A" karakteriyle test ettim. Her adımda 1000 öğe itildi. (Tam olarak aynı test. Sadece diziye dizge değiştirildi) Ve sistem 10 416 KB kullanım ve dizi uzunluğu 1 337 000'den sonra bellekten (isteniyor) çıktı. Yani, javascript motoru basit bir şekilde kısıtlanmış değil. Bu biraz daha karmaşık.


0

Bunu deneyebilirsiniz:

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

Benim için çalıştı.


1
Elbette bu, tüm karakterlerin maksimum 2 bayt olduğunu varsayar? 3 veya 4 baytlık karakterler varsa (bunlar UTF-8'de mümkündür), o zaman bu işlev bunları yalnızca 2 baytlık karakterler olarak sayar mı?
Adam Burley
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.