String'i Bytearray'e dönüştürme


92

JavaScript kullanarak bytearray'deki bir dizeyi nasıl dönüştürebilirim. Çıktı, aşağıdaki C # koduna eşdeğer olmalıdır.

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

UnicodeEncoding varsayılan olarak Little-Endianness ile UTF-16'dır.

Düzenleme: Bytearray tarafından oluşturulan istemci tarafını, yukarıdaki C # kodunu kullanarak sunucu tarafında oluşturulanla eşleştirme gereksinimim var.


3
Javascript, BLOB'larla kullanımının kolay olmasıyla tam olarak bilinmemektedir - neden dizeyi JSON biçiminde göndermiyorsunuz?
Marc Gravell

Belki buraya bir göz atabilirsiniz ..
V4Vendetta

2
Bir Javascript dizesi UTF-16'dır veya bunu zaten biliyor muydunuz?
Kevin

2
Öncelikle neden bunu javascript'e dönüştürmeniz gerekiyor?
BreakHead

17
Dizeler kodlanmamıştır. Evet, dahili olarak bayt olarak temsil edilirler ve bir kodlamaları vardır, ancak bu, komut dosyası düzeyinde aslında anlamsızdır. Dizeler, mantıksal karakter koleksiyonlarıdır. Bir karakteri kodlamak için, her karakter kodunu bir veya daha fazla bayt dizisine dönüştürmek için kullanabileceğiniz bir kodlama şemasını açıkça seçmelisiniz. Aşağıdaki bu sorunun yanıtları, charCodeAt'ı çağırdıkları ve değerini "bayt" adlı bir diziye yapıştırdıkları için gereksizdir. Merhaba! charCodeAt, 255'ten büyük değerler döndürebilir, bu nedenle bu bir bayt değildir!
Triynko

Yanıtlar:


21

C # bunu çalıştırıyor

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

İle bir dizi oluşturacak

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

bayt dizisi

Kod 255'ten büyük olan bir karakter için şöyle görünecektir

bayt dizisi

JavaScript'te çok benzer bir davranış istiyorsanız, bunu yapabilirsiniz (v2 biraz daha sağlam bir çözümdür, orijinal sürüm ise yalnızca 0x00 ~ 0xff için çalışacaktır)

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


1
Bunu zaten denedim ama bu bana yukarıdaki C # kodundan farklı bir sonuç veriyor. Bu durumda olduğu gibi, C # kod çıktı bayt dizisi = 72,0,101,0,108,0,108,0,111,0 Her ikisini de eşleştirmek için bir gereksinimim var, böylece çalışmıyor.
55'te shas

2
@shas Öncekini yalnızca Firefox 4'te test ettim. Güncellenen sürüm Firefox 4, Chrome 13 ve IE9'da test edildi.
BrunoLM

41
Dize unicode karakterler içeriyorsa, charCodeAt (i) 'nin> 255 olacağını unutmayın, bu muhtemelen istediğiniz şey değildir.
broofa

23
Evet, bu yanlış. charCodeAt bir bayt döndürmez. 255'ten büyük bir değeri "bayt" adı verilen bir diziye itmenin bir anlamı yoktur; çok yanıltıcı. Bu işlev kodlama yapmaz, sadece karakter kodlarını bir diziye yapıştırır.
Triynko

1
Hiçbir şeyi kodlamadığı için bu cevabın neden doğru olarak işaretlendiğini anlamıyorum.
AB

34

Node.js'de çalışan bir çözüm arıyorsanız, bunu kullanabilirsiniz:

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

3
Bu node.js için ama sanırım soru tarayıcıda çalışan bir çözüm arıyor. Yine de, bu soruya verilen diğer cevapların çoğunun aksine, doğru şekilde çalışıyor, yani +1.
Daniel Cassidy

