TextEncoder.encodeInto()

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since January 2020.

TextEncoder.encodeInto() 方法接受一个要编码的字符串和一个目标 Uint8Array,将生成的 UTF-8 编码的文本放入目标数组中,并返回一个指示编码进度的字典对象。这相比于旧的 encode() 方法性能更高——尤其是当目标缓冲区是 WASM 堆视图时。

语法

js
encodeInto(string, uint8Array)

参数

string

一个字符串,包含将要编码的文本。

uint8Array

一个 Uint8Array 对象实例,用于将生成的 UTF-8 编码的文本放入其中。

返回值

一个对象,包含两个参数。

read

已经从源字符串中转换为 UTF-8 的,使用 UTF-16 编码的码元数。如果 uint8Array 没有足够的空间,则此值可能小于 string.length

written

在目标 Uint8Array 中修改的字节数。写入的字节确保形成完整的 UTF-8 字节序列。

编码到特定的位置

encoder.encodeInto() 总是将它输出的结果放在数组的开头。然而,有时使输出从一个指定的索引开始很有用。解决方案是:TypedArray.prototype.subarray()

js
const encoder = new TextEncoder();

function encodeIntoAtPosition(string, u8array, position) {
  return encoder.encodeInto(
    string,
    position ? u8array.subarray(position | 0) : u8array,
  );
}

const u8array = new Uint8Array(8);
encodeIntoAtPosition("hello", u8array, 2);
console.log(u8array.join()); // 0,0,104,101,108,108,111,0

缓冲区大小

转换一个 JavaScript 字符串 s 到 UTF-8 序列时,完全转换输出所需的空间从不会小于 s.length 个字节,也从不会大于 s.length * 3 字节。如果预计输出到缓冲区(通常是在 WASM 堆中)的生命周期很短,则可以为输出分配 s.length * 3 字节的空间,这样就能保证在第一次尝试中转换整个字符串。注意,s.length * 3 的情况是很少见的,因为很少有字符串中一定包含扩展之后为 3 个字节的字符。长文本的长度将不太可能超过 s.length * 2 个字节。因此,更乐观的方法可能是分配 s.length * 2 + 5个字节的空间,并且在极少数的情况下(乐观分配的方法出现错误)进行重新分配。

如果预计输出的生命周期很长,则可以计算最小分配空间为 roundUpToBucketSize(s.length),最大分配空间为 s.length * 3,并且选择一个阈值 t(作为内存使用和速度之间的折衷方案),使得 roundUpToBucketSize(s.length) + t >= s.length * 3 时,分配 s.length * 3 的空间。否则,首先为 roundUpToBucketSize(s.length) 分配空间并进行转换。如果返回的字典对象中的 read 的大小为 s.length,则表示转换完成。如果不是,将目标的缓冲区的大小重新分配为 written + (s.length - read) * 3,并从字符串 s 中的索引 read 处开始取子字符串,并从目标缓冲区的索引 written 处开始取子缓冲区进行转换。

上述 roundUpToBucketSize() 函数,用于将空间向上舍入到分配器的桶大小。例如,如果你的 Wasm 分配器使用 2 的幂次方桶,则 roundUpToBucketSize() 应该返回该参数(如果它是 2 的幂次方的话,否则返回下一个 2 的幂次方)。如果 Wasm 分配器的行为未知,则 roundUpToBucketSize() 应为恒等函数。

如果你分配器的行为未知,但是你可能想要进行多达两次重新分配,并使得第一次重分配的步骤将剩余未转换的长度乘以 2 而不是 3。然而,在这种情况下,将已经写入缓冲区的长度乘以 2 通常是没有意义的,因为在这种情况下,如果发生第二次重分配,与原始长度乘 3 相比,它将总是过度分配。上述建议假设你不需要为 0 终止符分配空间。也就是说,在 WASM 方面,你正在使用 Rust 字符串或者一个不以 0 终止的 C++ 类。如果你正在使用 C++ std::string 标准库,即使逻辑长度已经向你展示,当计算向上舍入到分配器的桶大小的时候,你还是需要去考虑额外的终止符字节。请参阅下一节有关 C 字符串的内容。

没有 0 终止符

如果输入字符串在输入中包含字符 U+0000,encodeInto() 将在输出中写入一个 0x00 字节。encodeInto() 不会在逻辑输出后写入 C 风格的 0x00 标记字节。

如果你的 Wasm 程序使用 C 字符串,那么你有职责去写 0x00 的标记,并且如果你的 JavaScript 字符串包含 U+0000,则无法阻止 Wasm 程序看到逻辑截断的字符串。观察:

js
const encoder = new TextEncoder();

function encodeIntoWithSentinel(string, u8array, position) {
  const stats = encoder.encodeInto(
    string,
    position ? u8array.subarray(position | 0) : u8array,
  );
  if (stats.written < u8array.length) u8array[stats.written] = 0; // append null if room
  return stats;
}

示例

html
<p class="source">This is a sample paragraph.</p>
<p class="result"></p>
js
const sourcePara = document.querySelector(".source");
const resultPara = document.querySelector(".result");
const string = sourcePara.textContent;

const textEncoder = new TextEncoder();
const utf8 = new Uint8Array(string.length);

const encodedResults = textEncoder.encodeInto(string, utf8);
resultPara.textContent +=
  `Bytes read: ${encodedResults.read}` +
  ` | Bytes written: ${encodedResults.written}` +
  ` | Encoded result: ${utf8}`;

规范

Specification
Encoding
# ref-for-dom-textencoder-encodeinto①

浏览器兼容性

Report problems with this compatibility data on GitHub
desktopmobileserver
Chrome
Edge
Firefox
Opera
Safari
Chrome Android
Firefox for Android
Opera Android
Safari on iOS
Samsung Internet
WebView Android
WebView on iOS
Deno
Node.js
encodeInto

Legend

Tip: you can click/tap on a cell for more information.

Full support
Full support

参见