如何将字符串转换为字节数组

IT技术 javascript
2021-02-04 10:06:53

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

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

因为 UnicodeEncoding 默认是 UTF-16 和 Little-Endianness。

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

6个回答

如果您正在寻找适用于 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);
由于新的 Buffer 已被弃用,所以应该使用 from: var buffer = Buffer.from(str, 'utf16le');
2021-03-20 10:06:53
这是针对 node.js 但我认为问题是寻找在浏览器中工作的解决方案。尽管如此,它确实可以正常工作,这与该问题的大多数其他答案不同,所以 +1。
2021-03-30 10:06:53
这有效但更简单的代码是 function convertString(myString) { var myBuffer = new Buffer(myString, 'utf16le'); 控制台日志(我的缓冲区);返回我的缓冲区;}
2021-04-05 10:06:53
截至 2021 年 11 月 5 日,new Buffer失败,因为Buffer未在 Chrome 浏览器中定义
2021-04-05 10:06:53

在 C# 中运行这个

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

将创建一个数组

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

字节数组

对于代码大于 255 的字符,它看起来像这样

字节数组

如果你想在 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(', '));

@shas 我只在 Firefox 4 上测试了以前的版本。更新版本在 Firefox 4、Chrome 13 和 IE9 上测试。
2021-03-16 10:06:53
请注意,如果字符串包含 unicode 字符,则 charCodeAt(i) 将 > 255,这可能不是您想要的。
2021-04-01 10:06:53
是的,这是不正确的。charCodeAt 不返回字节。将大于 255 的值推送到名为“bytes”的数组中是没有意义的;非常误导。该函数根本不执行编码,它只是将字符代码粘贴到一个数组中。
2021-04-10 10:06:53
我已经尝试过这个,但这给了我与上面的 C# 代码不同的结果。就像在这种情况下,C# 代码输出字节数组是 = 72,0,101,0,108,0,108,0,111,0 我需要匹配两者,所以这不起作用。
2021-04-11 10:06:53
我不明白为什么这个答案被标记为正确,因为它没有编码任何东西。
2021-04-11 10:06:53

2018更新- 2018年最简单的方法应该是TextEncoder

let utf8Encode = new TextEncoder();
utf8Encode.encode("abc")
// Uint8Array [ 97, 98, 99 ]

注意事项- 返回的元素是 a Uint8Array并非所有浏览器都支持它

这是奇特的。我不认为使用不同的变量名作为 utf8Decode 和 utf8Encode 会起作用。
2021-03-20 10:06:53
您可以使用TextDecoder解码:new TextDecoder().decode(new TextEncoder().encode(str)) == str.
2021-03-26 10:06:53
以下是支持表TextEncodercaniuse
2021-04-09 10:06:53

我想 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 版本和引擎。所以如果你想要可靠的解决方案,你应该看看:

@Triynko 你说对了一半,但实际上这个答案确实有效。JavaScript 字符串实际上不是 Unicode 代码点序列,而是 UTF-16 代码单元序列。尽管有名称,但charCodeAt返回范围为 0-65535 的 UTF-16 代码单元。2 字节范围之外的字符表示为代理对,就像在 UTF-16 中一样。(顺便说一句,这适用于其他几种语言中的字符串,包括 Java 和 C#。)
2021-03-13 10:06:53
@Triynko 经过我的编辑和测试,您仍然认为这不是完整的答案吗?如果是,你有答案吗?
2021-03-19 10:06:53
仍然没有一个完整的答案。UTF16 是一种可变长度编码,它使用 16 位块来表示字符。单个字符将被编码为 2 个字节或 4 个字节,具体取决于字符代码值的大小。由于此函数最多写入2个字节,因此无法处理所有unicode字符码位,也不是UTF16编码的完整实现,远非如此。
2021-03-22 10:06:53
顺便说一句,(charCode & 0xFF00) >> 8是多余的,你不需要在转移之前屏蔽它。
2021-04-05 10:06:53

UTF-16 字节数组

JavaScript 将字符串编码为UTF-16,就像 C# 一样UnicodeEncoding,因此字节数组应该完全匹配 using 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;
}
我建议这应该是这个问题的公认答案。
2021-03-15 10:06:53
我会将逆函数描述为“将 UTF-8 字节数组转换为原生 UTF-16 字符串”。我从来没有产生相反的结果。在 myc env 中,我通过将 API 输出更改为字符范围而不是字节范围来删除此代码,然后我使用符文来解析范围。
2021-03-26 10:06:53
与此相反的是什么?
2021-04-07 10:06:53