如何将字符串转换为Bytearray


90

如何使用JavaScript转换字节数组中的字符串。输出应等效于以下C#代码。

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

由于UnicodeEncoding默认为带有Little-Endianness的UTF-16。

编辑:我需要使用上面的C#代码将字节数组生成的客户端与服务器端生成的字节数组进行匹配。


3
javascript并不是很容易与BLOB一起使用而闻名-为什么您不只使用JSON发送字符串?
Marc Gravell

也许您可以在这里看看..
V4仇杀队

2
Javascript字符串为UTF-16,还是您已经知道这一点?
凯文

2
首先,为什么需要在javascript中进行转换?
BreakHead 2011年

17
字符串未编码。是的,它们在内部以字节表示,并且具有编码,但是在脚本级别基本上没有意义。字符串是字符的逻辑集合。要对字符进行编码,必须显式选择一种编码方案,该方案可用于将每个字符代码转换为一个或多个字节的序列。下面这个问题的答案是垃圾,因为它们调用charCodeAt并将其值粘贴到名为“ bytes”的数组中。你好!charCodeAt可以返回大于255的值,因此它不是一个字节!
Triynko

Answers:


21

在C#中运行

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

将创建一个数组

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

byte array

对于代码大于255的字符,它将如下所示

byte array

如果您希望在JavaScript中实现非常相似的行为,则可以执行此操作(v2是更强大的解决方案,而原始版本仅适用于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
我已经尝试过了,但这给了我与上面的C#代码不同的结果。像这种情况,C#代码输出字节数组为= 72,0,101,0,108,0,108,0,111,0我需要将两者都匹配,以至于无法正常工作。
SHA的

2
@shas我仅在Firefox 4上测试了以前的版本。更新版本在Firefox 4,Chrome 13和IE9上进行了测试。
BrunoLM 2011年

40
请注意,如果字符串包含Unicode字符,则charCodeAt(i)将大于255,这可能不是您想要的。
broofa

23
是的,这是不正确的。charCodeAt不返回字节。将大于255的值推入称为“ bytes”的数组中没有任何意义。非常误导。该函数根本不执行编码,只是将字符代码粘贴到数组中。
Triynko

1
我不明白为什么这个答案被标记为正确的,因为它没有编码任何东西。
2015年

32

如果您正在寻找可以在node.js中使用的解决方案,则可以使用以下方法:

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
这是针对node.js的,但是我认为问题是在寻找一种可在浏览器中使用的解决方案。不过,它确实可以正常运行,与该问题的其他答案不同,因此+1。
丹尼尔·卡西迪

这行得通,但更简单的代码是函数convertString(myString){var myBuffer = new Buffer(myString,'utf16le'); console.log(myBuffer); 返回myBuffer; }
菲利普·鲁托维兹

16

我想C#和Java产生相等的字节数组。如果您使用非ASCII字符,则仅添加一个0是不够的。我的示例包含一些特殊字符:

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#是否放置BOM(字节顺序标记),但是如果使用UTF-16,JavaString.getBytes将添加以下字节: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

编辑:

添加了特殊字符(U + 1D11E)MUSICAL SYMBOL G CLEF(BPM之外),因此在UTF-16中不仅占用2个字节,而且占用4个字节。

当前的JavaScript版本内部使用“ UCS-2”,因此此符号占用2个普通字符的空间。

我不确定,但使用时 charCodeAt它,我们似乎可以确切地获得UTF-16中也使用的替代代码点,因此可以正确处理非BPM字符。

这个问题绝对是不容易的。它可能取决于所使用的JavaScript版本和引擎。因此,如果您需要可靠的解决方案,则应该看看:


1
仍然不是一个完整的答案。UTF16是一种可变长度编码,使用16位块表示字符。单个字符将被编码为2个字节或4个字节,具体取决于字符代码值的大小。由于此函数最多写入2个字节,因此它不能处理所有unicode字符代码点,并且不是一个完整的UTF16编码实现,也不是很容易。
Triynko

@Triynko经过我的编辑和测试后,您仍然认为这不是完整的答案吗?如果是,您有答案吗?
hgoebl 2013年

2
@Triynko您说得对,但实际上这个答案确实可以正常工作。JavaScript字符串实际上不是Unicode代码点的序列,它们是UTF-16代码单元的序列。尽管有名称,但charCodeAt返回的是UTF-16代码单元,范围为0-65535。就像UTF-16中一样,2字节范围以外的字符表示为代理对。(顺便说一句,其他几种语言的字符串也是如此,包括Java和C#。)
Daniel Cassidy

顺便说一句,这(charCode & 0xFF00) >> 8是多余的,您无需在转移之前将其屏蔽。
帕特里克·罗伯茨

15

2018年最简单的方法应该是TextEncoder,但是返回的元素不是字节数组,而是Uint8Array。(并非所有浏览器都支持它)

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

这很奇怪。我不认为使用不同的变量名称,因为utf8Decode和utf8Encode可以工作。
Unihedron

您可以使用TextDecoder解码:new TextDecoder().decode(new TextEncoder().encode(str)) == str
Fons

以下是支持列表TextEncodercaniuse
Fons

11

UTF-16字节数组

JavaScript将字符串编码为UTF-16,就像C#一样UnicodeEncoding,因此字节数组应使用完全匹配charCodeAt(),并将每个返回的字节对拆分为2个单独的字节,如下所示:

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

例如:

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

但是,如果要获取UTF-8字节数组,则必须对字节进行转码。

UTF-8字节数组

该解决方案听起来有些微不足道,但是我在高流量的生产环境中使用了以下代码,并取得了巨大的成功(原始来源)。

此外,对于感兴趣的读者,我发布了unicode帮助器,可帮助我处理其他语言(例如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;
}

与此相反的是什么?
simbo1905 '19

我将反函数描述为“将UTF-8字节数组转换为本地UTF-16字符串”。我从来没有产生逆。在myc env中,我通过将API输出更改为字符范围而不是字节范围来删除了此代码,然后使用符文来解析范围。
jchook

我建议这应该是该问题的公认答案。
1919年

10

受到@hgoebl的回答的启发。他的代码用于UTF-16,而我需要一些用于US-ASCII的代码。因此,这里有一个更完整的答案,涵盖了US-ASCII,UTF-16和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是可变长度的,不包含在内,因为我必须自己编写编码。UTF-8和UTF-16是可变长度的。顾名思义,UTF-8,UTF-16和UTF-32的位数最少。如果UTF-32字符的代码点为65,则意味着有3个前导0。但是用于UTF-16的相同代码只有1个前导0。另一方面,US-ASCII是固定宽度的8位,这意味着它可以直接转换为字节。

String.prototype.charCodeAt返回最大2个字节的数目,并完全匹配UTF-16。但是,对于UTF-32 String.prototype.codePointAt,这是ECMAScript 6(Harmony)建议的一部分。因为charCodeAt返回2个字节,这比US-ASCII可以表示的字符更多,所以stringToAsciiByteArray在这种情况下,该函数将抛出该字符,而不是将字符分成两半并占用一个或两个字节。

注意,这个答案是不平凡的,因为字符编码是不平凡的。您想要哪种字节数组取决于您希望这些字节表示哪种字符编码。

javascript可以选择在内部使用UTF-16或UCS-2,但是由于它具有的行为类似于UTF-16,因此我看不到为什么任何浏览器都会使用UCS-2。另请参阅:https : //mathiasbynens.be/notes/javascript-encoding

是的,我知道这个问题已有4岁,但我自己需要这个答案。


函数返回的Node的Buffer结果'02'[ 48, 0, 50, 0 ]哪里。哪一个是正确的?stringToUtf16ByteArray[ 0, 48, 0, 50 ]
pkyeck

@pkyeck我上面的stringToUtf16ByteArray函数返回没有BOM的UTF-16 BE。您从节点给出的示例是不带BOM的UTF-16 LE。我以为大端比小端更正常,但可能是错误的。
SkySpiral7 '18

2

由于我无法对答案发表评论,因此我将基于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);

说如果您想在浏览器中使用Node.js缓冲区,则可以使用它。

https://github.com/feross/buffer

因此,汤姆·斯蒂克尔(Tom Stickel)的反对是无效的,答案的确是有效的答案。


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
如果您提供一些文本以及代码来解释为什么有人选择这种方法而不是其他答案,这将很有帮助。
NightOwl888

这种方法比其他方法更简单,但是也一样,这就是我什么都没写的原因。
Fabio Maciel

encodeHex将返回一个16位数字数组,而不是字节。
帕夫洛

0

我当场想出的最佳解决方案(尽管最有可能是粗略的)是:

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

尽管我注意到这个问题已经存在了一年多。


2
这不能正常工作。可变长度字符逻辑不正确,UTF-16中没有8位字符。尽管有名称,但charCodeAt返回的是16位UTF-16代码单元,因此您不需要任何可变长度逻辑。您可以只调用charCodeAt,将结果分成两个8位字节,然后将它们填充到输出数组中(因为问题要求输入UTF-16LE,因此最低位在前)。
丹尼尔·卡西迪

0

我知道这个问题将近4岁了,但这对我来说很顺利:

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

或者,如果您只想使用字符串而不使用数组,则可以使用:

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
这种作品,但极具误导性。该bytes数组不包含“字节”,它包含16位数字,以UTF-16代码单位表示字符串。这几乎是问题所要的,但实际上只是偶然。
丹尼尔·卡西迪

-1

这与@BrunoLM发布的转换为String原型函数的函数相同:

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

如果您这样定义函数,则可以在任何字符串上调用.getBytes()方法:

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

31
就像它引用的答案一样,这仍然是不正确的。charCodeAt不返回字节。将大于255的值推入称为“字节”的数组中没有任何意义。非常误导。该函数根本不执行编码,只是将字符代码粘贴到数组中。要执行UTF16编码,您必须检查字符代码,确定是否需要用2个字节或4个字节来表示它(因为UTF16是可变长度编码),然后将每个字节分别写入数组。
Triynko

8
同样,修改本地数据类型的原型也是一种不好的做法。
Andrew Lundin

@AndrewLundin,这很有趣...说谁?
Jerther


-3

您不需要下划线,只需使用内置地图即可:

var string = 'Hello World!';

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


1
这将返回一个16位数字的数组,该字符串将字符串表示为UTF-16代码点的序列。这不是OP所要求的,但是至少它可以使您一臂之力。
丹尼尔·卡西迪
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.