Skip to content

字符编码(UTF-8)

字符编码决定了计算机如何将字符(如汉字、英文字母、标点符号)转换为二进制数据。在 Web 开发中,UTF-8 已经成为统一的标准编码,支持全球几乎所有语言和符号。本节将详细介绍 UTF-8 编码声明、BOM(字节顺序标记)、编码检测机制,以及常见乱码问题的排查方法。

前置知识

阅读本节前,建议先了解:lang 属性与语言声明

为什么需要字符编码

计算机只能处理二进制数据,字符编码提供了字符与二进制之间的映射规则。不同的编码方案支持不同的字符集:

编码支持语言说明
UTF-8所有语言变长编码(1-4 字节),向后兼容 ASCII
UTF-16所有语言固定 2/4 字节,Windows 内部常用
UTF-32所有语言固定 4 字节,处理方便但空间浪费
GBK/GB2312简体中文中国国家标准
Big5繁体中文台湾地区常用
Shift_JIS日语日本常用
EUC-KR韩语韩国常用
ISO-8859-1西欧语言Latin-1,不支持中文

统一使用 UTF-8

现代 Web 开发应统一使用 UTF-8 编码,避免使用 GBK、Big5 等地区性编码。UTF-8 支持全球所有语言,且是 HTML5 规范的推荐编码。

UTF-8 声明

HTML5 标准声明

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <!-- UTF-8 声明:必须在 <head> 前 1024 字节内 -->
  <meta charset="UTF-8" />
  <title>字符编码示例</title>
</head>
<body>
  <p>中文、日本語、한국어、English、Français</p>
</body>
</html>

为什么 charset 要在 head 开头

  • 浏览器在解析 HTML 时,需要尽早知道字符编码
  • 如果在遇到 <meta charset> 之前遇到了非 ASCII 字符,可能使用错误的编码解析
  • <meta charset> 应尽量出现在 <head> 的前 1024 字节内

HTML4 时代的方式(已过时)

html
<!-- HTML4 方式:已不推荐 -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

HTTP 头声明

服务器也可以通过 HTTP 响应头声明编码:

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

编码优先级:HTTP 头 > HTML meta 标签 > BOM

BOM(字节顺序标记)

BOM(Byte Order Mark)是 UTF-8/16/32 编码文件开头的特殊标记字节。

编码BOM 十六进制说明
UTF-8EF BB BF可选,表示 UTF-8
UTF-16 (BE)FE FF大端序
UTF-16 (LE)FF FE小端序
UTF-32 (BE)00 00 FE FF大端序
UTF-32 (LE)FF FE 00 00小端序

Web 开发与 BOM

html
<!-- UTF-8 文件带 BOM:文件开头有 EF BB BF 三个字节 -->
<!-- 浏览器通常能正确处理,但可能导致问题 -->

<!-- 问题 1:PHP 的 header() 函数前有 BOM 会报错 -->
<?php
header('Location: /home');
// 如果文件带 BOM,这个 header() 调用会失败
// 因为 BOM 的字节会被当作输出
?>

<!-- 问题 2:JavaScript 文件带 BOM 可能引起解析错误 -->
// UTF-8 BOM 会作为文件的第一个字符
// 在严格模式下可能导致问题

<!-- 问题 3:CSS @charset 规则前有 BOM 会被忽略 -->
@charset "UTF-8";
/* 如果文件带 BOM,@charset 不生效 */

建议:Web 文件不使用 BOM

HTML、CSS、JavaScript 文件建议保存为 UTF-8 without BOM(无 BOM 的 UTF-8),以避免潜在问题。

乱码问题排查

常见乱码原因

原因症状解决方法
文件实际编码与声明不一致中文显示为 ???å�ä»统一使用 UTF-8 保存和声明
数据库编码不一致从数据库读取的数据乱码数据库连接和表都使用 UTF-8
HTTP 头与 meta 不一致部分内容乱码确保服务器发送正确的 Content-Type
文件带 BOM页面开头出现 以 UTF-8 without BOM 保存
URL 编码问题查询参数中文乱码使用 encodeURIComponent

排查步骤

html
<!-- 1. 检查 HTML 声明 -->
<head>
  <meta charset="UTF-8" />  <!-- 确保有此声明 -->
</head>

<!-- 2. 检查 HTTP 响应头 -->
<!-- Chrome DevTools → Network → Response Headers -->
<!-- Content-Type: text/html; charset=UTF-8 -->

<!-- 3. 检查文件实际编码 -->
<!-- VS Code:右下角状态栏查看编码 -->
<!-- Sublime Text:File → Reopen with Encoding -->

<!-- 4. 检查数据库连接 -->
<!-- PHP:mysqli_set_charset($conn, "utf8mb4") -->
<!-- Node.js:使用 utf8mb4 字符集 -->

数据库编码

sql
-- MySQL 设置数据库和表为 UTF-8
CREATE DATABASE myapp
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100) NOT NULL
) CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;
php
// PHP 连接数据库设置编码
$mysqli = new mysqli('localhost', 'user', 'pass', 'myapp');
$mysqli->set_charset('utf8mb4');

