Wie viele Bytes enthält eine JavaScript-Zeichenfolge?

90

Ich habe eine Javascript-Zeichenfolge, die ungefähr 500 KB groß ist, wenn sie vom Server in UTF-8 gesendet wird. Wie kann ich die Größe in JavaScript bestimmen?

Ich weiß, dass JavaScript UCS-2 verwendet, das bedeutet also 2 Bytes pro Zeichen. Hängt es jedoch von der JavaScript-Implementierung ab? Oder auf der Seite Kodierung oder vielleicht Inhaltstyp?

Paul Biggar
quelle
Ca. Die Antwort wäre Länge * charsize, also ist Ihre Vermutung nah.
Glasnt
1
Modernes JavaScript, zum Beispiel ES6, verwendet nicht nur UCS-2, mehr Details hier: stackoverflow.com/a/46735247/700206
Whitneyland

Antworten:

34

StringDie Werte sind nicht implementierungsabhängig. Gemäß der ECMA-262 3rd Edition-Spezifikation repräsentiert jedes Zeichen eine einzelne 16-Bit-Einheit des UTF-16-Textes :

4.3.16 Zeichenfolgenwert

Ein Zeichenfolgenwert ist ein Mitglied vom Typ Zeichenfolge und eine endlich geordnete Folge von null oder mehr 16-Bit-Ganzzahlwerten ohne Vorzeichen.

HINWEIS Obwohl jeder Wert normalerweise eine einzelne 16-Bit-Einheit von UTF-16-Text darstellt, stellt die Sprache keine Einschränkungen oder Anforderungen an die Werte, außer dass es sich um 16-Bit-Ganzzahlen ohne Vorzeichen handelt.

CMS
quelle
7
Meine Lektüre dieser Passage impliziert keine Unabhängigkeit bei der Umsetzung.
Paul Biggar
4
UTF-16 ist nicht garantiert, nur die Tatsache, dass die Zeichenfolgen als 16-Bit-Ints gespeichert sind.
Björnl
Es ist nur in Bezug auf UTF-16 implementierungsabhängig. Die 16-Bit-Zeichenbeschreibung ist universell.
Panzercrisis
1
Ich denke, intern könnte Firefox sogar 1 Byte pro Zeichen für einige Zeichenfolgen verwenden .... blog.mozilla.org/javascript/2014/07/21/…
Michal Charemza
1
UTF-16 ist ausdrücklich nicht so erlaubt, wie ich es lese. UTF-16-Zeichen können bis zu 4 Byte enthalten, die Spezifikation besagt jedoch, dass "Werte 16-Bit-Ganzzahlen ohne Vorzeichen sein müssen". Dies bedeutet, dass JavaScript-Zeichenfolgenwerte eine Teilmenge von UTF-16 sind. Eine UTF-16-Zeichenfolge mit 3 oder 4 Byte Zeichen ist jedoch nicht zulässig.
Whitneyland
70

Diese Funktion gibt die Bytegröße aller UTF-8-Zeichenfolgen zurück, die Sie an sie übergeben.

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

Quelle

JavaScript-Engines können UCS-2 oder UTF-16 intern verwenden. Die meisten mir bekannten Engines verwenden UTF-16, aber was auch immer sie gewählt haben, es ist nur ein Implementierungsdetail, das die Eigenschaften der Sprache nicht beeinflusst.

Die ECMAScript / JavaScript-Sprache selbst macht jedoch Zeichen gemäß UCS-2 und nicht UTF-16 verfügbar.

Quelle

Lauri Oherd
quelle
8
Verwenden Sie .split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./)stattdessen. Ihr Snippet schlägt für Zeichenfolgen fehl, die in "% uXXXX" codiert sind.
Rob W
Wird für die Größenberechnung von Websocket-Frames verwendet und ergibt für einen String-Frame dieselbe Größe wie für Chrome-Entwicklungswerkzeuge.
Benutzer85155
2
S3 wird für Javascript-Zeichenfolgen verwendet, die auf s3 hochgeladen wurden. Es zeigt genau dieselbe Größe an [(byteCount (s)) / 1024) .toFixed (2) + "KiB"]
user85155
39

Sie können den Blob verwenden , um die Zeichenfolgengröße in Bytes abzurufen.

