So konvertieren Sie einen String in Bytearray

87

Wie kann ich einen String in Bytearray mit JavaScript konvertieren? Die Ausgabe sollte dem folgenden C # -Code entsprechen.

UnicodeEncoding encoding = new UnicodeEncoding();
byte[] bytes = encoding.GetBytes(AnyString);

Da UnicodeEncoding standardmäßig UTF-16 mit Little-Endianness ist.

Bearbeiten: Ich muss die vom Bytearray generierte Client-Seite mit der auf der Server-Seite generierten Client-Seite mit dem obigen C # -Code abgleichen.

shas
quelle
3
Javascript ist nicht gerade bekannt für seine einfache Verwendung mit BLOBs - warum senden Sie den String nicht einfach in JSON?
Marc Gravell
Vielleicht können Sie einen Blick hier werfen ..
V4Vendetta
2
Eine Javascript-Zeichenfolge ist UTF-16, oder wussten Sie das bereits?
Kevin
2
Zunächst einmal, warum müssen Sie dies in Javascript konvertieren?
BreakHead
17
Zeichenfolgen werden nicht codiert. Ja, intern werden sie als Bytes dargestellt und haben eine Codierung, aber das ist auf Skriptebene im Wesentlichen bedeutungslos. Zeichenfolgen sind logische Sammlungen von Zeichen. Um ein Zeichen zu codieren, müssen Sie explizit ein Codierungsschema auswählen, mit dem Sie jeden Zeichencode in eine Folge von einem oder mehreren Bytes umwandeln können. Die Antworten auf diese Frage sind Müll, da sie charCodeAt aufrufen und seinen Wert in ein Array namens "Bytes" stecken. Hallo! charCodeAt kann Werte größer als 255 zurückgeben, es ist also kein Byte!
Triynko

Antworten:

20

In C # läuft dies

UnicodeEncoding encoding = new UnicodeEncoding();
byte[] bytes = encoding.GetBytes("Hello");

Erstellt ein Array mit

72,0,101,0,108,0,108,0,111,0

Byte-Array

Für ein Zeichen, dessen Code größer als 255 ist, sieht es so aus

Byte-Array

Wenn Sie ein sehr ähnliches Verhalten in JavaScript wünschen, können Sie dies tun (v2 ist eine etwas robustere Lösung, während die Originalversion nur für 0x00 ~ 0xff funktioniert).

var str = "Hello竜";
var bytes = []; // char codes
var bytesv2 = []; // char codes

for (var i = 0; i < str.length; ++i) {
  var code = str.charCodeAt(i);
  
  bytes = bytes.concat([code]);
  
  bytesv2 = bytesv2.concat([code & 0xff, code / 256 >>> 0]);
}

// 72, 101, 108, 108, 111, 31452
console.log('bytes', bytes.join(', '));

// 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 220, 122
console.log('bytesv2', bytesv2.join(', '));

BrunoLM
quelle
1
Ich habe dies bereits versucht, aber dies gibt mir das andere Ergebnis als der obige C # -Code. Wie in diesem Fall ist das C # -Code-Ausgabebyte-Array = 72,0,101,0,108,0,108,0,111,0. Ich muss beide übereinstimmen, damit das nicht funktioniert.
Shas
2
@shas Ich habe die vorherige Version nur unter Firefox 4 getestet. Die aktualisierte Version wurde unter Firefox 4, Chrome 13 und IE9 getestet.
BrunoLM
40
Beachten Sie, dass charCodeAt (i)> 255 ist, wenn die Zeichenfolge Unicode-Zeichen enthält, was wahrscheinlich nicht das ist, was Sie wollen.
Broofa
23
Ja, das ist falsch. charCodeAt gibt kein Byte zurück. Es macht keinen Sinn, einen Wert größer als 255 in ein Array namens "Bytes" zu verschieben. sehr irreführend. Diese Funktion führt überhaupt keine Codierung durch, sondern klebt nur die Zeichencodes in ein Array.
Triynko
1
Ich verstehe nicht, warum diese Antwort als richtig markiert ist, da sie nichts codiert.
AB
31

