DevToolBox免费
博客

ASCII vs Unicode vs UTF-8 编码详解

10 分钟阅读作者 DevToolBox

字符编码是所有数字文本的基础。无论你是在调试乱码邮件、修复数据库排序规则,还是搞清楚为什么 JSON 文件在某些字符上出错,理解 ASCII、Unicode 和 UTF-8 都是必不可少的。本综合指南解释了计算机中文本的表示方式、从 ASCII 到 Unicode 的演进过程,以及为什么 UTF-8 编码成为了现代网络的通用标准。

使用我们的二进制-文本转换工具进行转换 →

1. ASCII — 7 位的基石

ASCII(美国信息交换标准代码)于 1963 年发布,使用 7 位表示 128 个字符(0–127)。包括 33 个控制字符(0–31 和 127)和 95 个可打印字符(32–126)。

ASCII 专为英文文本和电传打字机设计。每个字符占一个字节,高位(第 7 位)未使用或用于奇偶校验。

95 个可打印字符包括大写字母(A–Z)、小写字母(a–z)、数字(0–9)和 33 个标点/符号字符。控制字符包括 NUL(0)、TAB(9)、LF(10)、CR(13)和 ESC(27)。

十进制十六进制字符说明
000NULNull character
909TABHorizontal tab
100ALFLine feed (newline)
130DCRCarriage return
271BESCEscape
3220(space)Space
48300Digit zero
57399Digit nine
6541AUppercase A
905AZUppercase Z
9761aLowercase a
1227AzLowercase z
1277FDELDelete

2. 扩展 ASCII — 代码页混战

由于 ASCII 只使用 7 位,一个字节中第 8 位(值 128–255)是空闲的。不同系统用这个范围表示不同字符,产生了代码页——不兼容的 ASCII 扩展。

ISO 8859-1(Latin-1)添加了西欧字符,如 é、ü、ñ 和 å。它是 HTTP/1.1 和早期 HTML 的默认编码。Windows-1252 是微软对 ISO 8859-1 的超集,在 128–159 范围内添加了弯引号(“ ”)、长破折号(—)和欧元符号(€)等字符。

根本问题是:字节 0xE9 在 Latin-1 中表示 é,在 Windows-1251(西里尔文)中表示 щ,在 Windows-1252 中表示 é。如果不知道使用的是哪个代码页,就无法正确解码文本。这就是为什么"乱码"在早期互联网上如此猖獗。

其他著名的代码页包括 ISO 8859-5(西里尔文)、ISO 8859-15(Latin-9,用欧元符号替代了 Latin-1)、Shift_JIS 和 EUC-JP(日文)、Big5(繁体中文)以及 GB2312/GBK(简体中文)。

# Same byte, different interpretations:
Byte: 0xE9

Latin-1 (ISO 8859-1):  é  (LATIN SMALL LETTER E WITH ACUTE)
Windows-1251 (Cyrillic): щ  (CYRILLIC SMALL LETTER SHCHA)
Windows-874 (Thai):     ้  (THAI CHARACTER MAI THO)

# This is why encoding metadata is critical!

3. Unicode — 统一字符集

Unicode 是一个通用字符集,为每种文字中的每个字符分配一个唯一的码位。码位写成 U+ 后跟 4–6 个十六进制数字(如 U+0041 = A,U+4E16 = 世,U+1F600 = 😀)。

Unicode 目前定义了超过 154,000 个字符,涵盖 168 种文字,从拉丁文和西里尔文到埃及象形文字和表情符号。最大码位为 U+10FFFF,理论上限为 1,114,112 个码位。

Unicode 被组织为 17 个<strong>平面</strong>,每个平面包含 65,536 个码位:

平面名称码位范围内容
0Basic Multilingual Plane (BMP)U+0000 - U+FFFFLatin, Cyrillic, Greek, CJK, Arabic, Hebrew, most symbols
1Supplementary Multilingual Plane (SMP)U+10000 - U+1FFFFEmoji, historic scripts, musical notation, math symbols
2Supplementary Ideographic Plane (SIP)U+20000 - U+2FFFFRare CJK ideographs
3-13UnassignedU+30000 - U+DFFFFReserved for future use
14Supplementary Special-purpose Plane (SSP)U+E0000 - U+EFFFFTag characters, variation selectors
15-16Private Use AreasU+F0000 - U+10FFFFCustom characters (not standardized)

