Làm thế nào để chuyển đổi một chuỗi sang Bytearray


90

Làm cách nào để chuyển đổi một chuỗi trong bytearray bằng JavaScript. Đầu ra phải tương đương với mã C # bên dưới.

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

Vì UnicodeEncoding được mặc định là UTF-16 với Little-Endianness.

Chỉnh sửa: Tôi có yêu cầu khớp phía máy khách được tạo bytearray với phía máy khách được tạo ở phía máy chủ bằng cách sử dụng mã C # ở trên.


3
javascript không được biết đến nhiều nhất vì dễ sử dụng với BLOB - tại sao bạn không gửi chuỗi trong JSON?
Marc Gravell

Có lẽ bạn có thể xem ở đây ..
V4Vendetta

2
Chuỗi Javascript là UTF-16, hoặc bạn đã biết điều này chưa?
Kevin

2
Trước hết tại sao bạn cần chuyển đổi điều này trong javascript?
BreakHead

17
Các chuỗi không được mã hóa. Có, bên trong chúng được biểu diễn dưới dạng byte và chúng có mã hóa, nhưng điều đó về cơ bản là vô nghĩa ở cấp kịch bản. Chuỗi là tập hợp các ký tự logic. Để mã hóa một ký tự, bạn phải chọn một cách rõ ràng một lược đồ mã hóa, mà bạn có thể sử dụng để chuyển đổi mỗi mã ký tự thành một chuỗi một hoặc nhiều byte. Các câu trả lời cho câu hỏi này dưới đây là rác, vì chúng gọi charCodeAt và gắn giá trị của nó vào một mảng có tên là "byte". Xin chào! charCodeAt có thể trả về giá trị lớn hơn 255, vì vậy nó không phải là một byte!
Triynko

Câu trả lời:


21

Trong C # chạy cái này

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

Sẽ tạo một mảng với

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

mảng byte

Đối với một ký tự có mã lớn hơn 255, nó sẽ trông như thế này

mảng byte

Nếu bạn muốn một hành vi tương tự trong JavaScript, bạn có thể thực hiện việc này (v2 là giải pháp mạnh mẽ hơn một chút, trong khi phiên bản gốc sẽ chỉ hoạt động với 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(', '));


1
Tôi đã thử điều này nhưng điều này cho tôi kết quả khác với mã C # ở trên. Giống như trường hợp này, mảng byte đầu ra mã C # là = 72,0,101,0,108,0,108,0,111,0 Tôi có yêu cầu phải khớp cả hai để nó không hoạt động.
shas

2
@shas Tôi chỉ thử nghiệm phiên bản trước trên Firefox 4. Phiên bản cập nhật đã được thử nghiệm trên Firefox 4, Chrome 13 và IE9.
BrunoLM

40
Lưu ý rằng nếu chuỗi chứa ký tự unicode, charCodeAt (i) sẽ> 255, có thể không phải là những gì bạn muốn.
broofa

23
Vâng, điều này không chính xác. charCodeAt không trả về một byte. Không có ý nghĩa gì khi đẩy một giá trị lớn hơn 255 vào một mảng được gọi là "byte"; rất dễ gây hiểu lầm. Hàm này hoàn toàn không thực hiện mã hóa, nó chỉ gắn các mã ký tự vào một mảng.
Triynko

1
Tôi không hiểu tại sao câu trả lời này được đánh dấu là đúng vì nó không mã hóa bất kỳ thứ gì.
AB

32

Nếu bạn đang tìm kiếm một giải pháp hoạt động trong node.js, bạn có thể sử dụng điều này:

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
Đây là dành cho node.js nhưng tôi nghĩ câu hỏi đang tìm kiếm giải pháp hoạt động trong trình duyệt. Tuy nhiên, nó hoạt động chính xác, không giống như hầu hết các câu trả lời khác cho câu hỏi này, vì vậy hãy +1.
Daniel Cassidy

Điều này hoạt động nhưng mã đơn giản hơn nhiều là function convertString (myString) {var myBuffer = new Buffer (myString, 'utf16le'); console.log (myBuffer); trả lại myBuffer; }
Philip Rutovitz