Beispiele:

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
quelle
2
Gott sei Dank für Blobs! Dies sollte wahrscheinlich die akzeptierte Antwort für moderne Browser sein.
Prasanthv
Wie importiere ich Blob in Node.js?
Alexander Mills
3
Ahh, mit Node.js verwenden wir zum Beispiel BufferBuffer.from('😂').length
Alexander Mills
19

Versuchen Sie diese Kombination mit der Funktion unescape js:

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

Beispiel für einen vollständigen Codierungsprozess:

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
quelle
4
Die unescapeJavaScript-Funktion ist veraltet und sollte nicht zum Dekodieren von URI (Uniform Resource Identifiers) verwendet werden. Quelle
Lauri Oherd
@LauriOherd Ich weiß, der Kommentar ist alt, aber: In dieser Antwort unescapewird nicht verwendet, um URIs zu dekodieren . Es wird verwendet, um %xxSequenzen in einzelne Zeichen umzuwandeln . Wenn encodeURIComponenteine Zeichenfolge als UTF-8 codiert wird, die Codeeinheiten entweder als entsprechendes ASCII-Zeichen oder als %xxSequenz darstellt, führt der Aufruf unescape(encodeURIComponent(...))zu einer binären Zeichenfolge, die die UTF-8-Darstellung der ursprünglichen Zeichenfolge enthält. Beim .lengthkorrekten Aufrufen wird die Größe der als UTF-8 codierten Zeichenfolge in Byte angegeben.
TS
Und ja ( un) escapeist seit 1999 veraltet, aber es ist immer noch in jedem Browser verfügbar ... - Das heißt, es gibt gute Gründe, es zu verwerfen. Grundsätzlich gibt es keine Möglichkeit, sie korrekt zu verwenden (außer UT- / En- / Decodierung von UTF8 in Kombination mit en- / decodeURI( Component) - oder zumindest kenne ich keine andere nützliche Anwendung für ( un) escape). Und heute gibt es bessere Alternativen zum Codieren / Decodieren von UTF8 ( TextEncoderusw.)
TS
10

Beachten Sie, dass Sie Folgendes verwenden können, wenn Sie auf node.js abzielen Buffer.from(string).length:

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

UTF-8 codiert Zeichen mit 1 bis 4 Bytes pro Codepunkt. Wie CMS in der akzeptierten Antwort hervorhob, speichert JavaScript jedes Zeichen intern mit 16 Bit (2 Byte).

Wenn Sie jedes Zeichen in der Zeichenfolge über eine Schleife analysieren und die Anzahl der pro Codepunkt verwendeten Bytes zählen und dann die Gesamtzahl mit 2 multiplizieren, sollte die Speichernutzung von JavaScript in Bytes für diese UTF-8-codierte Zeichenfolge angegeben werden. Vielleicht so etwas:

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

Beispiele:

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

Dies sind 3 Möglichkeiten, die ich benutze:

  1. TextEncoder ()

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

  2. Klecks

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

  3. Puffer

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

Hong Ly
quelle
2

Die Antwort von Lauri Oherd funktioniert gut für die meisten in freier Wildbahn gesehenen Zeichenfolgen, schlägt jedoch fehl, wenn die Zeichenfolge einzelne Zeichen im Bereich der Ersatzpaare 0xD800 bis 0xDFFF enthält. Z.B

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

Diese längere Funktion sollte alle Zeichenfolgen verarbeiten:

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

Z.B

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

Die Größe für Zeichenfolgen mit Ersatzpaaren wird korrekt berechnet:

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

Die Ergebnisse können mit der integrierten Funktion von Node verglichen werden Buffer.byteLength:

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

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

Die Größe einer JavaScript-Zeichenfolge beträgt

  • Pre-ES6 : 2 Bytes pro Zeichen
  • ES6 und höher: 2 Bytes pro Zeichen oder 5 oder mehr Bytes pro Zeichen

