JavaScript TextEncoder和TextDecoder处理UTF-8编码转换
在 Web 开发中处理文本数据时,JavaScript 内部使用 UTF-16 编码(即 String 类型),而网络传输或文件存储通常更倾向于使用 UTF-8 编码(即 Uint8Array 字节数组)。为了在这两种格式之间高效转换,现代浏览器和 Node.js 环境提供了 TextEncoder 和 TextDecoder 这两个原生 API。它们无需引入外部库,即可实现快速、准确的编码转换。
一、 理解核心概念
在开始操作之前,需要明确两个核心概念的区别:
- String (UTF-16):JavaScript 中的字符串类型,每个字符通常占用 2 个字节。
- Uint8Array (UTF-8):8 位无符号整数数组,代表字节流,常用于 File API、Fetch API 或 WebSocket 数据传输。
TextEncoder 负责将字符串转换为字节流,而 TextDecoder 负责将字节流还原为字符串。
二、 将字符串转换为 UTF-8 字节流
当你需要向服务器发送数据或写入二进制文件时,必须先将字符串编码为字节流。
-
创建 一个
TextEncoder实例。- 该对象是全局可用的,直接使用
new关键字调用即可。 - 默认编码始终为
utf-8,无需手动指定。
- 该对象是全局可用的,直接使用
-
调用
encode()方法并传入目标字符串。- 该方法接收一个普通的 JavaScript 字符串。
- 返回一个包含 UTF-8 字节的
Uint8Array对象。
-
验证 输出结果。
- 可以通过查看数组的
length或具体的字节值来确认转换是否正确。例如,英文字符通常占 1 个字节,中文字符通常占 3 个字节。
- 可以通过查看数组的
以下代码演示了将中文字符串编码的过程:
// 1. 创建编码器实例
const encoder = new TextEncoder();
// 2. 定义待转换的字符串
const originalString = "你好,世界!Hello World";
// 3. 执行编码操作
const utf8Bytes = encoder.encode(originalString);
// 4. 查看结果
console.log(utf8Bytes);
// 输出: Uint8Array(26) [228, 189, 160, 229, 165, 189, 239, 188, 140, 228, 184, 150, 231, 149, 140, 239, 188, 129, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
console.log(`字节长度: ${utf8Bytes.length}`);
// 输出: 字节长度: 26
三、 将 UTF-8 字节流还原为字符串
当从服务器接收数据(如 ArrayBuffer)或读取二进制文件时,需要将字节流解码回人类可读的字符串。
-
获取 字节流数据。
- 确保你的数据是
ArrayBuffer、TypedArray(如Uint8Array)或DataView类型。
- 确保你的数据是
-
创建 一个
TextDecoder实例。- 构造函数可以接受一个标签字符串(如
utf-8、gbk),虽然默认也是utf-8,但显式指定可以增强代码可读性。
- 构造函数可以接受一个标签字符串(如
-
调用
decode()方法并传入字节流。- 该方法会返回解码后的 JavaScript 字符串。
以下代码演示了将上一步生成的字节数组还原的过程:
// 假设这是从网络或文件读取到的 Uint8Array 数据
const savedBytes = new Uint8Array([228, 189, 160, 229, 165, 189, 239, 188, 140]);
// 1. 创建解码器实例
const decoder = new TextDecoder('utf-8');
// 2. 执行解码操作
const decodedString = decoder.decode(savedBytes);
// 3. 查看结果
console.log(decodedString);
// 输出: "你好,"
四、 处理流式数据与错误处理
在实际开发中,数据可能是分块到达的(例如视频流或大文件下载)。TextEncoder 和 TextDecoder 支持流式处理,这在处理大量数据时能节省内存并提高效率。
1. 使用 TextDecoder 处理流式数据
解码流式数据时,关键在于 decode() 方法的第二个参数 stream。
- 初始化 解码器。
- 循环接收 数据块。
- 调用
decode(chunk, { stream: true })。- 将
{ stream: true }传入,告诉解码器当前数据块后面可能还有数据,避免将不完整的多字节字符(如被切断的汉字)直接替换为替换字符(``)。
- 将
- 结束 流处理。
- 当所有数据块处理完毕后,调用 一次
decode()(不带参数或传入空数组)以刷新内部缓冲区并输出剩余字符。
- 当所有数据块处理完毕后,调用 一次
const streamDecoder = new TextDecoder('utf-8');
const chunks = [
new Uint8Array([228, 189]), // 不完整的汉字 "你" 的前两个字节
new Uint8Array([160, 229]), // "你" 的最后一个字节 + "好" 的前两个字节
new Uint8Array([165, 189]) // "好" 的最后一个字节
];
let result = '';
// 处理中间数据块
chunks.forEach((chunk, index) => {
const isLast = index === chunks.length - 1;
// 如果不是最后一个块,stream 设为 true
result += streamDecoder.decode(chunk, { stream: !isLast });
});
console.log(result); // 输出: "你好"
2. 处理解码错误
如果接收到的字节流不符合 UTF-8 规范(例如数据损坏),默认行为是用替换字符 `填充。如果需要严格的错误检查,可以配置fatal` 选项。
- 创建
TextDecoder时传入{ fatal: true }。 - 包裹
decode()调用在try...catch块中。 - 捕获
TypeError异常。- 一旦遇到无效字节流,解码器会抛出错误,而不是返回乱码。
const strictDecoder = new TextDecoder('utf-8', { fatal: true });
const corruptData = new Uint8Array([0xFF, 0xFF]); // 无效的 UTF-8 序列
try {
console.log(strictDecoder.decode(corruptData));
} catch (e) {
console.error("解码失败,数据包含非法字节序列");
}
五、 编码与解码的核心属性对比
为了更清晰地理解这两个 API 的功能差异,请参考下表。
| 特性 | TextEncoder | TextDecoder |
|---|---|---|
| 主要功能 | 将字符串转换为字节流 | 将字节流转换为字符串 |
| 输入类型 | String |
ArrayBuffer, TypedArray 等 |
| 输出类型 | Uint8Array |
String |
| 支持编码 | 仅支持 UTF-8 | 支持 UTF-8, UTF-16LE, ISO-8859-2 等多种 |
| 流式处理 | 不支持(需手动分块处理) | 支持(通过 stream 参数) |
| 常见场景 | 发送网络请求、写入文件 | 读取服务器响应、读取文件 |
六、 数据转换流程图
以下流程图展示了字符串与字节流在不同场景下的转换路径,帮助理解整个生命周期。
七、 实战场景:读取并解析用户上传的文本文件
假设你允许用户上传一个 .txt 文件,并希望在网页中显示其内容。
-
监听 文件输入框的
change事件。- 获取用户选择的文件对象 (
File对象)。
- 获取用户选择的文件对象 (
-
调用
file.arrayBuffer()方法。File对象继承自Blob,可以使用该方法将文件内容读取为ArrayBuffer。
-
实例化
TextDecoder。 -
解码
ArrayBuffer。 -
显示 解码后的文本到页面。
// 假设 HTML 中有一个 <input type="file" id="fileInput">
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', async (event) => {
const file = event.target.files[0];
if (!file) return;
// 1. 读取文件为 ArrayBuffer
const buffer = await file.arrayBuffer();
// 2. 创建解码器并解码
const decoder = new TextDecoder('utf-8');
const text = decoder.decode(buffer);
// 3. 输出内容
console.log("文件内容:", text);
});
通过以上步骤,你就可以在无需任何第三方库的情况下,利用原生 JavaScript API 高效地处理各种文本编码转换任务。

暂无评论,快来抢沙发吧!