Quanti byte in una stringa JavaScript?


97

Ho una stringa javascript che è di circa 500K quando viene inviata dal server in UTF-8. Come posso dire la sua dimensione in JavaScript?

So che JavaScript utilizza UCS-2, quindi significa 2 byte per carattere. Tuttavia, dipende dall'implementazione di JavaScript? O sulla codifica della pagina o forse sul tipo di contenuto?


Circa. la risposta sarebbe length * charsize, quindi la tua ipotesi è vicina.
glasnt

1
Moderna JavaScript, ad esempio ES6, non solo utilizzare UCS-2, più in dettaglio qui: stackoverflow.com/a/46735247/700206
whitneyland

Risposte:


36

Stringi valori non dipendono dall'implementazione, secondo la specifica ECMA-262 3a edizione , ogni carattere rappresenta una singola unità a 16 bit di testo UTF-16 :

4.3.16 Valore stringa

Un valore stringa è un membro del tipo String ed è una sequenza ordinata finita di zero o più valori interi senza segno a 16 bit.

NOTA Sebbene ogni valore in genere rappresenti una singola unità di testo UTF-16 a 16 bit, la lingua non pone alcuna restrizione o requisito sui valori tranne che sono numeri interi senza segno a 16 bit.


8
La mia lettura di quel passaggio non implica l'indipendenza dall'attuazione.
Paul Biggar,

4
UTF-16 non è garantito, solo il fatto delle stringhe memorizzate come int a 16 bit.
bjornl

Dipende solo dall'implementazione per quanto riguarda UTF-16. La descrizione dei caratteri a 16 bit è universale.
Panzercrisis

1
Penso che internamente Firefox potrebbe anche utilizzare 1 byte per carattere per alcune stringhe .... blog.mozilla.org/javascript/2014/07/21/…
Michal Charemza

1
UTF-16 non è esplicitamente consentito nel modo in cui lo sto leggendo. I caratteri UTF-16 possono contenere fino a 4 byte, ma la specifica dice "i valori devono essere interi senza segno a 16 bit". Ciò significa che i valori delle stringhe JavaScript sono un sottoinsieme di UTF-16, tuttavia, qualsiasi stringa UTF-16 che utilizza caratteri da 3 o 4 byte non sarebbe consentita.
Whitneyland

71

Questa funzione restituirà la dimensione in byte di qualsiasi stringa UTF-8 che le passi.

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

fonte

I motori JavaScript sono liberi di utilizzare internamente UCS-2 o UTF-16. La maggior parte dei motori che conosco utilizza UTF-16, ma qualunque scelta abbiano fatto, è solo un dettaglio di implementazione che non influirà sulle caratteristiche del linguaggio.

Il linguaggio ECMAScript / JavaScript stesso, tuttavia, espone i caratteri secondo UCS-2, non UTF-16.

fonte


9
Usa .split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./)invece. Lo snippet non riesce per le stringhe che codificano in "% uXXXX".
Rob W

Utilizzato per il calcolo delle dimensioni sui frame websocket, fornisce le stesse dimensioni per un frame String degli strumenti di sviluppo di Chrome.
user85155

2
Utilizzato per le stringhe javascript caricate su s3, s3 mostra esattamente la stessa dimensione [(byteCount (s)) / 1024) .toFixed (2) + "KiB"]
user85155


42

È possibile utilizzare il BLOB per ottenere la dimensione della stringa in byte.

Esempi:

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
Grazie a dio per i blob! Questa dovrebbe probabilmente essere la risposta accettata per i browser moderni.
prasanthv

come importare Blob in Node.js?
Alexander Mills

4
Ahh, con Node.js usiamo Buffer, ad esempioBuffer.from('😂').length
Alexander Mills

19

Prova questa combinazione con l'utilizzo della funzione js di unescape :

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

Esempio di processo di codifica completo:

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
La unescapefunzione JavaScript è deprecata e non deve essere utilizzata per decodificare URI (Uniform Resource Identifiers). Fonte
Lauri Oherd

@LauriOherd So che il commento è vecchio, ma: in questa risposta, unescapenon viene utilizzato per decodificare gli URI. Viene utilizzato per convertire le %xxsequenze in singoli caratteri. Poiché encodeURIComponentcodifica una stringa come UTF-8, che rappresenta le unità di codice come carattere ASCII corrispondente o come %xxsequenza, la chiamata unescape(encodeURIComponent(...))risulta in una stringa binaria contenente la rappresentazione UTF-8 della stringa originale. La chiamata .lengthcorretta fornisce la dimensione in byte della stringa codificata come UTF-8.
TS

