Cara mengonversi String ke Bytearray

90

Bagaimana saya bisa mengubah string dalam bytearray menggunakan JavaScript. Output harus setara dengan kode C # di bawah ini.

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

Karena UnicodeEncoding secara default dari UTF-16 dengan Little-Endianness.

Sunting: Saya memiliki persyaratan untuk mencocokkan sisi klien yang dihasilkan bytearray dengan yang dihasilkan di sisi server menggunakan kode C # di atas.

shas
sumber
3
javascript tidak terlalu terkenal karena mudah digunakan dengan BLOB - mengapa Anda tidak mengirimkan saja string dalam JSON?
Marc Gravell
Mungkin Anda bisa melihatnya di sini ..
V4Vendetta
2
String Javascript adalah UTF-16, atau apakah Anda sudah mengetahuinya?
Kevin
2
Pertama-tama mengapa Anda perlu mengonversi ini dalam javascript?
BreakHead
17
String tidak dikodekan. Ya, secara internal mereka direpresentasikan sebagai byte dan mereka memiliki pengkodean, tetapi itu pada dasarnya tidak ada artinya di tingkat skrip. String adalah kumpulan karakter yang logis. Untuk mengenkode karakter, Anda harus secara eksplisit memilih skema encoding, yang dapat Anda gunakan untuk mengubah setiap kode karakter menjadi urutan satu atau lebih byte. Jawaban atas pertanyaan di bawah ini adalah sampah, karena mereka memanggil charCodeAt dan memasukkan nilainya ke dalam array yang disebut "byte". Halo! charCodeAt dapat mengembalikan nilai yang lebih besar dari 255, jadi ini bukan byte!
Triynko

Jawaban:

21

Dalam C # menjalankan ini

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

Akan membuat array dengan

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

array byte

Untuk karakter yang kodenya lebih besar dari 255 akan terlihat seperti ini

array byte

Jika Anda menginginkan perilaku yang sangat mirip di JavaScript, Anda dapat melakukan ini (v2 adalah solusi yang sedikit lebih kuat, sedangkan versi aslinya hanya akan berfungsi untuk 0x00 ~ 0xff)

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
sumber
1
Saya sudah mencoba ini tetapi ini memberi saya hasil yang berbeda dari kode C # di atas. Seperti untuk kasus ini array byte keluaran kode C # adalah = 72,0,101,0,108,0,108,0,111,0 Saya memiliki persyaratan untuk mencocokkan keduanya sehingga itu tidak berfungsi.
shas
2
@saya telah menguji sebelumnya hanya pada Firefox 4. Versi yang diperbarui telah diuji pada Firefox 4, Chrome 13 dan IE9.
BrunoLM
40
Perhatikan bahwa jika string berisi karakter unicode, charCodeAt (i) akan> 255, yang mungkin bukan yang Anda inginkan.
broofa
23
Ya, ini tidak benar. charCodeAt tidak mengembalikan satu byte. Tidak masuk akal untuk mendorong nilai yang lebih besar dari 255 ke dalam array yang disebut "byte"; sangat menyesatkan. Fungsi ini tidak melakukan pengkodean sama sekali, hanya menempelkan kode karakter ke dalam array.
Triynko
1
Saya tidak mengerti mengapa jawaban ini ditandai sebagai benar karena tidak menyandikan apa pun.
AB
32

Jika Anda mencari solusi yang berfungsi di node.js, Anda dapat menggunakan ini:

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
sumber
3
Ini untuk node.js tetapi saya pikir pertanyaannya adalah mencari solusi yang berfungsi di browser. Namun demikian, ini berfungsi dengan benar, tidak seperti kebanyakan jawaban lain untuk pertanyaan ini, jadi +1.
Daniel Cassidy
Ini berfungsi tetapi kode yang lebih sederhana adalah function convertString (myString) {var myBuffer = new Buffer (myString, 'utf16le'); console.log (myBuffer); kembalikan myBuffer; }
Philip Rutovitz
16