16

Tôi cho rằng C # và Java tạo ra các mảng byte bằng nhau. Nếu bạn có các ký tự không phải ASCII, thì không đủ để thêm một số 0. Ví dụ của tôi chứa một vài ký tự đặc biệt:

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

Tôi không biết C # có đặt BOM (Byte Order Marks) không, nhưng nếu sử dụng UTF-16, Java String.getBytessẽ thêm các byte sau: 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

Biên tập:

Đã thêm một ký tự đặc biệt (U + 1D11E) BIỂU TƯỢNG ÂM NHẠC G CLEF (bên ngoài BPM, vì vậy không chỉ lấy 2 byte trong UTF-16 mà còn 4 byte.

Các phiên bản JavaScript hiện tại sử dụng "UCS-2" trong nội bộ, vì vậy ký hiệu này chiếm khoảng cách của 2 ký tự bình thường.

Tôi không chắc nhưng khi sử dụng charCodeAt, có vẻ như chúng tôi nhận được chính xác điểm mã thay thế cũng được sử dụng trong UTF-16, vì vậy các ký tự không phải BPM được xử lý chính xác.

Vấn đề này hoàn toàn không tầm thường. Nó có thể phụ thuộc vào các phiên bản và công cụ JavaScript được sử dụng. Vì vậy, nếu bạn muốn có các giải pháp đáng tin cậy, bạn nên xem:


1
Vẫn không phải là một câu trả lời đầy đủ. UTF16 là một mã hóa có độ dài thay đổi sử dụng các khối 16 bit để biểu diễn các ký tự. Một ký tự duy nhất sẽ được mã hóa thành 2 byte hoặc 4 byte, tùy thuộc vào giá trị mã ký tự lớn như thế nào. Vì hàm này ghi nhiều nhất là 2 byte, nên nó không thể xử lý tất cả các điểm mã ký tự unicode và không phải là một triển khai hoàn chỉnh của mã hóa UTF16, không phải là lâu.
Triynko

@Triynko sau khi tôi chỉnh sửa và kiểm tra, bạn vẫn nghĩ rằng đây không phải là câu trả lời hoàn chỉnh? Nếu có, bạn có câu trả lời không?
hgoebl

2
@Triynko Bạn đúng một nửa, nhưng thực ra câu trả lời này hoạt động chính xác. Chuỗi JavaScript thực ra không phải là chuỗi các Điểm mã Unicode, chúng là chuỗi các Đơn vị mã UTF-16. Bất chấp tên, charCodeAttrả về Đơn vị mã UTF-16, trong phạm vi 0-65535. Các ký tự ngoài phạm vi 2 byte được biểu diễn dưới dạng các cặp thay thế, giống như trong UTF-16. (Bằng cách này, điều này là đúng của chuỗi trong một số ngôn ngữ khác, bao gồm cả Java và C #.)
Daniel Cassidy

Nhân tiện, (charCode & 0xFF00) >> 8là thừa, bạn không cần phải che nó trước khi chuyển.
Patrick Roberts

15

Cách dễ nhất trong năm 2018 nên là TextEncoder nhưng phần tử trả về không phải là mảng byte, nó là Uint8Array. (Và không phải tất cả các trình duyệt đều hỗ trợ nó)

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

Điều này thật đặc biệt. Tôi không cho rằng sử dụng các tên biến khác nhau làm utf8Decode và utf8Encode sẽ hoạt động.
Unihedron

Bạn có thể sử dụng TextDecoder để decode: new TextDecoder().decode(new TextEncoder().encode(str)) == str.
Fons

Đây là bảng hỗ trợ của TextEncoder: caniuse
Fons

11

Mảng byte UTF-16

JavaScript mã hóa các chuỗi dưới dạng UTF-16 , giống như C # UnicodeEncoding, do đó, các mảng byte phải khớp chính xác bằng cách sử dụng charCodeAt()và tách mỗi cặp byte trả về thành 2 byte riêng biệt, như trong:

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

Ví dụ:

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

Tuy nhiên, nếu bạn muốn nhận một mảng UTF-8 byte, bạn phải chuyển mã các byte.

Mảng byte UTF-8

Giải pháp có vẻ hơi không tầm thường, nhưng tôi đã sử dụng mã bên dưới trong môi trường sản xuất có lưu lượng truy cập cao và thành công lớn ( nguồn gốc ).

Ngoài ra, đối với độc giả quan tâm, tôi đã xuất bản trình trợ giúp unicode của mình để giúp tôi làm việc với độ dài chuỗi được báo cáo bởi các ngôn ngữ khác như 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;
}

và nghịch đảo của điều này là gì?
simbo1905

Tôi sẽ mô tả hàm nghịch đảo là "chuyển đổi mảng byte UTF-8 thành chuỗi UTF-16 nguyên bản". Tôi không bao giờ tạo ra nghịch đảo. Trong myc env, tôi đã loại bỏ mã này bằng cách thay đổi đầu ra API thành một dải ký tự thay vì một dải byte, sau đó tôi sử dụng rune để phân tích các dải.
jchook

Tôi đề nghị đây nên là câu trả lời được chấp nhận cho câu hỏi này.
Rời khỏi TheCapital

10

Lấy cảm hứng từ câu trả lời của @ hgoebl. Mã của anh ấy là UTF-16 và tôi cần thứ gì đó cho US-ASCII. Vì vậy, đây là câu trả lời đầy đủ hơn bao gồm US-ASCII, UTF-16 và 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 có độ dài thay đổi và không được bao gồm vì tôi sẽ phải tự viết mã hóa. UTF-8 và UTF-16 có độ dài thay đổi. UTF-8, UTF-16 và UTF-32 có số bit tối thiểu như tên của chúng chỉ ra. Nếu một ký tự UTF-32 có điểm mã là 65 thì điều đó có nghĩa là có 3 số 0 đứng đầu. Nhưng cùng một mã cho UTF-16 chỉ có 1 đầu 0. Mặt khác US-ASCII là 8-bit có độ rộng cố định có nghĩa là nó có thể được dịch trực tiếp sang byte.

String.prototype.charCodeAttrả về số lượng tối đa là 2 byte và khớp chính xác với UTF-16. Tuy nhiên đối với UTF-32String.prototype.codePointAt là cần thiết, là một phần của đề xuất ECMAScript 6 (Hài hòa). Vì charCodeAt trả về 2 byte là nhiều ký tự khả dĩ hơn US-ASCII có thể đại diện, nên hàm stringToAsciiByteArraysẽ ném vào các trường hợp như vậy thay vì chia đôi ký tự và lấy một hoặc cả hai byte.

Lưu ý rằng câu trả lời này là không tầm thường vì mã hóa ký tự là không tầm thường. Loại mảng byte bạn muốn phụ thuộc vào kiểu mã hóa ký tự bạn muốn các byte đó đại diện.

javascript có tùy chọn sử dụng nội bộ UTF-16 hoặc UCS-2 nhưng vì nó có các phương thức hoạt động giống như UTF-16 nên tôi không hiểu tại sao bất kỳ trình duyệt nào cũng sử dụng UCS-2. Cũng thấy: https://mathiasbynens.be/notes/javascript-encoding

Vâng, tôi biết câu hỏi đã có từ 4 năm trước nhưng tôi cần câu trả lời này cho chính mình.


Kết quả Bộ đệm của Node cho '02'[ 48, 0, 50, 0 ]nơi khi stringToUtf16ByteArrayhàm của bạn trả về [ 0, 48, 0, 50 ]. Cái nào đúng?
pkyeck

@pkyeck Hàm stringToUtf16ByteArray của tôi ở trên trả về UTF-16 BE mà không có BOM. Ví dụ bạn đưa ra từ nút là UTF-16 LE không có BOM. Tôi đã nghĩ Big-endian bình thường hơn little-endian nhưng có thể sai.
SkySpiral

2

Vì tôi không thể bình luận về câu trả lời, tôi sẽ xây dựng câu trả lời của 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);