E yes ( un) escapeè deprecato dal 1999 ma è ancora disponibile in tutti i browser ... - Detto questo, ci sono buone ragioni per deprecarlo. Fondamentalmente non c'è modo di usarli correttamente (eccetto per en- / decoding UTF8 in combinazione con en- / decodeURI( Component) - o almeno non conosco altre applicazioni utili per ( un) escape). E oggi ci sono alternative migliori per codificare / decodificare UTF8 ( TextEncoder, ecc.)
TS

10

Tieni presente che se scegli come target node.js puoi utilizzare Buffer.from(string).length:

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

7

UTF-8 codifica i caratteri utilizzando da 1 a 4 byte per punto di codice. Come CMS ha sottolineato nella risposta accettata, JavaScript memorizzerà ogni carattere internamente utilizzando 16 bit (2 byte).

Se analizzi ogni carattere nella stringa tramite un ciclo e conti il ​​numero di byte utilizzati per punto di codice, quindi moltiplichi il conteggio totale per 2, dovresti avere l'utilizzo della memoria di JavaScript in byte per quella stringa con codifica UTF-8. Forse qualcosa del genere:

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

Esempi:

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

7

Questi sono 3 modi in cui uso:

  1. TextEncoder ()

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

  2. Blob

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

  3. Buffer

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


5

La dimensione di una stringa JavaScript è

  • Pre-ES6 : 2 byte per carattere
  • ES6 e versioni successive: 2 byte per carattere o 5 o più byte per carattere

Pre-ES6
Sempre 2 byte per carattere. UTF-16 non è consentito perché la specifica dice "i valori devono essere interi senza segno a 16 bit". Poiché le stringhe UTF-16 possono utilizzare caratteri a 3 o 4 byte, violerebbe il requisito di 2 byte. Fondamentalmente, mentre UTF-16 non può essere completamente supportato, lo standard richiede che i due caratteri byte utilizzati siano caratteri UTF-16 validi. In altre parole, le stringhe JavaScript pre-ES6 supportano un sottoinsieme di caratteri UTF-16.

ES6 e versioni successive
2 byte per carattere o 5 o più byte per carattere. Le dimensioni aggiuntive entrano in gioco perché ES6 (ECMAScript 6) aggiunge il supporto per gli escape del punto di codice Unicode . L'utilizzo di un'escape Unicode ha questo aspetto: \ u {1D306}

Note pratiche

  • Questo non si riferisce all'implementazione interna di un particolare motore. Ad esempio, alcuni motori utilizzano strutture di dati e librerie con supporto completo UTF-16, ma ciò che forniscono esternamente non deve essere necessariamente un supporto completo UTF-16. Inoltre un motore può fornire anche il supporto UTF-16 esterno, ma non è obbligato a farlo.

  • Per ES6, praticamente i caratteri non saranno mai più lunghi di 5 byte (2 byte per il punto di fuga + 3 byte per il punto di codice Unicode) perché l'ultima versione di Unicode ha solo 136.755 caratteri possibili, che si adattano facilmente a 3 byte. Tuttavia questo tecnicamente non è limitato dallo standard, quindi in linea di principio un singolo carattere potrebbe utilizzare, ad esempio, 4 byte per il punto di codice e 6 byte in totale.

  • La maggior parte degli esempi di codice qui per il calcolo della dimensione in byte non sembrano prendere in considerazione gli escape del punto di codice Unicode ES6, quindi i risultati potrebbero essere errati in alcuni casi.


1
Mi chiedo solo, se la dimensione è di 2 byte per carattere, perché Buffer.from('test').lengthe Buffer.byteLength('test')uguale a 4 (in nodo) e new Blob(['test']).sizeanche uguale a 4?
user1063287

Pre-ES6: UTF-16 è consentito: vedere ECMA-262 3a edizione (dal 1999) : la prima pagina dice che UCS2 o UTF-16 è consentito. Pagina 5, definizione del valore di stringa: "... Sebbene ogni valore di solito rappresenti una singola unità di 16 bit di testo UTF-16, ...". A pagina 81 c'è una tabella che mostra come le coppie surrogate corrispondenti devono essere codificate come quattro byte UTF-8.
TS

"per carattere" - Se con questo intendi per "carattere percepito dall'utente" ( specifica , spiegazione più semplice ) potrebbe essere un numero qualsiasi di unità di codice a 16 bit. Se intendevi per "codepoint", può essere una o due unità di codice a 16 bit in UTF-16 . (Non possono essere 2,5 unità di codice (o come si ottengono 5 byte?))
TS