关键区别:Unicode 是一个字符集(将数字映射到字符),不是编码。编码决定了这些数字如何存储为字节。这就是 UTF-8、UTF-16 和 UTF-32 的作用。

# Unicode code points are abstract numbers:
U+0041  →  A          (Latin capital letter A)
U+00E9  →  é          (Latin small letter e with acute)
U+4E16  →  世         (CJK Unified Ideograph - "world")
U+1F600 →  😀         (Grinning face emoji)

# These are just numbers — the encoding determines
# how they become bytes in memory or files.

4. UTF-8 编码 — 可变长度的精妙设计

UTF-8(Unicode 转换格式 — 8 位)是一种可变长度编码,每个字符使用 1 到 4 个字节。它由 Ken Thompson 和 Rob Pike 于 1992 年设计,现在是网络上占主导地位的编码(超过 98% 的网站使用)。

UTF-8 的设计非常优雅:它与 ASCII 向后兼容(任何有效的 ASCII 文件也是有效的 UTF-8),它是自同步的(你可以从任何位置找到字符边界),而且非 NUL 字符永远不会产生零字节(对 C 字符串安全)。

编码算法使用前缀系统来指示字节数:

字节数码位范围字节 1字节 2字节 3字节 4
1U+0000 - U+007F0xxxxxxx---
2U+0080 - U+07FF110xxxxx10xxxxxx--
3U+0800 - U+FFFF1110xxxx10xxxxxx10xxxxxx-
4U+10000 - U+10FFFF11110xxx10xxxxxx10xxxxxx10xxxxxx

让我们跟踪编码字符 é(U+00E9,带尖音符的拉丁小写字母 e)的过程:

# Encoding é (U+00E9) to UTF-8:
# Step 1: 0x00E9 = 0000 0000 1110 1001 in binary
# Step 2: U+00E9 falls in range U+0080–U+07FF → 2 bytes
# Step 3: Template: 110xxxxx 10xxxxxx
# Step 4: Fill in bits from right:
#   00E9 = 000 1110 1001
#   Byte 1: 110_00011 = 0xC3
#   Byte 2: 10_101001 = 0xA9
# Result: é = C3 A9 (2 bytes in UTF-8)

echo -n "é" | xxd
# Output: 00000000: c3a9

一个更复杂的例子——中文字符 世(U+4E16,意为"世界"):

# Encoding 世 (U+4E16) to UTF-8:
# Step 1: 0x4E16 = 0100 1110 0001 0110 in binary
# Step 2: U+4E16 falls in range U+0800–U+FFFF → 3 bytes
# Step 3: Template: 1110xxxx 10xxxxxx 10xxxxxx
# Step 4: Fill in bits:
#   4E16 = 0100 1110 0001 0110
#   Byte 1: 1110_0100 = 0xE4
#   Byte 2: 10_111000 = 0xB8
#   Byte 3: 10_010110 = 0x96
# Result: 世 = E4 B8 96 (3 bytes in UTF-8)

echo -n "世" | xxd
# Output: 00000000: e4b8 96

对于表情符号如 😀(U+1F600,微笑脸):

# Encoding 😀 (U+1F600) to UTF-8:
# Step 1: 0x1F600 = 0001 1111 0110 0000 0000 in binary
# Step 2: U+1F600 falls in range U+10000–U+10FFFF → 4 bytes
# Step 3: Template: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
# Step 4: Fill in bits:
#   1F600 = 0 0001 1111 0110 0000 0000
#   Byte 1: 11110_000 = 0xF0
#   Byte 2: 10_011111 = 0x9F
#   Byte 3: 10_011000 = 0x98
#   Byte 4: 10_000000 = 0x80
# Result: 😀 = F0 9F 98 80 (4 bytes in UTF-8)

