字符编码对照表
字符编码(Charset)决定文字如何映射为字节。本表覆盖开发中常见的 ASCII、UTF-8、GBK、GB2312、Big5、ISO-8859-1 等编码的含义与典型用途,解决乱码与编码选择问题。
原理解析
字符集(charset)是「字符 ↔ 数字编码」的映射表。早期各国各自定义(中文 GBK、Big5,日文 Shift-JIS),互不兼容;Unicode 把所有语言的字符统一编到一个码空间(码点从 U+0000 起),从根本上解决乱码。
UTF-8 是 Unicode 的一种变长编码:ASCII 字符仍占 1 字节,其它字符占 2–4 字节。它向后兼容 ASCII 且自同步,成为 Web 与 Linux 的默认编码。乱码几乎总因「写出的编码」与「读出时声称的编码」不一致。
什么时候该查这张表
这张表在你要给数据加明确编码声明时有用。它列出 ASCII、UTF-8、UTF-16、GBK、Big5、Shift_JIS、ISO-8859-1 等常见编码的字节结构与典型用途。乱码排查的第一件事就是确认两端编码一致。
不该查的情况:你在设计字符集的转换策略或做编码自动探测。GBK 与 UTF-8 之间不是简单的一一映射,GBK 里有大量与 Unicode 不兼容的私有区字符,自动猜编码可能得到看似合理实则错误的结果,这类问题需要专门的检测库。
判断标准:能用 UTF-8 就用 UTF-8,且必须带 BOM 声明或显式声明。历史上出现的 Shift_JIS 与 GBK 混用问题,本质都是「有一方没声明」或「声明了但实际不符」,而不是编码本身不好。
特别注意 UTF-8 没有 BOM 与有 BOM 的差别不是可有可无的:BOM 会被当作正文的第一个字符,导致 JSON 解析失败、文件头出现不可见垃圾、以及字符串比较不相等。这是最典型的「数据看起来没问题但就是不对」。
边界与常见陷阱
UTF-8 BOM 会让 JSON 解析直接失败
带 BOM 的 JSON 喂给 JSON.parse 会抛 Unexpected token 错误,因为 BOM(U+FEFF)不在 JSON 规范的合法起始字符里。写入端(Excel 导出的 CSV 尤其常见)容易带上 BOM,读取端却没剥。防御做法:读取时先检测前三个字节是 EF BB BF 就跳过;写入 JSON/接口响应时不要加 BOM。CSV 另说,多数表格软件靠 BOM 判断编码,此时应当保留。
读写两端编码不一致产生的乱码可以双向传播
典型的双重乱码:UTF-8 文本被按 Latin-1 读一遍再按 UTF-8 写回,中文变成 æ¼ ä¸æ–‡,且这个结果被持久化后已无法自动还原(信息在第一次转换时就丢了,只能靠原始数据恢复)。更隐蔽的是有些转换是「看似可逆」的,往返两次后仍能还原,但中间落库那一刻的字段长度、索引、正则匹配都可能已经出问题。规范做法是全链路只允许 UTF-8,转换只在明确的边界发生。
文件扩展名与实际编码不一致,程序按名猜就会错
.csv 文件可能是 UTF-8、GBK、Shift_JIS 或 UTF-8 with BOM,扩展名不携带任何编码信息。程序里写 if (ext === ’.csv’) useUtf8 一定是错的。可靠顺序是:① 看 BOM(EF BB BF ⇒ UTF-8,FF FE ⇒ UTF-16LE);② 读文件头若干字节做合法性检测(UTF-8 多字节序列结构是否完整);③ 让用户显式选择或上传时探测;④ 最后才用启发式猜测,且必须带置信度。
与相邻内容的分工
要查某个字符长什么样、码点是多少,用站内「ASCII 码」页;本页关注的是编码方案层面的选择与声明。
要理解百分号编码与字符编码的关系(为什么中文是三个 %XX),属于 URL 编码页的范畴,与本页是两个层次。
要处理 HTML 实体与百分号编码的叠加,属于 HTML 实体页的范畴,本页不覆盖。
若目的是排查具体乱码,需要拿到原始字节做检测,通常靠「文件头 + 声明 + 实际渲染」三处对照,单靠本页的对照表不足以定位。
实战片段
要点:① 读文件先剥 BOM,写 JSON 与接口响应绝不能加 BOM,写 CSV 反而要加(让 Excel 识别 UTF-8);② 扩展名不携带编码信息,不能靠 if (ext === ’.csv’) 判断;③ 检测顺序是 BOM → 字节合法性探测 → 用户指定 → 启发式猜测(带置信度);④ 双重乱码一旦落库信息已丢失,只能从原始数据恢复,所以要在入口就阻断;⑤ 全链路只允许 UTF-8,转换只在明确边界发生。
// ① 检测 BOM(读文件前必做)
function stripBom(buf) {
const b = new Uint8Array(buf)
if (b.length >= 3 && b[0] === 0xef && b[1] === 0xbb && b[2] === 0xbf) return b.slice(3)
return b
}
// ② 按 BOM 判断 UTF-16 / UTF-8
function sniffEncoding(buf) {
const b = new Uint8Array(buf)
if (b.length >= 2 && b[0] === 0xff && b[1] === 0xfe) return 'utf-16le'
if (b.length >= 2 && b[0] === 0xfe && b[1] === 0xff) return 'utf-16be'
if (b.length >= 3 && b[0] === 0xef && b[1] === 0xbb && b[2] === 0xbf) return 'utf-8-bom'
return 'unknown' // 未命中就走内容探测或让用户选
}
// ③ 🔴 带 BOM 的 JSON 会让 JSON.parse 直接抛错
const badJson = new TextDecoder('utf-8').decode(
new Uint8Array([0xef, 0xbb, 0xbf, ...new TextEncoder().encode('{"a":1}')]))
try { JSON.parse(badJson) } catch (e) { console.log('抛错:', e.message) } // Unexpected token
// ④ 读取 CSV:先剥 BOM,再按声明/探测的编码解码
import { parse } from 'papaparse'
function readCsv(buf, encoding = 'utf-8') {
const text = new TextDecoder(encoding).decode(stripBom(buf))
return parse(text, { header: true, skipEmptyLines: true })
}
// ⑤ 写入侧:JSON 绝不加 BOM;CSV 加 BOM(让 Excel 正确识别 UTF-8)
const jsonStr = JSON.stringify({ a: 1 })
const csvStr = '' + 'name
age
' // BOM 前置
console.log(jsonStr.charCodeAt(0) === 0xfeff) // false ✅
// ⑥ 检测是否发生双重乱码(出现典型乱码字符)
const mojibake = /[ÃÂæ¼][-]/
console.log(mojibake.test('æ¼ ä¸æ–‡')) // true ⇒ 需从原始数据恢复实践注解
- UTF-8 的 BOM 会让 JSON 解析失败Windows 记事本另存的 UTF-8 文件常带 EF BB BF 前缀,JSON.parse 会直接抛「Unexpected token」;PHP 里则表现为「headers already sent」。做数据文件时应保存为「UTF-8 无 BOM」,服务端解析前也可先剥掉前三个字节。
- MySQL 的 utf8 其实是 utf8mb3,存不了 emoji历史上 MySQL 的 utf8 只覆盖最多 3 字节,四字节字符(emoji、部分生僻字)会被截断或报 Incorrect string value。新库一律用 utf8mb4,并同步确认连接层的 character_set_client 也是 utf8mb4。
- 「编码」与「字符集」是两层,混用会误判根因UTF-8 是编码(字节怎么排),Unicode 是字符集(码点怎么分配)。乱码的真因通常是「写入用 A 编码、读取声称是 B」。排查第一步永远是确认这两端声明是否一致,而不是先改代码。
数据来源
- IANA Character Sets Registry
- The Unicode Standard
- ISO/IEC 8859 series
- GB 18030-2022
- RFC 3629 (UTF-8)
上述编号、名称与定义均来自公开规范,本站不主张对规范数据本身的独创性;本页的编排方式、中文释义与实践注解由本站撰写。
| 编码 | 说明 | 典型用途 |
|---|---|---|
| ASCII | 7 位字符集,覆盖英文字母、数字与基础符号(0-127) | 最古老的基础编码,所有现代编码的兼容子集 |
| UTF-8 | Unicode 的可变长编码,1-4 字节 | Web 默认编码,覆盖全球字符,中文 3 字节 |
| UTF-16 | Unicode 的 2 或 4 字节编码 | Windows/Java/.NET 内部常用,JS 字符串底层 |
| UTF-32 | Unicode 的固定 4 字节编码 | 处理简单但体积大,较少用于传输 |
| ISO-8859-1 | Latin-1,单字节覆盖西欧语言 | HTTP 默认编码,常与 Windows-1252 混淆 |
| Windows-1252 | CP1252,Latin-1 超集,补了 € 等可打印字符 | Windows 西欧默认,浏览器常把 8859-1 当 1252 解析 |
| GBK | 汉字内码扩展,双字节覆盖繁简中文 | 中文 Windows 旧默认,兼容 GB2312 |
| GB2312 | 早期简体中文双字节编码 | 简体中文基础字符集,被 GBK 全面兼容 |
| GB18030 | 国标强制标准,覆盖中日韩及少数民族文字 | 中国政府/金融系统要求,兼容 GBK |
| Big5 | 大五码,繁体中文双字节编码 | 港台地区传统中文编码 |
| Shift-JIS | 日文编码,单/双字节混合 | 日本 Windows 默认日文编码 |
| EUC-KR | 韩文编码,双字节 | 韩国传统韩文编码 |
常见问题
UTF-8 和 GBK 有什么区别?
UTF-8 是 Unicode 的可变长编码,一套字符集覆盖全球语言,是 Web 事实标准;GBK 是中文双字节编码,只覆盖中英文,体积更小但无法表示其他语言。现代项目应统一使用 UTF-8 以避免乱码。
为什么会出现乱码(mojibake)?
当文本以 A 编码保存、却用 B 编码解读时就会出现乱码。最常见是中文以 GBK 保存却被当成 UTF-8(或反之)读取。确保文件、数据库、HTTP 头(charset=utf-8)三方编码一致即可避免。
ISO-8859-1 和 Windows-1252 一样吗?
不一样但常被混用。8859-1 在 0x80-0x9F 是控制字符;1252 在这段放了大量可打印字符(如 €、智能引号)。浏览器在声明 8859-1 时实际常按 1252 解析,因此用 1252 更明确。
emoji 用哪种编码?
emoji 属于 Unicode,UTF-8 下通常占 4 字节(部分新 emoji 更多),UTF-16 下用代理对(surrogate pair)表示。只要系统用 UTF-8,emoji 就能正确存储与显示。