Pre-ES6
Immer 2 Bytes pro Zeichen. UTF-16 ist nicht zulässig, da in der Spezifikation angegeben ist, dass "Werte vorzeichenlose 16-Bit-Ganzzahlen sein müssen". Da UTF-16-Zeichenfolgen 3- oder 4-Byte-Zeichen verwenden können, würde dies die 2-Byte-Anforderung verletzen. Entscheidend ist, dass UTF-16 zwar nicht vollständig unterstützt werden kann, der Standard jedoch vorschreibt, dass die beiden verwendeten Bytezeichen gültige UTF-16-Zeichen sind. Mit anderen Worten, JavaScript-Zeichenfolgen vor ES6 unterstützen eine Teilmenge von UTF-16-Zeichen.

ES6 und höher
2 Bytes pro Zeichen oder 5 oder mehr Bytes pro Zeichen. Die zusätzlichen Größen kommen ins Spiel, da ES6 (ECMAScript 6) die Unterstützung für Unicode-Codepunkt- Escapezeichen hinzufügt . Die Verwendung eines Unicode-Escape sieht folgendermaßen aus: \ u {1D306}

Praktische Hinweise

  • Dies bezieht sich nicht auf die interne Implementierung eines bestimmten Motors. Beispielsweise verwenden einige Engines Datenstrukturen und Bibliotheken mit vollständiger UTF-16-Unterstützung, aber was sie extern bereitstellen, muss nicht vollständige UTF-16-Unterstützung sein. Ein Motor kann auch externe UTF-16-Unterstützung bieten, ist jedoch nicht dazu verpflichtet.

  • Für ES6 werden praktisch keine Zeichen länger als 5 Byte sein (2 Byte für den Escape-Punkt + 3 Byte für den Unicode-Codepunkt), da die neueste Version von Unicode nur 136.755 mögliche Zeichen enthält, was problemlos in 3 Byte passt. Dies ist jedoch technisch nicht durch den Standard beschränkt, so dass im Prinzip ein einzelnes Zeichen beispielsweise 4 Bytes für den Codepunkt und 6 Bytes insgesamt verwenden könnte.

  • Die meisten Codebeispiele hier zur Berechnung der Bytegröße scheinen ES6-Unicode-Codepunkt-Escapezeichen nicht zu berücksichtigen, sodass die Ergebnisse in einigen Fällen falsch sein können.