echo -n "😀" | xxd
# Output: 00000000: f09f 9880

5. UTF-16 和 UTF-32 — UTF-8 的替代方案

UTF-16 每个字符使用 2 或 4 个字节。BMP 中的字符(U+0000–U+FFFF)使用 2 个字节。BMP 外的字符使用代理对——两个 16 位代码单元。

代理对的工作方式:从码位减去 0x10000,将得到的 20 位分成高代理(0xD800–0xDBFF)和低代理(0xDC00–0xDFFF)。

例如,😀(U+1F600):0x1F600 - 0x10000 = 0xF600。高 10 位:0x3D → 0xD800 + 0x3D = 0xD83D。低 10 位:0x200 → 0xDC00 + 0x200 = 0xDE00。所以 😀 在 UTF-16 中是 D83D DE00

UTF-16 的使用场景:JavaScript 字符串(String.charCodeAt() 返回 UTF-16 代码单元)、Java 的 char 类型、Windows API(wchar_t)以及 .NET 的 System.String

// JavaScript uses UTF-16 internally
const emoji = "😀";

// .length counts UTF-16 code units, NOT characters
console.log(emoji.length);          // 2 (surrogate pair)
console.log(emoji.charCodeAt(0));   // 55357 (0xD83D - high surrogate)
console.log(emoji.charCodeAt(1));   // 56832 (0xDE00 - low surrogate)

// Use codePointAt() for actual Unicode code points
console.log(emoji.codePointAt(0));  // 128512 (0x1F600)

// Use spread or Array.from for correct character counting
console.log([...emoji].length);     // 1 (correct!)
console.log(Array.from(emoji).length); // 1

// String.fromCodePoint handles supplementary characters
console.log(String.fromCodePoint(0x1F600)); // 😀

UTF-32 每个字符固定使用 4 个字节。它是最简单的编码(码位 = 存储值),但对拉丁文本浪费空间(ASCII 的 4 倍大小)。它在一些程序内部用于简化字符索引,但很少用于存储或传输。

编码大小主要用途
UTF-81-4 bytes/charWeb, files, APIs, JSON, databases
UTF-162 or 4 bytes/charJavaScript, Java, Windows, .NET
UTF-324 bytes/char (fixed)Internal processing, random access

6. BOM(字节顺序标记)

字节顺序标记(BOM)是一个特殊的 Unicode 字符 U+FEFF,放在文件开头以指示其编码和字节顺序。

对于 UTF-16 和 UTF-32,BOM 是必不可少的——它告诉读取器文件使用的是大端序还是小端序字节顺序。

编码BOM 字节说明
UTF-8EF BB BFOptional (discouraged)
UTF-16 BEFE FFBig-endian byte order
UTF-16 LEFF FELittle-endian byte order
UTF-32 BE00 00 FE FFBig-endian byte order
UTF-32 LEFF FE 00 00Little-endian byte order

UTF-8 BOM 争议:UTF-8 没有字节顺序问题(字节始终按相同顺序),所以 BOM 在技术上是不必要的。Microsoft 工具(记事本、Excel)默认添加 UTF-8 BOM(EF BB BF),这可能导致问题:

  • PHP 脚本可能在 headers 之前输出 BOM,导致"headers already sent"错误
  • 如果 shebang 行前面有 BOM 字节,Shell 脚本可能失败
  • JSON 解析器可能拒绝以 BOM 开头的文件(RFC 8259 禁止这样做)
  • 连接文件可能将 BOM 嵌入输出的中间

建议:除非特定工具要求(如 Excel CSV 导入),否则不要对 UTF-8 文件使用 BOM。大多数现代编辑器和系统无需 BOM 即可处理 UTF-8。

# Check for BOM in a file:
xxd file.txt | head -1
# UTF-8 BOM: 00000000: efbb bf...

# Remove UTF-8 BOM with sed (Linux/macOS):
sed -i '1s/^\xEF\xBB\xBF//' file.txt

# Remove BOM with Python:
with open('file.txt', 'rb') as f:
    content = f.read()