Se ogni elemento in una stringa javascript ( valori interi senza segno a 16 bit ("elementi") ) sia effettivamente rappresentato internamente da due byte non è definito nello standard. (E come potrebbe essere - Finché l'interfaccia fornita al programma javascript segue lo standard tutto funziona come previsto.) Mozilla, ad esempio, può usare solo un byte per codepoint se la stringa contiene solo latin1
TS

Gli escape del punto di codice Unicode non hanno nulla a che fare con la lunghezza della stringa: è solo un nuovo modo di rappresentare le stringhe nel codice sorgente. ( '\u{1F600}'.length===2, '\u{1F600}'==='\uD83D\uDE00', '\u{1F600}'==='😀')
TS

3

Un singolo elemento in una stringa JavaScript è considerato una singola unità di codice UTF-16. Vale a dire, i caratteri delle stringhe vengono memorizzati a 16 bit (1 unità di codice) e 16 bit è uguale a 2 byte (8 bit = 1 byte).

Il charCodeAt()metodo può essere utilizzato per restituire un numero intero compreso tra 0 e 65535 che rappresenta l'unità di codice UTF-16 all'indice specificato.

La codePointAt()può essere utilizzato per restituire l'intero punto di codifica per i caratteri Unicode, ad esempio UTF-32.

Quando un carattere UTF-16 non può essere rappresentato in una singola unità di codice a 16 bit, avrà una coppia surrogata e quindi utilizzerà due unità di codice (2 x 16 bit = 4 byte)

Consulta Codifiche Unicode per diverse codifiche e relativi intervalli di codice.


Quello che dici sui surrogati sembrerebbe violare le specifiche dello script ECMA. Come ho commentato sopra, la specifica richiede due byte per carattere e consentire le coppie surrogate violerebbe questo.
Whitneyland

I motori Javascript ES5 sono internamente liberi di utilizzare USC-2 o UTF-16, ma ciò che sta effettivamente utilizzando è una sorta di UCS-2 con surrogati. Questo perché consente di esporre le metà surrogate come caratteri separati, singoli interi senza segno UTF-16. Se si utilizza un carattere Unicode nel codice sorgente che necessita di più di una singola unità di codice a 16 bit per essere rappresentata, verrà utilizzata una coppia surrogata. Questo comportamento non è in violazione con le specifiche, vedere il capitolo 6 testo sorgente: ecma-international.org/ecma-262/5.1
holmberd

2

La risposta di Lauri Oherd funziona bene per la maggior parte delle stringhe viste in natura, ma fallirà se la stringa contiene caratteri solitari nell'intervallo di coppie surrogate, da 0xD800 a 0xDFFF. Per esempio

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

Questa funzione più lunga dovrebbe gestire tutte le stringhe:

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

Per esempio

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

Calcolerà correttamente la dimensione per le stringhe contenenti coppie surrogate:

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

I risultati possono essere confrontati con la funzione incorporata di Node Buffer.byteLength:

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

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

1

Sto lavorando con una versione incorporata del motore V8. Ho testato una singola stringa. Spingendo ogni passo 1000 caratteri. UTF-8.

Primo test con carattere "A" a byte singolo (8 bit, ANSI) (esadecimale: 41). Secondo test con carattere a due byte (16 bit) "Ω" (hex: CE A9) e terzo test con carattere a tre byte (24 bit) "☺" (hex: E2 98 BA).

In tutti e tre i casi il dispositivo stampa la memoria esaurita a 888 000 caratteri e utilizzando ca. 26348 kb di RAM.

Risultato: i caratteri non vengono archiviati dinamicamente. E non con solo 16 bit. - Ok, forse solo per il mio caso (Embedded 128 MB RAM Device, V8 Engine C ++ / QT) - La codifica dei caratteri non ha nulla a che fare con la dimensione in ram del motore javascript. Ad esempio encodingURI, ecc. È utile solo per la trasmissione e l'archiviazione di dati di alto livello.

Incorporato o no, il fatto è che i caratteri non sono solo memorizzati a 16 bit. Sfortunatamente non ho una risposta al 100%, cosa fa Javascript nell'area di basso livello. Btw. Ho provato lo stesso (primo test sopra) con un array di caratteri "A". Hai spinto 1000 elementi ogni passo. (Esattamente lo stesso test. Ho appena sostituito la stringa nell'array) E il sistema ha esaurito la memoria (richiesta) dopo 10 416 KB utilizzando e una lunghezza dell'array di 1 337 000. Quindi, il motore javascript non è limitato. È un tipo più complesso.


0

Puoi provare questo:

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

Ha funzionato per me.


1
Sicuramente questo presuppone che tutti i caratteri siano al massimo 2 byte? Se sono presenti caratteri a 3 o 4 byte (che sono possibili in UTF-8), questa funzione li conterà solo come caratteri a 2 byte?
Adam Burley,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.