bằng cách nói rằng bạn có thể sử dụng điều này nếu bạn muốn sử dụng bộ đệm Node.js trong trình duyệt của mình.

https://github.com/feross/buffer

Do đó, lời phản đối của Tom Stickel là không có cơ sở, và câu trả lời thực sự là một câu trả lời xác đáng.


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
Sẽ rất hữu ích nếu bạn cung cấp một số văn bản đi kèm với mã để giải thích lý do tại sao một người có thể chọn cách tiếp cận này hơn là một trong các câu trả lời khác.
NightOwl888

cách tiếp cận này đơn giản hơn những cách khác nhưng làm tương tự, đó là lý do tôi đã không viết bất cứ điều gì.
Fabio Maciel

encodeHexsẽ trả về một mảng số 16 bit, không phải byte.
Pavlo

0

Giải pháp tốt nhất mà tôi đưa ra tại chỗ (mặc dù rất có thể là thô thiển) sẽ là:

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

Mặc dù tôi nhận thấy câu hỏi này đã ở đây hơn một năm.


2
Điều này không hoạt động chính xác. Logic ký tự có độ dài thay đổi không chính xác, không có ký tự 8 bit nào trong UTF-16. Mặc dù có tên, charCodeAttrả về Đơn vị mã 16-bit UTF-16, vì vậy bạn không cần bất kỳ logic độ dài thay đổi nào. Bạn chỉ có thể gọi charCodeAt, chia kết quả thành hai byte 8 bit và nhồi chúng vào mảng đầu ra (byte thứ tự thấp nhất đầu tiên vì câu hỏi yêu cầu UTF-16LE).
Daniel Cassidy