if content.startswith(b'\xef\xbb\xbf'):
    content = content[3:]
with open('file.txt', 'wb') as f:
    f.write(content)

7. HTML 中的编码

正确的编码声明确保浏览器正确渲染页面。浏览器通过三种方式确定编码:

1. HTTP Content-Type 头(最高优先级):

Content-Type: text/html; charset=utf-8

2. HTML meta 标签(必须在前 1024 字节内):

<!-- HTML5 (recommended) -->
<meta charset="UTF-8">

<!-- HTML4 / XHTML equivalent -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

3. HTML 字符引用用于单个字符:

<!-- Named entity -->
<p>Copyright &copy; 2025</p>

<!-- Decimal numeric entity -->
<p>&#169; = &#233; = &#19990;</p>

<!-- Hex numeric entity -->
<p>&#xA9; = &#xE9; = &#x4E16;</p>

<!-- Emoji via hex entity -->
<p>&#x1F600; = 😀</p>

优先顺序:HTTP 头 > BOM > meta 标签 > 编码嗅探。始终同时设置 HTTP 头和 meta 标签以获得最大可靠性。

对于 HTML5,<meta charset="UTF-8"> 声明应始终是 <head> 内的第一个元素,以确保在解析任何文本内容之前就被处理。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">  <!-- Must be first! -->
  <title>My Page</title>
</head>
<body>
  <p>café 世界 😀</p>  <!-- All render correctly with UTF-8 -->
</body>
</html>

8. 编程语言中的编码

不同编程语言处理字符串编码的方式不同。以下是主要语言的工作方式:

Python 3 区分 str(文本,Unicode)和 bytes(原始字节)。所有字符串默认为 Unicode:

# Python 3: str is Unicode, bytes is raw bytes
text = "café 世界 😀"       # str (Unicode)
encoded = text.encode('utf-8')  # bytes
decoded = encoded.decode('utf-8')  # back to str

print(type(text))      # <class 'str'>
print(type(encoded))   # <class 'bytes'>
print(encoded)         # b'caf\xc3\xa9 \xe4\xb8\x96\xe7\x95\x8c \xf0\x9f\x98\x80'
print(len(text))       # 8 (characters)
print(len(encoded))    # 18 (bytes)

# Handling encoding errors
bad_bytes = b'\xc3\x28'  # Invalid UTF-8
# text = bad_bytes.decode('utf-8')  # UnicodeDecodeError!
text = bad_bytes.decode('utf-8', errors='replace')  # '\ufffd('
text = bad_bytes.decode('utf-8', errors='ignore')   # '('

# Reading files with explicit encoding
with open('file.txt', 'r', encoding='utf-8') as f:
    content = f.read()

JavaScript / TypeScript 内部使用 UTF-16。TextEncoderTextDecoder API 处理编码转换:

// JavaScript: strings are UTF-16, use TextEncoder for UTF-8
const text = "café 世界 😀";

// TextEncoder converts string → UTF-8 bytes
const encoder = new TextEncoder();
const bytes = encoder.encode(text);
console.log(bytes);  // Uint8Array(18) [99, 97, 102, ...]
console.log(bytes.length);  // 18 bytes

// TextDecoder converts UTF-8 bytes → string
const decoder = new TextDecoder('utf-8');
const decoded = decoder.decode(bytes);
console.log(decoded);  // "café 世界 😀"

// Handling errors
const badBytes = new Uint8Array([0xC3, 0x28]);
const strictDecoder = new TextDecoder('utf-8', { fatal: true });
try {
  strictDecoder.decode(badBytes);  // throws TypeError
} catch (e) {
  console.log('Invalid UTF-8 sequence');
}

// Correct character counting with Intl.Segmenter
const segmenter = new Intl.Segmenter();
const graphemes = [...segmenter.segment(text)];
console.log(graphemes.length);  // 8 (correct grapheme clusters)