Saya kira C # dan Java menghasilkan array byte yang sama. Jika Anda memiliki karakter non-ASCII, itu tidak cukup untuk menambahkan tambahan 0. Contoh saya berisi beberapa karakter khusus:

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

Saya tidak tahu apakah C # menempatkan BOM (Byte Order Marks), tetapi jika menggunakan UTF-16, Java String.getBytesmenambahkan byte berikut: 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

Edit:

Menambahkan karakter khusus (U + 1D11E) MUSICAL SYMBOL G CLEF (di luar BPM, jadi mengambil tidak hanya 2 byte dalam UTF-16, tetapi 4.

Versi JavaScript saat ini menggunakan "UCS-2" secara internal, jadi simbol ini menggunakan spasi 2 karakter normal.

Saya tidak yakin, tetapi saat menggunakannya charCodeAttampaknya kami mendapatkan titik kode pengganti yang juga digunakan dalam UTF-16, jadi karakter non-BPM ditangani dengan benar.

Masalah ini sama sekali tidak sepele. Ini mungkin tergantung pada versi dan mesin JavaScript yang digunakan. Jadi, jika Anda menginginkan solusi yang andal, Anda harus melihat:

hgoebl
sumber
1
Masih belum jawaban yang lengkap. UTF16 adalah pengkodean panjang variabel yang menggunakan potongan 16-bit untuk mewakili karakter. Sebuah karakter tunggal akan dikodekan sebagai 2 byte atau 4 byte, tergantung pada seberapa besar nilai kode karakter tersebut. Karena fungsi ini menulis paling banyak 2 byte, fungsi ini tidak dapat menangani semua poin kode karakter unicode, dan bukan implementasi lengkap dari pengkodean UTF16, tidak dalam jangka panjang.
Triynko
@Triynko setelah saya edit dan tes, apakah menurut Anda ini bukan jawaban lengkap? Jika ya, apakah Anda punya jawaban?
hgoebl
2
@Triynko Anda setengah benar, tetapi sebenarnya jawaban ini berfungsi dengan benar. String JavaScript sebenarnya bukan urutan Poin Kode Unicode, melainkan urutan Unit Kode UTF-16. Terlepas dari namanya, charCodeAtmengembalikan Unit Kode UTF-16, dalam kisaran 0-65535. Karakter di luar rentang 2-byte direpresentasikan sebagai pasangan pengganti, seperti di UTF-16. (Ngomong-ngomong, ini juga berlaku untuk string dalam beberapa bahasa lain, termasuk Java dan C #.)
Daniel Cassidy
Ngomong-ngomong, (charCode & 0xFF00) >> 8ini mubazir, Anda tidak perlu menutupinya sebelum berpindah.
Patrick Roberts
15

Cara termudah di tahun 2018 adalah TextEncoder tetapi elemen yang dikembalikan bukan byte array, melainkan Uint8Array. (Dan tidak semua browser mendukungnya)

let utf8Encode = new TextEncoder();
utf8Encode.encode("eee")
> Uint8Array [ 101, 101, 101 ]
kode4j
sumber
Ini aneh. Saya kira tidak menggunakan nama variabel yang berbeda karena utf8Decode dan utf8Encode akan berfungsi.
Unihedron
Anda dapat menggunakan TextDecoder untuk decode: new TextDecoder().decode(new TextEncoder().encode(str)) == str.
Fons
Berikut adalah tabel dukungan dari TextEncoder: caniuse
Fons
11

Array UTF-16 Byte

JavaScript mengkodekan string sebagai UTF-16 , seperti C # UnicodeEncoding, jadi array byte harus sama persis dengan yang digunakan charCodeAt(), dan memisahkan setiap pasangan byte yang dikembalikan menjadi 2 byte terpisah, seperti di:

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

Sebagai contoh:

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

Namun, Jika Anda ingin mendapatkan array byte UTF-8, Anda harus mentranskode byte tersebut.

Array UTF-8 Byte

Solusinya terasa agak tidak sepele, tetapi saya menggunakan kode di bawah ini dalam lingkungan produksi dengan lalu lintas tinggi dengan kesuksesan besar ( sumber asli ).

Juga, untuk pembaca yang tertarik, saya menerbitkan pembantu unicode saya yang membantu saya bekerja dengan panjang string yang dilaporkan oleh bahasa lain seperti PHP.

/**
 * 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
sumber
dan apa kebalikan dari ini?
simbo1905
Saya akan menggambarkan fungsi invers sebagai "mengubah array byte UTF-8 menjadi string UTF-16 asli". Saya tidak pernah menghasilkan kebalikannya. Di myc env, saya menghapus kode ini dengan mengubah output API ke rentang karakter alih-alih rentang byte, lalu saya menggunakan rune untuk mengurai rentang.
jchook
Saya menyarankan ini harus menjadi jawaban yang diterima untuk pertanyaan ini.
LeaveTheCapital
10

Terinspirasi oleh jawaban @ hgoebl. Kodenya untuk UTF-16 dan saya membutuhkan sesuatu untuk US-ASCII. Jadi, inilah jawaban yang lebih lengkap mencakup US-ASCII, UTF-16, dan 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 memiliki panjang variabel dan tidak disertakan karena saya harus menulis pengkodeannya sendiri. UTF-8 dan UTF-16 memiliki panjang variabel. UTF-8, UTF-16, dan UTF-32 memiliki jumlah bit minimum sesuai dengan namanya. Jika karakter UTF-32 memiliki titik kode 65 maka itu berarti ada 3 awalan 0. Tetapi kode yang sama untuk UTF-16 hanya memiliki 1 awalan 0. Sebaliknya US-ASCII adalah fixed width 8-bits yang berarti dapat langsung diterjemahkan ke byte.

String.prototype.charCodeAtmengembalikan jumlah maksimum 2 byte dan sama persis dengan UTF-16. Namun untuk UTF-32 String.prototype.codePointAtdiperlukan yang merupakan bagian dari proposal ECMAScript 6 (Harmoni). Karena charCodeAt mengembalikan 2 byte yang merupakan karakter yang lebih mungkin daripada yang dapat diwakili oleh US-ASCII, fungsi stringToAsciiByteArrayakan melempar dalam kasus seperti itu alih-alih membagi karakter menjadi dua dan mengambil salah satu atau kedua byte.

Perhatikan bahwa jawaban ini tidak sepele karena pengkodean karakter tidak sepele. Jenis array byte yang Anda inginkan bergantung pada pengkodean karakter apa yang Anda ingin wakili oleh byte tersebut.

javascript memiliki opsi untuk menggunakan UTF-16 atau UCS-2 secara internal tetapi karena ia memiliki metode yang bertindak seperti UTF-16, saya tidak mengerti mengapa browser apa pun akan menggunakan UCS-2. Lihat juga: https://mathiasbynens.be/notes/javascript-encoding

Ya, saya tahu pertanyaannya berusia 4 tahun tetapi saya membutuhkan jawaban ini untuk diri saya sendiri.

SkySpiral7
sumber
Hasil Buffer Node '02'adalah [ 48, 0, 50, 0 ]tempat stringToUtf16ByteArrayfungsi Anda kembali [ 0, 48, 0, 50 ]. yang mana yang benar?
pkyeck
@ pkyeck Fungsi stringToUtf16ByteArray saya di atas mengembalikan UTF-16 BE tanpa BOM. Contoh yang Anda berikan dari node adalah UTF-16 LE tanpa BOM. Saya pikir Big-endian lebih normal daripada little-endian, tetapi bisa saja salah.
SkySpiral7
2

Karena saya tidak dapat mengomentari jawabannya, saya akan melanjutkan jawaban Jin Izzraeel

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

dengan mengatakan bahwa Anda dapat menggunakan ini jika Anda ingin menggunakan buffer Node.js di browser Anda.

https://github.com/feross/buffer

Oleh karena itu, keberatan Tom Stickel tidak valid, dan jawabannya memang jawaban yang valid.

mmdts
sumber
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
sumber
4
Akan sangat membantu jika Anda memberikan beberapa teks untuk disertakan dengan kode untuk menjelaskan mengapa seseorang dapat memilih pendekatan ini daripada salah satu jawaban lainnya.
NightOwl888
pendekatan ini lebih sederhana daripada yang lain tetapi melakukan hal yang sama, itulah alasan saya tidak menulis apa pun.
Fabio Maciel
encodeHexakan mengembalikan larik angka 16-bit, bukan byte.
Pavlo
0

Solusi terbaik yang saya temukan di tempat (meskipun kemungkinan besar mentah) adalah:

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

Meskipun saya perhatikan pertanyaan ini telah ada di sini selama lebih dari setahun.

Whosdr
sumber
2
Ini tidak bekerja dengan benar. Logika karakter panjang variabel salah, tidak ada karakter 8-bit dalam UTF-16. Terlepas dari namanya, charCodeAtmengembalikan Unit Kode UTF-16 16-bit, jadi Anda tidak memerlukan logika panjang variabel apa pun. Anda bisa memanggil charCodeAt, membagi hasilnya menjadi dua byte 8-bit, dan memasukkannya ke dalam larik keluaran (byte urutan terendah terlebih dahulu karena pertanyaan menanyakan UTF-16LE).
Daniel Cassidy
0

Saya tahu pertanyaannya sudah hampir 4 tahun, tetapi inilah yang berjalan lancar dengan saya:

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

atau, jika Anda ingin bekerja dengan string saja, dan tanpa Array, Anda dapat menggunakan:

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
sumber
2
Jenis pekerjaan ini, tetapi sangat menyesatkan. The bytesArray tidak mengandung 'byte', berisi nomor 16-bit, yang mewakili string dalam UTF-16 unit kode. Ini hampir seperti pertanyaan yang ditanyakan, tetapi sebenarnya hanya kebetulan.
Daniel Cassidy
-1

Berikut adalah fungsi yang sama yang diposting oleh @BrunoLM diubah menjadi fungsi prototipe String:

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

Jika Anda mendefinisikan fungsi seperti itu, maka Anda dapat memanggil metode .getBytes () pada string apa pun:

var str = "Hello World!";
var bytes = str.getBytes();
mweaver
sumber
31
Ini masih salah, seperti jawaban yang dirujuknya. charCodeAt tidak mengembalikan satu byte. Tidak masuk akal untuk mendorong nilai yang lebih besar dari 255 ke dalam array yang disebut "byte"; sangat menyesatkan. Fungsi ini tidak melakukan pengkodean sama sekali, hanya menempelkan kode karakter ke dalam array. Untuk melakukan pengkodean UTF16, Anda harus memeriksa kode charcter, memutuskan apakah Anda perlu mewakilinya dengan 2 byte atau 4 byte (karena UTF16 adalah pengkodean panjang variabel), lalu menulis setiap byte ke array secara individual.
Triynko
8
Selain itu, memodifikasi prototipe tipe data native merupakan praktik yang buruk.
Andrew Lundin
@AndrewLundin, itu menarik ... bilang siapa?
Jerther
2
@Jerther: stackoverflow.com/questions/14034180/…
Andrew Lundin
-3

Anda tidak perlu garis bawah, cukup gunakan peta bawaan:

var string = 'Hello World!';

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

Christian Gutierrez Sierra
sumber
1
Ini mengembalikan larik angka 16-bit yang mewakili string sebagai urutan poin kode UTF-16. Bukan itu yang diminta OP, tapi setidaknya itu membuat Anda berpisah di sana.
Daniel Cassidy