Bu işe yarıyor, ancak çok daha basit kod, convertString (myString) işlevidir {var myBuffer = new Buffer (myString, 'utf16le'); console.log (myBuffer); myBuffer'ı döndür; }
Philip Rutovitz

16

C # ve Java'nın eşit bayt dizileri ürettiğini düşünüyorum. ASCII olmayan karakterleriniz varsa, fazladan 0 eklemek yeterli değildir. Örneğim birkaç özel karakter içerir:

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

C # 'nin BOM (Bayt Sırası İşaretleri) yerleştirip yerleştirmediğini bilmiyorum, ancak UTF-16 kullanılıyorsa, Java String.getBytesaşağıdaki baytları ekler: 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

Düzenle:

Özel bir karakter eklendi (U + 1D11E) MUSICAL SYMBOL G CLEF (BPM dışında, bu nedenle UTF-16'da sadece 2 bayt değil, 4.

Mevcut JavaScript sürümleri dahili olarak "UCS-2" kullanır, bu nedenle bu sembol 2 normal karakterlik yer kaplar.

Emin değilim ama kullanırken charCodeAt UTF-16'da da kullanılan yedek kod noktalarını tam olarak alıyoruz, bu nedenle BPM olmayan karakterler doğru şekilde işleniyor.

Bu sorun kesinlikle önemsiz değildir. Kullanılan JavaScript sürümlerine ve motorlarına bağlı olabilir. Dolayısıyla, güvenilir çözümler istiyorsanız, şunlara bir göz atmalısınız:


1
Yine de tam bir cevap değil. UTF16, karakterleri temsil etmek için 16 bitlik parçalar kullanan değişken uzunluklu bir kodlamadır. Tek bir karakter, karakter kod değerinin ne kadar büyük olduğuna bağlı olarak 2 bayt veya 4 bayt olarak kodlanacaktır. Bu işlev en fazla 2 bayt yazdığından, tüm unicode karakter kod noktalarını işleyemez ve uzun bir atışla değil, UTF16 kodlamasının tam bir uygulaması değildir.
Triynko

@Triynko, düzenlemem ve testimden sonra, bunun hala tam bir cevap olmadığını düşünüyor musunuz? Cevabınız evet ise cevabınız var mı?
hgoebl

2
@Triynko Yarı haklısınız, ama aslında bu cevap doğru çalışıyor. JavaScript dizeleri aslında Unicode Kod Noktaları dizileri değildir, UTF-16 Kod Birimleri dizileridir. İsme rağmen, charCodeAt0-65535 aralığında bir UTF-16 Kod Birimi döndürür. 2 baytlık aralığın dışındaki karakterler, UTF-16'da olduğu gibi vekil çiftler olarak temsil edilir. (Bu arada, bu, Java ve C # dahil olmak üzere diğer birçok dildeki dizeler için de geçerlidir.)
Daniel Cassidy

Bu arada, (charCode & 0xFF00) >> 8gereksiz, geçiş yapmadan önce maskelemenize gerek yok.
Patrick Roberts

16

2018'deki en kolay yol TextEncoder olmalıdır, ancak döndürülen öğe bayt dizisi değil, Uint8Array'dir. (Ve tüm tarayıcılar bunu desteklemez)

let utf8Encode = new TextEncoder();
utf8Encode.encode("eee")
> Uint8Array [ 101, 101, 101 ]

Bu tuhaf. Utf8Decode ve utf8Encode çalışacağı için farklı değişken adları kullanmanın işe yarayacağını sanmıyorum.
Unihedron

Sen kullanabilirsiniz TextDecoder kod çözme için: new TextDecoder().decode(new TextEncoder().encode(str)) == str.
Fons

İşte destek tabloları TextEncoder: caniuse
Fons

11

UTF-16 Bayt Dizisi

JavaScript, dizeleri tıpkı C # 'ler gibi UTF-16 olarak kodlar UnicodeEncoding, bu nedenle bayt dizileri charCodeAt(), aşağıdaki gibi döndürülen her bayt çiftini kullanarak ve bölerek tam olarak eşleşmelidir :

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

Örneğin:

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

Bununla birlikte, UTF-8 bayt dizisi almak istiyorsanız, baytların kodunu dönüştürmelisiniz.

UTF-8 Bayt Dizisi

Çözüm biraz önemsiz gibi görünüyor, ancak aşağıdaki kodu yüksek trafikli bir üretim ortamında büyük bir başarıyla kullandım ( orijinal kaynak ).

Ayrıca, ilgilenen okuyucu için, PHP gibi diğer diller tarafından bildirilen dizi uzunluklarıyla çalışmama yardımcı olan unicode yardımcılarımı yayınladım .

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

ve bunun tersi nedir?
simbo1905

Ters işlevi "UTF-8 bayt dizisini yerel UTF-16 dizesine dönüştürmek" olarak tanımlardım. Tersini asla üretmedim. Myc env olarak, bir karakter aralığının yerine bir bayt aralığında API çıkışını değiştirmek bu kodu uzaklaştırıldı, sonra kullanılan rünlerini aralıkları ayrıştırmak.
jchook

Bu soru için kabul edilen cevabın bu olmasını öneririm.
LeaveTheCapital

10

@ Hgoebl'ın cevabından esinlenilmiştir. Onun kodu UTF-16 için ve US-ASCII için bir şeye ihtiyacım vardı. İşte US-ASCII, UTF-16 ve UTF-32'yi kapsayan daha eksiksiz bir cevap.

/**@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 değişken uzunluktadır ve kodlamayı kendim yazmam gerektiğinden dahil edilmemiştir. UTF-8 ve UTF-16 değişken uzunluktadır. UTF-8, UTF-16 ve UTF-32, adlarından da anlaşılacağı gibi minimum sayıda bit içerir. Bir UTF-32 karakterinin kod noktası 65 ise, bu, başında 3 0 olduğu anlamına gelir. Ancak UTF-16 için aynı kodda yalnızca 1 önde 0 bulunur. Öte yandan US-ASCII sabit genişlikte 8 bittir, bu da doğrudan baytlara çevrilebileceği anlamına gelir.

String.prototype.charCodeAtmaksimum 2 bayt sayısı döndürür ve UTF-16 ile tam olarak eşleşir. Ancak String.prototype.codePointAt, ECMAScript 6 (Harmony) teklifinin bir parçası olan UTF-32 için gereklidir. CharCodeAt, US-ASCII'nin temsil edebileceğinden daha olası karakter olan 2 bayt döndürdüğünden, işlev stringToAsciiByteArraybu tür durumlarda karakteri ikiye bölmek ve baytlardan birini veya her ikisini birden almak yerine atar.

Karakter kodlaması önemsiz olmadığı için bu cevabın önemsiz olmadığını unutmayın. Ne tür bir bayt dizisi istediğiniz, bu baytların hangi karakter kodlamasını temsil etmesini istediğinize bağlıdır.

javascript, UTF-16 veya UCS-2'yi dahili olarak kullanma seçeneğine sahiptir, ancak UTF-16 gibi davranan yöntemlere sahip olduğundan, herhangi bir tarayıcının neden UCS-2 kullandığını anlamıyorum. Ayrıca bkz .: https://mathiasbynens.be/notes/javascript-encoding

Evet, sorunun 4 yaşında olduğunu biliyorum ama bu cevaba ihtiyacım vardı.


İçin düğümün Tampon sonuçları '02'vardır [ 48, 0, 50, 0 ]nerede olarak stringToUtf16ByteArrayişlev dönünceye [ 0, 48, 0, 50 ]. hangisi doğru?
pkyeck

@pkyeck Yukarıdaki stringToUtf16ByteArray işlevim, BOM olmadan UTF-16 BE döndürüyor. Düğümden verdiğiniz örnek, BOM'suz UTF-16 LE'dir. Big-endian'ın küçük endian'dan daha normal olduğunu düşünmüştüm ama yanlış olabilirdi.
SkySpiral7

2

Cevaba yorum yapamayacağım için Jin Izzraeel'in cevabına dayanırdım.

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

Tarayıcınızda bir Node.js arabelleği kullanmak istiyorsanız bunu kullanabileceğinizi söyleyerek.

https://github.com/feross/buffer

Bu nedenle, Tom Stickel'in itirazı geçerli değil ve cevap gerçekten de geçerli bir cevap.


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

String.prototype.decodeHex = function () {    
    return this.map(e => String.fromCharCode(e)).join('')
};

4
Diğer cevaplardan biri yerine neden bu yaklaşımı seçebileceğini açıklamak için kodla birlikte bir metin sunmanız yararlı olacaktır.
NightOwl888

bu yaklaşım diğerlerinden daha basit ama aynısını yapın, bu yüzden hiçbir şey yazmadım.
Fabio Maciel

encodeHexbayt değil, 16 bitlik bir sayı dizisi döndürür.
Pavlo

0

Yerinde bulduğum en iyi çözüm (büyük olasılıkla kaba olsa da):

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

Yine de bu sorunun bir yıldan fazla süredir burada olduğunu fark ettim.


2
Bu doğru çalışmıyor. Değişken uzunluklu karakter mantığı yanlış, UTF-16'da 8 bitlik karakter yok. İsme rağmen, charCodeAt16 bitlik bir UTF-16 Kod Birimi döndürür, bu nedenle herhangi bir değişken uzunluk mantığına ihtiyacınız yoktur. Sadece charCodeAt'ı çağırabilir, sonucu 8 bitlik iki bayta bölebilir ve bunları çıktı dizisine yerleştirebilirsiniz (soru UTF-16LE'yi sorduğundan en düşük sıralı bayt önce).
Daniel Cassidy

0

Sorunun neredeyse 4 yaşında olduğunu biliyorum, ama benim için sorunsuz bir şekilde çalışan şu:

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

veya yalnızca dizelerle çalışmak istiyorsanız ve Array kullanmıyorsanız, şunları kullanabilirsiniz:

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


2
Bu tür işler, ancak son derece yanıltıcıdır. bytesİçermiyor dizisi UTF-16 kod birimleri dize temsil eden 16 bitlik rakamları içerir, 'bayt'. Neredeyse sorulan soru buydu, ama gerçekten sadece kazayla.
Daniel Cassidy

-1

Burada, @BrunoLM'nin bir String prototip işlevine dönüştürülmüş olarak gönderdiği işlev:

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

İşlevi bu şekilde tanımlarsanız, herhangi bir dizede .getBytes () yöntemini çağırabilirsiniz:

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

31
Bu, referans verdiği cevap gibi hala yanlıştır. charCodeAt bir bayt döndürmez. 255'ten büyük bir değeri "bayt" adı verilen bir diziye itmenin bir anlamı yoktur; çok yanıltıcı. Bu işlev kodlama yapmaz, sadece karakter kodlarını bir diziye yapıştırır. UTF16 kodlamasını gerçekleştirmek için, karakter kodunu incelemeniz, onu 2 bayt veya 4 bayt ile göstermeniz gerekip gerekmediğine karar vermeniz (UTF16 değişken uzunluklu bir kodlama olduğundan) ve ardından her baytı diziye ayrı ayrı yazmanız gerekir.
Triynko

8
Ayrıca, yerel veri türlerinin prototipini değiştirmek kötü bir uygulamadır.
Andrew Lundin

@AndrewLundin, bu ilginç ... kim diyor?
Jerther


-3

Alt çizgiye ihtiyacınız yok, sadece yerleşik haritayı kullanın:

var string = 'Hello World!';

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


1
Bu, dizeyi UTF-16 kod noktaları dizisi olarak temsil eden 16 bitlik bir sayı dizisi döndürür. OP'nin istediği bu değil, ama en azından sizi oraya götürüyor.
Daniel Cassidy
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.