Go 使用 rune 类型(int32 的别名)表示 Unicode 码位。字符串是 UTF-8 字节序列:

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    text := "café 世界 😀"

    // len() returns byte count
    fmt.Println(len(text))  // 18 bytes

    // utf8.RuneCountInString() returns character count
    fmt.Println(utf8.RuneCountInString(text))  // 8 runes

    // Range over string iterates by rune (not byte)
    for i, r := range text {
        fmt.Printf("byte %d: U+%04X %c (%d bytes)\n",
            i, r, r, utf8.RuneLen(r))
    }
    // byte 0: U+0063 c (1 bytes)
    // byte 1: U+0061 a (1 bytes)
    // byte 2: U+0066 f (1 bytes)
    // byte 3: U+00E9 é (2 bytes)
    // byte 5: U+0020   (1 bytes)
    // byte 6: U+4E16 世 (3 bytes)
    // byte 9: U+754C 界 (3 bytes)
    // byte 12: U+0020   (1 bytes)
    // byte 13: U+1F600 😀 (4 bytes)
}

9. 常见编码错误

乱码(日语中的"文字化け")是用错误编码解码字节导致的乱码文本。以下是常见模式和修复方法:

常见乱码模式及其原因:

  • é 而不是 é — UTF-8 字节被当作 Latin-1 解读
  • 蟩 而不是 — UTF-8 字节被当作 Latin-1 解读(3 字节字符)
  • ���(替换字符)— UTF-8 解码中的无效字节序列
  • ???(问号)— 字符在目标编码中无法表示
# Why does "é" become "é"?
# UTF-8 encodes é as two bytes: C3 A9
# If those bytes are read as Latin-1:
#   C3 → Ã
#   A9 → ©
# Result: "é" instead of "é"

# Why does "世" become "世"?
# UTF-8 encodes 世 as three bytes: E4 B8 96
# If those bytes are read as Latin-1:
#   E4 → ä    B8 → ¸    96 → (control char, often shown as –)
# Result: "世" instead of "世"

调试工具:

  • xxdhexdump — 检查文件中的原始字节
  • file -i filename(Linux/macOS)— 检测文件编码
  • iconv -f FROM -t TO filename — 在编码之间转换
  • chardet / chardetect(Python 库)— 自动检测编码

使用 iconv 的常见修复方法:

# Convert from Latin-1 to UTF-8:
iconv -f ISO-8859-1 -t UTF-8 input.txt > output.txt

# Convert from Windows-1252 to UTF-8:
iconv -f WINDOWS-1252 -t UTF-8 input.txt > output.txt

# List all available encodings:
iconv -l

# Detect encoding with chardet (Python):
pip install chardet
chardetect file.txt
# file.txt: utf-8 with confidence 0.99

在 Python 中,你可以修复双重编码的文本:

# Fix double-encoded UTF-8 in Python:
# If UTF-8 bytes were incorrectly decoded as Latin-1 then re-encoded
broken = "café"  # é was double-encoded
fixed = broken.encode('latin-1').decode('utf-8')
print(fixed)  # "café"

# Using chardet to auto-detect encoding:
import chardet

with open('mystery.txt', 'rb') as f:
    raw = f.read()
    result = chardet.detect(raw)
    print(result)
    # {'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}
    text = raw.decode(result['encoding'])

10. 表情符号编码

表情符号是 Unicode 字符,主要位于补充多语言平面(U+1F000–U+1FFFF)和其他补充平面。在 UTF-8 中,大多数表情符号需要 4 个字节

# Simple emoji: single code point
😀 = U+1F600
UTF-8:  F0 9F 98 80 (4 bytes)
UTF-16: D83D DE00  (4 bytes, surrogate pair)

ZWJ(零宽连接符)序列使用 U+200D 将多个表情符号组合成单个字形。例如,家庭表情符号由单个人物表情符号通过 ZWJ 连接而成:

# ZWJ (Zero Width Joiner) sequences:
# 👨‍👩‍👧‍👦 = Man + ZWJ + Woman + ZWJ + Girl + ZWJ + Boy
# U+1F468 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466

# In JavaScript:
const family = "👨‍👩‍👧‍👦";
console.log(family.length);           // 11 (UTF-16 code units!)
console.log([...family].length);      // 7 (code points, still wrong!)