Wenn Sie nach einer Lösung suchen, die in node.js funktioniert, können Sie Folgendes verwenden:

var myBuffer = [];
var str = 'Stack Overflow';
var buffer = new Buffer(str, 'utf16le');
for (var i = 0; i < buffer.length; i++) {
    myBuffer.push(buffer[i]);
}

console.log(myBuffer);
Jin
quelle
3
Dies ist für node.js, aber ich denke, die Frage sucht nach einer Lösung, die in einem Browser funktioniert. Trotzdem funktioniert es korrekt, im Gegensatz zu den meisten anderen Antworten auf diese Frage, also +1.
Daniel Cassidy
Dies funktioniert, aber viel einfacher Code ist die Funktion convertString (myString) {var myBuffer = neuer Puffer (myString, 'utf16le'); console.log (myBuffer); return myBuffer; }
Philip Rutovitz
16

Ich nehme an, C # und Java erzeugen Arrays mit gleichen Bytes. Wenn Sie Nicht-ASCII-Zeichen haben, reicht es nicht aus, eine zusätzliche 0 hinzuzufügen. Mein Beispiel enthält einige Sonderzeichen:

var str = "Hell ö € Ω 𝄞";
var bytes = [];
var charCode;

for (var i = 0; i < str.length; ++i)
{
    charCode = str.charCodeAt(i);
    bytes.push((charCode & 0xFF00) >> 8);
    bytes.push(charCode & 0xFF);
}

alert(bytes.join(' '));
// 0 72 0 101 0 108 0 108 0 32 0 246 0 32 32 172 0 32 3 169 0 32 216 52 221 30

Ich weiß nicht, ob C # Stückliste (Byte Order Marks) platziert, aber bei Verwendung von UTF-16 String.getBytesfügt Java die folgenden Bytes hinzu: 254 255.

String s = "Hell ö € Ω ";
// now add a character outside the BMP (Basic Multilingual Plane)
// we take the violin-symbol (U+1D11E) MUSICAL SYMBOL G CLEF
s += new String(Character.toChars(0x1D11E));
// surrogate codepoints are: d834, dd1e, so one could also write "\ud834\udd1e"

byte[] bytes = s.getBytes("UTF-16");
for (byte aByte : bytes) {
    System.out.print((0xFF & aByte) + " ");
}
// 254 255 0 72 0 101 0 108 0 108 0 32 0 246 0 32 32 172 0 32 3 169 0 32 216 52 221 30

Bearbeiten:

Ein Sonderzeichen (U + 1D11E) wurde hinzugefügt. MUSICAL SYMBOL G CLEF (außerhalb von BPM, also nicht nur 2 Bytes in UTF-16, sondern 4.

Aktuelle JavaScript-Versionen verwenden intern "UCS-2", sodass dieses Symbol 2 normale Zeichen einnimmt.

Ich bin mir nicht sicher, aber wenn charCodeAtwir es verwenden, erhalten wir anscheinend genau die Ersatzcodepunkte, die auch in UTF-16 verwendet werden, sodass Nicht-BPM-Zeichen korrekt behandelt werden.

Dieses Problem ist absolut nicht trivial. Dies kann von den verwendeten JavaScript-Versionen und Engines abhängen. Wenn Sie also zuverlässige Lösungen wünschen, sollten Sie sich Folgendes ansehen:

hgoebl
quelle
1
Immer noch keine vollständige Antwort. UTF16 ist eine Codierung mit variabler Länge, die 16-Bit-Blöcke zur Darstellung von Zeichen verwendet. Ein einzelnes Zeichen wird entweder als 2 Byte oder als 4 Byte codiert, je nachdem, wie groß der Zeichencodewert ist. Da diese Funktion höchstens 2 Bytes schreibt, kann sie nicht alle Unicode-Zeichencodepunkte verarbeiten und ist bei weitem keine vollständige Implementierung der UTF16-Codierung.
Triynko
@Triynko nach meiner Bearbeitung und meinem Test, denkst du immer noch, dass dies nicht die vollständige Antwort ist? Wenn ja, haben Sie eine Antwort?
Hgoebl
2
@Triynko Du hast halb recht, aber tatsächlich funktioniert diese Antwort richtig. JavaScript-Zeichenfolgen sind keine Sequenzen von Unicode-Codepunkten, sondern Sequenzen von UTF-16-Codeeinheiten. Gibt trotz des Namens charCodeAteine UTF-16-Codeeinheit im Bereich von 0 bis 65535 zurück. Zeichen außerhalb des 2-Byte-Bereichs werden wie in UTF-16 als Ersatzpaare dargestellt. (Übrigens gilt dies für Zeichenfolgen in mehreren anderen Sprachen, einschließlich Java und C #.)
Daniel Cassidy
Ist übrigens (charCode & 0xFF00) >> 8redundant, müssen Sie es vor dem Verschieben nicht maskieren.
Patrick Roberts
14

Der einfachste Weg im Jahr 2018 sollte TextEncoder sein, aber das zurückgegebene Element ist kein Byte-Array, sondern Uint8Array. (Und nicht alle Browser unterstützen es)

let utf8Encode = new TextEncoder();
utf8Encode.encode("eee")
> Uint8Array [ 101, 101, 101 ]
code4j
quelle
Das ist eigenartig. Ich nehme nicht an, dass die Verwendung unterschiedlicher Variablennamen als utf8Decode und utf8Encode funktionieren würde.
Unihedron
Mit TextDecoder können Sie Folgendes dekodieren : new TextDecoder().decode(new TextEncoder().encode(str)) == str.
Fons
Hier sind die Support-Tabellen von TextEncoder: caniuse
Fons
11

UTF-16-Byte-Array

JavaScript codiert Zeichenfolgen wie die von C # als UTF-16 , UnicodeEncodingsodass die Byte-Arrays genau mit übereinstimmen charCodeAt()und jedes zurückgegebene Byte-Paar in zwei separate Bytes aufteilen sollten, wie in:

function strToUtf16Bytes(str) {
  const bytes = [];
  for (ii = 0; ii < str.length; ii++) {
    const code = str.charCodeAt(ii); // x00-xFFFF
    bytes.push(code & 255, code >> 8); // low, high
  }
  return bytes;
}

Beispielsweise:

strToUtf16Bytes('🌵'); 
// [ 60, 216, 53, 223 ]

Wenn Sie jedoch ein UTF-8-Byte-Array erhalten möchten, müssen Sie die Bytes transkodieren.

UTF-8-Byte-Array

Die Lösung fühlt sich nicht trivial an, aber ich habe den folgenden Code in einer stark frequentierten Produktionsumgebung mit großem Erfolg verwendet ( Originalquelle ).

Für den interessierten Leser habe ich außerdem meine Unicode-Helfer veröffentlicht , die mir helfen, mit Zeichenfolgenlängen zu arbeiten, die von anderen Sprachen wie PHP gemeldet werden.

/**
 * Convert a string to a unicode byte array
 * @param {string} str
 * @return {Array} of bytes
 */
export function strToUtf8Bytes(str) {
  const utf8 = [];
  for (let ii = 0; ii < str.length; ii++) {
    let charCode = str.charCodeAt(ii);
    if (charCode < 0x80) utf8.push(charCode);
    else if (charCode < 0x800) {
      utf8.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
    } else if (charCode < 0xd800 || charCode >= 0xe000) {
      utf8.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
    } else {
      ii++;
      // Surrogate pair:
      // UTF-16 encodes 0x10000-0x10FFFF by subtracting 0x10000 and
      // splitting the 20 bits of 0x0-0xFFFFF into two halves
      charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (str.charCodeAt(ii) & 0x3ff));
      utf8.push(
        0xf0 | (charCode >> 18),
        0x80 | ((charCode >> 12) & 0x3f),
        0x80 | ((charCode >> 6) & 0x3f),
        0x80 | (charCode & 0x3f),
      );
    }
  }
  return utf8;
}
jchook
quelle
und was ist das Gegenteil davon?
simbo1905
Ich würde die Umkehrfunktion als "Konvertieren eines UTF-8-Byte-Arrays in einen nativen UTF-16-String" beschreiben. Ich habe nie das Gegenteil produziert. In myc env habe ich diesen Code entfernt, indem ich die API-Ausgabe in einen Zeichenbereich anstelle eines Bytebereichs geändert habe. Anschließend habe ich Runen verwendet , um die Bereiche zu analysieren.
Jchook
Ich würde vorschlagen, dass dies die akzeptierte Antwort auf diese Frage sein sollte.
LeaveTheCapital
10

Inspiriert von @ hgoebls Antwort. Sein Code ist für UTF-16 und ich brauchte etwas für US-ASCII. Hier ist eine vollständigere Antwort zu US-ASCII, UTF-16 und UTF-32.

/**@returns {Array} bytes of US-ASCII*/
function stringToAsciiByteArray(str)
{
    var bytes = [];
   for (var i = 0; i < str.length; ++i)
   {
       var charCode = str.charCodeAt(i);
      if (charCode > 0xFF)  // char > 1 byte since charCodeAt returns the UTF-16 value
      {
          throw new Error('Character ' + String.fromCharCode(charCode) + ' can\'t be represented by a US-ASCII byte.');
      }
       bytes.push(charCode);
   }
    return bytes;
}
/**@returns {Array} bytes of UTF-16 Big Endian without BOM*/
function stringToUtf16ByteArray(str)
{
    var bytes = [];
    //currently the function returns without BOM. Uncomment the next line to change that.
    //bytes.push(254, 255);  //Big Endian Byte Order Marks
   for (var i = 0; i < str.length; ++i)
   {
       var charCode = str.charCodeAt(i);
       //char > 2 bytes is impossible since charCodeAt can only return 2 bytes
       bytes.push((charCode & 0xFF00) >>> 8);  //high byte (might be 0)
       bytes.push(charCode & 0xFF);  //low byte
   }
    return bytes;
}
/**@returns {Array} bytes of UTF-32 Big Endian without BOM*/
function stringToUtf32ByteArray(str)
{
    var bytes = [];
    //currently the function returns without BOM. Uncomment the next line to change that.
    //bytes.push(0, 0, 254, 255);  //Big Endian Byte Order Marks
   for (var i = 0; i < str.length; i+=2)
   {
       var charPoint = str.codePointAt(i);
       //char > 4 bytes is impossible since codePointAt can only return 4 bytes
       bytes.push((charPoint & 0xFF000000) >>> 24);
       bytes.push((charPoint & 0xFF0000) >>> 16);
       bytes.push((charPoint & 0xFF00) >>> 8);
       bytes.push(charPoint & 0xFF);
   }
    return bytes;
}

UTF-8 hat eine variable Länge und ist nicht enthalten, da ich die Codierung selbst schreiben müsste. UTF-8 und UTF-16 sind variabel lang. UTF-8, UTF-16 und UTF-32 haben eine Mindestanzahl von Bits, wie der Name schon sagt. Wenn ein UTF-32-Zeichen einen Codepunkt von 65 hat, bedeutet dies, dass es 3 führende Nullen gibt. Der gleiche Code für UTF-16 hat jedoch nur 1 führende 0. US-ASCII hingegen hat eine feste Breite von 8 Bit, was bedeutet, dass es direkt in Bytes übersetzt werden kann.

String.prototype.charCodeAtGibt eine maximale Anzahl von 2 Bytes zurück und stimmt genau mit UTF-16 überein. Für UTF-32 String.prototype.codePointAtwird jedoch benötigt, was Teil des ECMAScript 6 (Harmony) -Vorschlags ist. Da charCodeAt 2 Bytes zurückgibt, was mehr mögliche Zeichen sind, als US-ASCII darstellen kann, wird die Funktion stringToAsciiByteArrayin solchen Fällen ausgelöst, anstatt das Zeichen in zwei Hälften zu teilen und eines oder beide Bytes zu verwenden.

Beachten Sie, dass diese Antwort nicht trivial ist, da die Zeichenkodierung nicht trivial ist. Welche Art von Byte-Array Sie möchten, hängt davon ab, welche Zeichencodierung diese Bytes darstellen sollen.

Javascript hat die Möglichkeit, entweder UTF-16 oder UCS-2 intern zu verwenden. Da es jedoch Methoden gibt, die sich wie UTF-16 verhalten, verstehe ich nicht, warum ein Browser UCS-2 verwenden würde. Siehe auch: https://mathiasbynens.be/notes/javascript-encoding

Ja, ich weiß, dass die Frage 4 Jahre alt ist, aber ich brauchte diese Antwort für mich.

SkySpiral7
quelle
Die Pufferergebnisse von Node für '02'sind dort, [ 48, 0, 50, 0 ]wo Ihre stringToUtf16ByteArrayFunktion zurückkehrt [ 0, 48, 0, 50 ]. Welches ist korrekt?
Pkyeck
@pkyeck Meine obige Funktion stringToUtf16ByteArray gibt UTF-16 BE ohne Stückliste zurück. Das Beispiel, das Sie vom Knoten gegeben haben, ist UTF-16 LE ohne Stückliste. Ich hatte gedacht, Big-Endian sei normaler als Little-Endian, könnte aber falsch liegen.
SkySpiral7
2

Da ich die Antwort nicht kommentieren kann, würde ich auf Jin Izzraeels Antwort aufbauen

var myBuffer = [];
var str = 'Stack Overflow';
var buffer = new Buffer(str, 'utf16le');
for (var i = 0; i < buffer.length; i++) {
    myBuffer.push(buffer[i]);
}

console.log(myBuffer);

indem Sie sagen, dass Sie dies verwenden könnten, wenn Sie einen Node.js-Puffer in Ihrem Browser verwenden möchten.

https://github.com/feross/buffer

Daher ist der Einwand von Tom Stickel nicht gültig, und die Antwort ist in der Tat eine gültige Antwort.

mmdts
quelle
1
String.prototype.encodeHex = function () {
    return this.split('').map(e => e.charCodeAt())
};

String.prototype.decodeHex = function () {    
    return this.map(e => String.fromCharCode(e)).join('')
};
Fabio Maciel
quelle
4
Es wäre hilfreich, wenn Sie dem Code einen Text hinzufügen würden, um zu erklären, warum man diesen Ansatz anstelle einer der anderen Antworten wählen könnte.
NightOwl888
Dieser Ansatz ist einfacher als andere, macht aber dasselbe. Deshalb habe ich nichts geschrieben.
Fabio Maciel
encodeHexgibt ein Array von 16-Bit-Zahlen zurück, keine Bytes.
Pavlo
0

Die beste Lösung, die ich vor Ort gefunden habe (obwohl höchstwahrscheinlich grob), wäre:

String.prototype.getBytes = function() {
    var bytes = [];
    for (var i = 0; i < this.length; i++) {
        var charCode = this.charCodeAt(i);
        var cLen = Math.ceil(Math.log(charCode)/Math.log(256));
        for (var j = 0; j < cLen; j++) {
            bytes.push((charCode << (j*8)) & 0xFF);
        }
    }
    return bytes;
}

Obwohl ich bemerke, dass diese Frage seit über einem Jahr hier ist.

Whosdr
quelle
2
Dies funktioniert nicht richtig. Die Zeichenlogik mit variabler Länge ist falsch. UTF-16 enthält keine 8-Bit-Zeichen. Gibt trotz des Namens charCodeAteine 16-Bit-UTF-16-Codeeinheit zurück, sodass Sie keine Logik mit variabler Länge benötigen. Sie können einfach charCodeAt aufrufen, das Ergebnis in zwei 8-Bit-Bytes aufteilen und diese in das Ausgabearray einfügen (Byte niedrigster Ordnung zuerst, da die Frage nach UTF-16LE fragt).
Daniel Cassidy
0

Ich weiß, dass die Frage fast 4 Jahre alt ist, aber das hat bei mir reibungslos funktioniert:

String.prototype.encodeHex = function () {
  var bytes = [];
  for (var i = 0; i < this.length; ++i) {
    bytes.push(this.charCodeAt(i));
  }
  return bytes;
};

Array.prototype.decodeHex = function () {    
  var str = [];
  var hex = this.toString().split(',');
  for (var i = 0; i < hex.length; i++) {
    str.push(String.fromCharCode(hex[i]));
  }
  return str.toString().replace(/,/g, "");
};

var str = "Hello World!";
var bytes = str.encodeHex();

alert('The Hexa Code is: '+bytes+' The original string is:  '+bytes.decodeHex());

Wenn Sie nur mit Zeichenfolgen und ohne Array arbeiten möchten, können Sie Folgendes verwenden:

String.prototype.encodeHex = function () {
  var bytes = [];
  for (var i = 0; i < this.length; ++i) {
    bytes.push(this.charCodeAt(i));
  }
  return bytes.toString();
};

String.prototype.decodeHex = function () {    
  var str = [];
  var hex = this.split(',');
  for (var i = 0; i < hex.length; i++) {
    str.push(String.fromCharCode(hex[i]));
  }
  return str.toString().replace(/,/g, "");
};

var str = "Hello World!";
var bytes = str.encodeHex();

alert('The Hexa Code is: '+bytes+' The original string is:  '+bytes.decodeHex());

Hasan A Yousef
quelle
2
Diese Art von Arbeiten ist aber äußerst irreführend. Das bytesArray enthält keine 'Bytes', sondern 16-Bit-Zahlen, die die Zeichenfolge in UTF-16-Codeeinheiten darstellen. Dies ist fast das, wonach die Frage gestellt wurde, aber eigentlich nur durch Zufall.
Daniel Cassidy
-1

Hier ist dieselbe Funktion, die @BrunoLM veröffentlicht hat und die in eine String-Prototypfunktion konvertiert wurde:

String.prototype.getBytes = function () {
  var bytes = [];
  for (var i = 0; i < this.length; ++i) {
    bytes.push(this.charCodeAt(i));
  }
  return bytes;
};

Wenn Sie die Funktion als solche definieren, können Sie die Methode .getBytes () für eine beliebige Zeichenfolge aufrufen:

var str = "Hello World!";
var bytes = str.getBytes();
mweaver
quelle
31
Dies ist immer noch falsch, genau wie die Antwort, auf die es verweist. charCodeAt gibt kein Byte zurück. Es macht keinen Sinn, einen Wert größer als 255 in ein Array namens "Bytes" zu verschieben. sehr irreführend. Diese Funktion führt überhaupt keine Codierung durch, sondern klebt nur die Zeichencodes in ein Array. Um eine UTF16-Codierung durchzuführen, müssen Sie den Zeichencode untersuchen, entscheiden, ob Sie ihn mit 2 Byte oder 4 Byte darstellen müssen (da UTF16 eine Codierung variabler Länge ist), und dann jedes Byte einzeln in das Array schreiben.
Triynko
8
Es ist auch eine schlechte Praxis, den Prototyp nativer Datentypen zu ändern.
Andrew Lundin
@ AndrewLundin, das ist interessant ... sagt wer?
Jerther
2
@ Jerther: stackoverflow.com/questions/14034180/…
Andrew Lundin
-3

Sie brauchen keinen Unterstrich, verwenden Sie einfach die integrierte Karte:

var string = 'Hello World!';

document.write(string.split('').map(function(c) { return c.charCodeAt(); }));

Christian Gutierrez Sierra
quelle
1
Dies gibt ein Array von 16-Bit-Zahlen zurück, die die Zeichenfolge als Folge von UTF-16-Codepunkten darstellen. Das hat das OP nicht verlangt, aber es bringt Sie zumindest auf halbem Weg dorthin.
Daniel Cassidy