Whitneyland
quelle
1
Ich frage mich nur, ob die Größe 2 Bytes pro Zeichen beträgt. Warum entspricht Buffer.from('test').lengthund Buffer.byteLength('test')entspricht 4 (im Knoten) und new Blob(['test']).sizeentspricht auch 4?
user1063287
Pre-ES6: UTF-16 ist zulässig: Siehe ECMA-262, 3. Ausgabe (ab 1999) : Auf Seite 1 heißt es, dass UCS2 oder UTF-16 zulässig sind. Seite 5, Definition des Zeichenfolgenwerts: "... Obwohl jeder Wert normalerweise eine einzelne 16-Bit-Einheit von UTF-16-Text darstellt, ...". Auf Seite 81 finden Sie eine Tabelle, die zeigt, wie übereinstimmende Ersatzpaare als vier UTF-8-Bytes codiert werden müssen.
TS
"pro Zeichen" - Wenn Sie damit "pro vom Benutzer wahrgenommenes Zeichen" ( Spezifikation , einfachere Erklärung ) meinen, kann es sich um eine beliebige Anzahl von 16-Bit-Codeeinheiten handeln. Wenn Sie pro "Codepunkt" gemeint haben, können es in UTF-16 entweder eine oder zwei 16-Bit-Codeeinheiten sein . (Es können nicht 2,5
TS
Ob jedes Element in einer Javascript-Zeichenfolge ( 16-Bit-Ganzzahlwerte ohne Vorzeichen („Elemente“) ) tatsächlich intern durch zwei Bytes dargestellt wird, ist im Standard nicht definiert. (Und wie könnte es sein - Solange die Schnittstelle zum Javascript-Programm dem Standard entspricht, funktioniert alles wie vorgesehen.) Mozilla kann beispielsweise nur ein Byte pro Codepunkt verwenden, wenn die Zeichenfolge nur latin1 enthält
TS
Unicode-Codepunkt-Escapezeichen haben nichts mit der Länge von Zeichenfolgen zu tun - es ist nur eine neue Art, Zeichenfolgen im Quellcode darzustellen. ( '\u{1F600}'.length===2, '\u{1F600}'==='\uD83D\uDE00', '\u{1F600}'==='😀')
TS
1

Ich arbeite mit einer eingebetteten Version der V8-Engine. Ich habe eine einzelne Zeichenfolge getestet. Drücken Sie jeden Schritt 1000 Zeichen. UTF-8.

Erster Test mit Einzelbyte (8 Bit, ANSI) Zeichen "A" (hex: 41). Zweiter Test mit zwei Byte (16 Bit) "Ω" (hex: CE A9) und der dritte Test mit drei Byte (24 Bit) "☺" (hex: E2 98 BA).

In allen drei Fällen druckt das Gerät mit 888 000 Zeichen und ca. 26 348 kb im RAM.

Ergebnis: Die Zeichen werden nicht dynamisch gespeichert. Und nicht nur mit 16bit. - Ok, vielleicht nur für meinen Fall (Embedded 128 MB RAM-Gerät, V8 Engine C ++ / QT) - Die Zeichencodierung hat nichts mit der RAM-Größe der Javascript-Engine zu tun. Beispielsweise ist die Codierung von UUR usw. nur für die Übertragung und Speicherung von Daten auf hoher Ebene nützlich.

Eingebettet oder nicht, Tatsache ist, dass die Zeichen nicht nur in 16 Bit gespeichert sind. Leider habe ich keine 100% ige Antwort, was Javascript auf niedriger Ebene macht. Übrigens. Ich habe das gleiche (erster Test oben) mit einem Array von Zeichen "A" getestet. Schob 1000 Gegenstände pro Schritt. (Genau der gleiche Test. Nur String in Array ersetzt) ​​Und das System bringt nach 10 416 KB mit einer Array-Länge von 1 337 000 nicht mehr genügend Speicher (gewünscht). Die Javascript-Engine ist also nicht einfach eingeschränkt. Es ist etwas komplexer.

Dominik
quelle
1

Ein einzelnes Element in einer JavaScript-Zeichenfolge wird als einzelne UTF-16-Codeeinheit betrachtet. Das heißt, Zeichenfolgenzeichen werden in 16 Bit (1 Codeeinheit) gespeichert, und 16 Bit entspricht 2 Bytes (8 Bit = 1 Byte).

Die charCodeAt()Methode kann verwendet werden, um eine Ganzzahl zwischen 0 und 65535 zurückzugeben, die die UTF-16-Codeeinheit am angegebenen Index darstellt.

Mit codePointAt()kann der gesamte Codepunktwert für Unicode-Zeichen zurückgegeben werden, z. B. UTF-32.

Wenn ein UTF-16-Zeichen nicht in einer einzelnen 16-Bit-Codeeinheit dargestellt werden kann, hat es ein Ersatzpaar und verwendet daher zwei Codeeinheiten (2 x 16-Bit = 4 Byte).

Unter Unicode-Codierungen finden Sie verschiedene Codierungen und ihre Codebereiche.

holmberd
quelle
Was Sie über Leihmütter sagen, scheint gegen die ECMA-Skriptspezifikation zu verstoßen. Wie ich oben kommentiert habe, erfordert die Spezifikation zwei Bytes pro Zeichen, und das Zulassen von Ersatzpaaren würde dies verletzen.
Whitneyland
Javascript ES5-Engines können intern USC-2 oder UTF-16 verwenden, aber was tatsächlich verwendet wird, ist eine Art UCS-2 mit Ersatz. Dies liegt daran, dass Ersatzhälften als separate Zeichen, einzelne UTF-16-Ganzzahlen ohne Vorzeichen, angezeigt werden können. Wenn Sie in Ihrem Quellcode ein Unicode-Zeichen verwenden, für dessen Darstellung mehr als eine einzelne 16-Bit-Codeeinheit erforderlich ist, wird ein Ersatzpaar verwendet. Dieses Verhalten verstößt nicht gegen die Spezifikationen, siehe Kapitel 6 Quelltext: ecma-international.org/ecma-262/5.1
holmberd
0

Sie können dies versuchen:

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

Es hat bei mir funktioniert.

user3728331
quelle
1
Dies setzt sicher voraus, dass alle Zeichen maximal 2 Bytes sind? Wenn es 3 oder 4 Byte Zeichen gibt (die in UTF-8 möglich sind), zählt diese Funktion sie nur als 2 Byte Zeichen?
Adam Burley