// Correct: use Intl.Segmenter
const seg = new Intl.Segmenter();
console.log([...seg.segment(family)].length);  // 1 (one grapheme cluster)

# In Python:
import unicodedata
family = "👨\u200D👩\u200D👧\u200D👦"
print(len(family))  # 7 code points
# For grapheme clusters, use the 'grapheme' library

变体选择器修改字符的呈现方式。U+FE0F(VS16)请求表情呈现,而 U+FE0E(VS15)请求文本呈现:

# Variation selectors change presentation:
# ❤ (U+2764) + U+FE0F → ❤️  (emoji presentation, red heart)
# ❤ (U+2764) + U+FE0E → ❤︎  (text presentation, outline heart)

# ☺ (U+263A) + U+FE0F → ☺️  (emoji style)
# ☺ (U+263A) + U+FE0E → ☺︎  (text style)

# In HTML:
# <span>&#x2764;&#xFE0F;</span>  → red emoji heart
# <span>&#x2764;&#xFE0E;</span>  → text-style heart

肤色修饰符(U+1F3FB–U+1F3FF)跟在基础表情符号后面以改变肤色。基础 + 修饰符显示为单个字符:

# Skin tone modifiers (Fitzpatrick scale):
# 👋 (U+1F44B) + 🏻 (U+1F3FB) → 👋🏻  (light skin)
# 👋 (U+1F44B) + 🏽 (U+1F3FD) → 👋🏽  (medium skin)
# 👋 (U+1F44B) + 🏿 (U+1F3FF) → 👋🏿  (dark skin)

# Each modifier adds 4 bytes in UTF-8:
# 👋 alone:  F0 9F 91 8B           (4 bytes)
# 👋🏽 :    F0 9F 91 8B F0 9F 8F BD  (8 bytes)

国旗序列使用成对的区域指示符号(U+1F1E6–U+1F1FF)。每个字母对应一个国家代码:

# Flag emoji use Regional Indicator Symbols:
# 🇺🇸 = U+1F1FA (RI U) + U+1F1F8 (RI S) = US flag
# 🇯🇵 = U+1F1EF (RI J) + U+1F1F5 (RI P) = JP flag
# 🇩🇪 = U+1F1E9 (RI D) + U+1F1EA (RI E) = DE flag

# Each regional indicator is 4 bytes in UTF-8
# So each flag emoji is 8 bytes in UTF-8

由于这些组合机制,一个可见的"字符"(字素簇)可以包含许多 Unicode 码位和许多字节。带有肤色的家庭表情符号在 UTF-8 中可以超过 25 个字节。

11. 最佳实践

遵循以下规则以避免项目中的编码问题:

  1. 始终使用 UTF-8。在编辑器、数据库、HTTP 头和 HTML meta 标签中都设置它。新项目没有理由使用其他编码。
  2. 显式声明编码。永远不要依赖编码检测或默认值。在 HTML 中使用 <meta charset="UTF-8">,在 HTTP 头中使用 Content-Type: text/html; charset=utf-8
  3. 数据库排序规则很重要。在 MySQL/MariaDB 中使用 utf8mb4(而不是 utf8)。MySQL 的 utf8 仅支持 3 字节字符(不支持表情符号)。对于排序规则,使用 utf8mb4_unicode_ciutf8mb4_0900_ai_ci
  4. 将文件保存为不带 BOM 的 UTF-8。配置编辑器以 UTF-8 格式保存。除非特别要求,否则避免使用 BOM。
  5. 在边界处理编码。尽早将字节解码为字符串(在输入时),尽晚将字符串编码为字节(在输出时)。
  6. 使用非 ASCII 数据测试。在测试数据中使用像 "café 世界 😀" 这样的字符串,以便尽早发现编码错误。
  7. 使用参数化查询。永远不要用字符串拼接构造 SQL。参数化查询可以正确处理编码并防止 SQL 注入。
  8. 规范化 Unicode。使用 NFC 规范化进行存储和比较。字符 é 可以表示为单个码位(U+00E9)或 e + 组合尖音符(U+0065 U+0301)。NFC 确保使用一致的形式。