0

Tôi biết câu hỏi đã có từ gần 4 năm trước, nhưng đây là điều đã diễn ra suôn sẻ với tôi:

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

hoặc nếu bạn chỉ muốn làm việc với chuỗi và không có Mảng, bạn có thể sử dụng:

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
Loại này hoạt động, nhưng cực kỳ sai lầm. Các bytesmảng không chứa 'byte', nó có chứa số 16-bit, trong đó đại diện cho chuỗi trong UTF-16 đơn vị mã. Đây gần như là những gì câu hỏi đặt ra, nhưng thực sự chỉ là tình cờ.
Daniel Cassidy

-1

Đây là cùng một hàm mà @BrunoLM đã đăng được chuyển đổi thành một hàm nguyên mẫu Chuỗi:

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

Nếu bạn định nghĩa hàm như vậy, thì bạn có thể gọi phương thức .getBytes () trên bất kỳ chuỗi nào:

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

31
Điều này vẫn không chính xác, giống như câu trả lời mà nó tham chiếu. charCodeAt không trả về một byte. Không có ý nghĩa gì khi đẩy một giá trị lớn hơn 255 vào một mảng được gọi là "byte"; rất dễ gây hiểu lầm. Hàm này hoàn toàn không thực hiện mã hóa, nó chỉ gắn các mã ký tự vào một mảng. Để thực hiện mã hóa UTF16, bạn phải kiểm tra mã charcter, quyết định xem bạn sẽ cần biểu diễn nó bằng 2 byte hay 4 byte (vì UTF16 là mã hóa có độ dài thay đổi), sau đó ghi từng byte vào mảng riêng lẻ.
Triynko

8
Ngoài ra, việc sửa đổi nguyên mẫu của các kiểu dữ liệu gốc là một việc làm không tốt.
Andrew Lundin,

@AndrewLundin, đó là bắt giữ ... nói ai?
Jerther


-3

Bạn không cần gạch dưới, chỉ cần sử dụng bản đồ tích hợp sẵn:

var string = 'Hello World!';

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


1
Điều này trả về một mảng các số 16 bit đại diện cho chuỗi dưới dạng một chuỗi các điểm mã UTF-16. Đó không phải là những gì OP yêu cầu, nhưng ít nhất nó giúp bạn có một phần nào đó.
Daniel Cassidy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.