// 或者使用 PDO
$pdo = new PDO('mysql:host=localhost;dbname=myapp;charset=utf8mb4', 'user', 'pass');

特殊字符处理

HTML 实体

html
<!-- 必须转义的字符 -->
&lt;    <!-- 小于号 < -->
&gt;    <!-- 大于号 > -->
&amp;   <!-- 和号 & -->
&quot;  <!-- 双引号 " -->
&apos;  <!-- 单引号 '(HTML5 中可用) -->

<!-- 示例 -->
<p>使用 &lt;div&gt; 标签创建容器</p>
<p>价格:5 &amp; 10 元</p>

Unicode 字符引用

html
<!-- 十进制引用 -->
<p>版权所有 &#169; 2024</p>
<p>人民币 ¥&#165;100</p>

<!-- 十六进制引用 -->
<p>版权所有 &#xA9; 2024</p>

<!-- 命名实体 -->
<p>版权所有 &copy; 2024</p>
<p>注册商标 &reg;</p>
<p>省略号 &hellip;</p>
<p>破折号 &mdash;</p>

Emoji 表情

UTF-8 天然支持 Emoji:

html
<!-- Emoji 在 HTML 中直接使用 -->
<p>前端开发 &#x1F680; 太有趣了!</p>
<p>学习进度:&#x1F4AF; 100%</p>

<!-- 也可以直接使用 Unicode 字符 -->
<p>前端开发 🚀 太有趣了!</p>
<p>学习进度:💯 100%</p>

实战示例:多语言 UTF-8 页面

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>UTF-8 多语言页面</title>
  <style>
    body {
      font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif;
      max-width: 800px;
      margin: 0 auto;
      padding: 2rem;
    }
    .lang-section { margin: 2rem 0; padding: 1rem; border: 1px solid #e0e0e0; border-radius: 8px; }
    .lang-section h2 { margin-top: 0; }
  </style>
</head>
<body>
  <h1>UTF-8 编码支持示例</h1>
  <p>本页面使用 UTF-8 编码,可以正确显示以下所有语言:</p>

  <div class="lang-section">
    <h2>简体中文</h2>
    <p>春眠不觉晓,处处闻啼鸟。夜来风雨声,花落知多少。</p>
  </div>

  <div class="lang-section" lang="zh-Hant">
    <h2>繁體中文</h2>
    <p>春眠不覺曉,處處聞啼鳥。夜來風雨聲,花落知多少。</p>
  </div>

  <div class="lang-section" lang="ja">
    <h2>日本語</h2>
    <p>春眠暁を覚えず、処処啼鳥を聞く。夜来風雨の声、花落知多少。</p>
  </div>

  <div class="lang-section" lang="ko">
    <h2>한국어</h2>
    <p>HTML5는 현대 웹 개발의 핵심 기술입니다.</p>
  </div>

  <div class="lang-section" lang="en">
    <h2>English</h2>
    <p>HTML5 is the core technology for modern web development.</p>
  </div>

  <div class="lang-section" lang="fr">
    <h2>Fran&ccedil;ais</h2>
    <p>HTML5 est la technologie de base du d&eacute;veloppement Web moderne.</p>
  </div>

  <div class="lang-section" lang="de">
    <h2>Deutsch</h2>
    <p>HTML5 ist die Kerntechnologie f&uuml;r die moderne Webentwicklung.</p>
  </div>

  <div class="lang-section">
    <h2>特殊字符</h2>
    <p>
      版权 &copy; | 注册 &reg; | 商标 &trade; | 省略号 &hellip; |
      箭头 &rarr; | 乘号 &times; | 除号 &divide; |
      人民币 &yen; | 欧元 &euro; | 磅 &pound;
    </p>
  </div>

  <div class="lang-section">
    <h2>Emoji 表情符号</h2>
    <p>
      &#x1F600; &#x1F60E; &#x1F680; &#x1F4BB; &#x1F4F1;
      &#x1F3A8; &#x1F30D; &#x2602; &#x2615; &#x1F3B5;
    </p>
  </div>
</body>
</html>

注意事项

  1. 统一使用 UTF-8:所有文件(HTML、CSS、JS、数据库)都使用 UTF-8 编码
  2. 不带 BOM 保存:Web 文件以 UTF-8 without BOM 保存
  3. 尽早声明编码<meta charset="UTF-8"> 放在 <head> 最前面
  4. 数据库使用 utf8mb4:MySQL 的 utf8 只支持最多 3 字节的 UTF-8,utf8mb4 支持完整的 4 字节(包括 Emoji)
  5. HTTP 头也要正确:确保服务器发送 Content-Type: text/html; charset=UTF-8

最佳实践

  • 编辑器默认编码设为 UTF-8 without BOM
  • 所有新文件保存为 UTF-8
  • <meta charset="UTF-8"> 放在 <head> 的第一行
  • 数据库使用 utf8mb4 字符集
  • API 请求和响应都使用 UTF-8
  • 处理用户输入时验证编码合法性
  • 使用 Content-Security-Policy 防止编码注入

下一节

继续学习:文本方向(dir 属性)

参考链接