使用正确编码创建数据库的示例:

-- MySQL / MariaDB: Always use utf8mb4
CREATE DATABASE myapp
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(255),
  bio TEXT
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- PostgreSQL: UTF-8 is the default and recommended encoding
CREATE DATABASE myapp
  ENCODING 'UTF8'
  LC_COLLATE 'en_US.UTF-8'
  LC_CTYPE 'en_US.UTF-8';

-- Check current encoding:
-- MySQL:      SHOW VARIABLES LIKE 'character_set%';
-- PostgreSQL: SHOW server_encoding;
# .editorconfig — enforce UTF-8 across your project
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

12. 常见问题

ASCII 和 UTF-8 有什么区别?

ASCII 是一个 7 位字符集,包含 128 个字符(英文字母、数字、符号和控制字符)。UTF-8 是一种可变长度编码,可以表示所有 154,000 多个 Unicode 字符。UTF-8 向后兼容 ASCII——前 128 个 UTF-8 字符与 ASCII 完全相同,各使用一个字节。ASCII 之外的字符在 UTF-8 中使用 2–4 个字节。

为什么应该使用 UTF-8 而不是 UTF-16 或 UTF-32?

UTF-8 对于以拉丁文为主的文本(包括大多数代码、标记和网页内容)是最节省空间的。它向后兼容 ASCII,因此现有的 ASCII 工具无需更改即可使用。它也是网络(98% 以上的网站)、JSON(RFC 8259 要求)和大多数现代 API 的标准。UTF-16 在 JavaScript 和 Java 内部使用,UTF-32 用于方便的码位索引,但两者都不推荐用于交换或存储。

如何修复文本中的乱码?

首先,通过检查字节模式确定原始编码。常见模式:é 而不是 é 意味着 UTF-8 被错误地当作 Latin-1 读取。修复方法:在 Python 中使用 text.encode("latin-1").decode("utf-8")。对于文件,使用 iconv -f WRONG_ENCODING -t utf-8 file.txt。Python 的 chardet 库可以自动检测原始编码。

Unicode 和 UTF-8 有什么区别?

Unicode 是一个字符集——从数字(码位)到字符的映射。例如,U+0041 = A,U+4E16 = 世。UTF-8 是一种编码——将这些码位转换为字节以进行存储和传输的规则。可以把 Unicode 看作字典,UTF-8 看作书写风格。UTF-16 和 UTF-32 等其他编码也可以表示 Unicode 字符,只是使用不同的字节模式。

为什么 MySQL 的 utf8 不支持表情符号,应该用什么替代?

MySQL 的 utf8 字符集实际上是一个非标准的最大 3 字节 UTF-8 子集。它无法存储在 UTF-8 中需要 4 个字节的字符,包括所有表情符号(如 😀 U+1F600)和许多 CJK 字符。请改用 utf8mb4,它支持完整的 UTF-8 范围(1–4 字节)。创建数据库或表时,请指定:CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci

理解字符编码是构建可靠软件的基础。从 ASCII 到 Unicode 再到 UTF-8 的演进解决了全球文本表示问题。始终使用 UTF-8,显式声明编码,并使用多样化的字符进行测试以避免编码错误。

使用我们的二进制-文本转换器进行文本与二进制的转换 →

使用我们的 HTML 实体工具进行编码 →

使用我们的 Base64 工具进行编码和解码 →

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

01Text ↔ Binary Converter&;HTML Entity EncoderB64Base64 Encoder/DecoderAaASCII Text Converter

相关文章

HTML 特殊字符与实体:完整参考表(2025)

完整的 HTML 实体参考表。包括符号、箭头、数学运算符、货币符号和特殊字符的命名和数字字符引用。

Base64 编码实战:每个开发者都应该知道的 7 个真实用途

发现 Base64 编码的 7 个实际应用:HTML 嵌入图片、Kubernetes Secrets、JWT Token、Data URI 等。