文章

Linux 字符集

Linux 字符集(character set,charset),字符编码。

Linux 字符集

字符集和字符编码

在 Linux 系统中,字符编码和字符集是处理文本数据(包括文件名、文件内容、终端显示、环境变量等)的核心概念。理解它们对于避免乱码、正确进行国际化(i18n)和本地化(l10n)至关重要。

字符集(Charater Set)是字符的集合。字符是各种文字和符号的总称,包括文字、标点符号、图形符号、数字、控制符号等。常见的字符集,如:

  • ASCII字符集:最基本的字符集,包含 128 个字符(0-127),包括英文字母、数字、标点符号和控制字符。每个字符用 1 个字节(7 位)表示。
  • ISO-8859 系列 (Latin-1, Latin-2 等): 扩展 ASCII 的字符集,使用 1 个字节(8 位),增加了 128 个字符,用于支持西欧、中欧、北欧等语言的字母和符号。范围是 0-255。
  • Unicode字符集:现代标准! 旨在包含世界上所有书写系统的所有字符。它定义了一个巨大的字符集(码位空间),远远超出了 1 字节或 2 字节的范围。Unicode 本身不是编码

字符码(Code Point)也称编码点,在每个字符集中,每个字符有个编号,称之为字符集编号,该编号就是字符码。Code point(code point or code position is any of the numerical values that make up the code space,该字符在子库表中的位置)。譬如,ASCII字符集中字母’A’的字符码就是65。

字符编码(Character Encoding)是字符的编码方式,是将字符集中的字符码映射为字节流的一种具体实现方案。或者说,是将字符集中的字符映射到计算机存储(字节序列)的规则。如:

  • ASCII 编码: 直接对应 ASCII 字符集,1 字节/字符。
  • ISO-8859-X 编码: 直接对应各自的 ISO-8859-X 字符集,1 字节/字符。
  • UTF-8Linux 和 Web 的绝对主流!Unicode 的一种变长编码方案。
    • 使用 1 到 4 个字节表示一个字符。
    • 完全兼容 ASCII: ASCII 字符(0-127)用 1 个字节表示,且编码值与 ASCII 相同。这是其巨大优势。
    • 非 ASCII 字符(如中文、日文、表情符号)使用 2、3 或 4 个字节。
    • 优点:节省空间(对于主要使用 ASCII 的文本),无字节序问题,兼容性好。
  • UTF-16:Unicode 的另一种编码。使用 2 或 4 个字节表示一个字符。存在大端序(BE)和小端序(LE)的问题。在 Linux 桌面/服务器环境中不如 UTF-8 常见,但在 Windows 和 Java 内部使用较多。
  • UTF-32:Unicode 编码,固定使用 4 个字节表示每个字符。非常浪费空间,主要用于内部处理,很少用于存储或传输。
  • GB2312 / GBK / GB18030:主要用于简体中文的字符集和编码(GB18030 兼容 Unicode)。
  • Big5: 主要用于繁体中文的字符集和编码。
  • EUC-JP / Shift_JIS:主要用于日文的编码。

字符集和字符编码是完全不同的概念。字符编码依赖于字符集,没有字符集,字符编码就无意义,也就是说字符集是字符编码的基础。一个字符集可以有多个编码实现,所以有众多的字符编码方式。对于ASCII、GB2312、Big5、GBK,其既是字符集,本身又是字符编码方式,所以会让人很混乱。

查看系统支持的字符集

1
cat /usr/share/i18n/SUPPORTED

locale

Linux 使用 locale 系统来定义用户的语言、地域、字符编码偏好等环境设置。这是配置字符编码的关键。

locale是某一个地域内的人们的语言习惯、文化传统和生活习惯,是根据计算机用户所使用的语言,所在国家或者地区,以及当地的文化传统所定义的一个软件运行时的语言环境

locale命令

查看已安装的字符集

1
locale -a
  • zh是指中文、汉语
  • en是指英语
  • 中国大陆zh_CN
  • 香港zh_HK
  • 台湾zh_TW
  • 新加坡zh_SG

查看已安装的中文字符集

1
2
# 只查看中国大陆,不包括香港、台湾
locale -a|grep zh_CN

环境变量

LANG:设置默认的 locale,是其他未设置的 LC_* 变量的后备值,优先级最低

LANGUAGE:是设置应用程序的界面语言。

LC_ALL最高优先级,覆盖所有其他 LC_* 变量和 LANG。通常用于临时强制某个 locale。

LC_CTYPE最关键! 决定字符的分类(字母、数字、空格等)、大小写转换规则以及文本数据和文件名的字符编码。例如:

  • en_US.UTF-8 (英语, 美国, UTF-8 编码)
  • zh_CN.GB18030 (简体中文, 中国, GB18030 编码)
  • zh_TW.UTF-8 (繁体中文, 台湾, UTF-8 编码)
  • ja_JP.UTF-8 (日语, 日本, UTF-8 编码)

其他 LC_* 变量 (LC_MESSAGES, LC_TIME, LC_NUMERIC, LC_MONETARY, LC_COLLATE) 控制消息语言、日期时间格式、数字/货币格式、排序规则等。

设置 locale

系统级:通常通过 /etc/locale.conf (systemd 系统) 或 /etc/default/locale (某些 sysvinit 系统) 设置 LANG

用户级:在 ~/.bashrc, ~/.profile, ~/.config/i18n 等 shell 配置文件中设置 LANGLC_* 变量。例如:

1
2
3
export LANG="en_US.UTF-8"
# 或者更精确地控制字符编码
export LC_CTYPE="en_US.UTF-8"

临时:在命令行中设置 LC_ALLLC_CTYPE

1
LC_ALL="zh_CN.GBK" some_command # 仅对这次命令生效

终端模拟器 (Terminal Emulator)

即使系统 LC_CTYPE 设置正确,终端模拟器(如 GNOME Terminal, Konsole, xterm, PuTTY)本身也有自己的字符编码设置。

终端的编码设置必须与它接收到的数据的编码(通常由 LC_CTYPE 决定)以及它使用的字体所能显示的字符集匹配,才能正确显示文本,避免乱码。

在终端的设置菜单中通常可以找到字符编码选项(如 UTF-8, GBK, ISO-8859-1 等)。现代 Linux 发行版的终端通常默认设置为 UTF-8。

文件编码

文本文件本身是以特定的字节序列存储的,这个序列遵循某种字符编码规则。

Linux 工具本身通常不关心文件的扩展名(.txt, .c 等),它们依赖 LC_CTYPE 的设置来解释文件的字节流。

如果文件的编码与 LC_CTYPE 设置的编码不一致,或者终端编码不匹配,就会导致文件内容显示为乱码。

重要工具

iconv

iconv:强大的字符编码转换工具。

语法:iconv -f from_encoding -t to_encoding inputfile -o outputfile

示例:将 GBK 文件转换为 UTF-8

1
iconv -f GBK -t UTF-8 gbkfile.txt -o utf8file.txt

file

file:猜测文件的类型和可能的编码(基于内容分析,不一定完全准确)。

1
file -i filename.txt # 输出如:filename.txt: text/plain; charset=utf-8

enca / chardetect

enca / chardetect:更智能的文件编码分析工具(通常需要额外安装)。

文本编辑器

文本编辑器:大多数现代编辑器(Vim, Emacs, VS Code, Gedit, Kate, Nano 等)都可以检测或指定文件的编码进行打开、编辑和保存。这是处理不同编码文件最常用的方式。

locale-gen

locale-gen:生成系统可用的 locale(通常由发行版安装程序或管理员运行)。

修改字符集

旧版 Debian 和 Ubuntu

/var/lib/locales/supported.d/目录是旧版 Debian 和 Ubuntu(主要在使用 dpkg-reconfigure locales 管理 locale 的时代)用来存放用户自定义 locale 支持列表的地方。每个文件对应一种语言环境列表。

1
ls /var/lib/locales/supported.d/

/var/lib/locales/supported.d/目录下有两个文件en和zh-hans(网上说en和zh-hans是由local文件分割成的两个文件)。

en保存英文字符。

zh-hans表示简体中文,保存中文字符。

修改(添加或删除)汉语(中文)字符集

1
sudo vim /var/lib/locales/supported.d/zh-hans

例如:

1
2
zh_CN.UTF-8 UTF-8
zh_SG.UTF-8 UTF-8

现代Linux发行版

在现代 Linux 发行版中,特别是使用 systemd 的系统(如 Debian 10+/Ubuntu 16.04+/Fedora/RHEL 8+ 等),/var/lib/locales/supported.d/ 这个目录已经不存在了

现在发生了什么变化?

集中化管理 (/etc/locale.gen):

  • 现代系统将所有可生成 locale 的列表统一放在一个文件:/etc/locale.gen
  • 这个文件包含了系统上所有可能生成的 locale,但大部分行默认是被注释掉的(以 # 开头)。
  • 你需要编辑这个文件,取消注释你需要的 locale(比如 en_US.UTF-8 UTF-8, zh_CN.UTF-8 UTF-8 等),然后运行生成命令

生成命令 (locale-gen):

  • 编辑好 /etc/locale.gen 后,你需要运行 sudo locale-gen(不需要任何参数)。
  • 这个命令会读取 /etc/locale.gen所有未被注释的行,并实际生成这些 locale 定义文件(存放在 /usr/lib/locale/ 目录下)。
  • 运行 locale-gen 之后,新启用的 locale 就可以在 locale -a 命令中看到,并可以在 LANG, LC_* 环境变量中使用了。

localectl 命令 (systemd 系统):

  • 在基于 systemd 的系统上,还有一个更高级的管理工具:localectl
  • 你可以用它来查看和设置系统范围的默认 locale:
    • localectl list-locales:列出系统已经生成的可用 locale(相当于 locale -a)。
    • localectl set-locale LANG=en_US.UTF-8:设置系统默认的 LANG 环境变量,这会修改 /etc/locale.conf 文件。注意:这设置的是环境变量,并不会生成新的 locale。你需要的 locale 必须先通过 /etc/locale.gen + locale-gen 生成出来。
  • 它通常不直接管理 locale 的生成,生成工作还是由 /etc/locale.gen + locale-gen 负责。

安装中文字符集(旧版)

  1. 安装中文包
    1
    
    apt-get install language-pack-zh*
    
  2. 配置相关环境变量
    1
    2
    3
    4
    
    vim /etc/environment  # 通常应该不是使用这一个文件;使用以下文件之一
    vim /etc/locale.conf
    vim /etc/default/locale
    vim ~/.bashrc
    

    增加2行:

    1
    2
    
    LANG="zh_CN.UTF-8"            # 这一行不一定需要
    LANGUAGE="zh_CN:zh:en_US:en"  # 这一行不一定需要
    

    不一定需要增加,不增加中文仍然乱码的话,才增加上面1行 或 2行。

  3. 重新设置本地配置
    1
    
    dpkg-reconfigure locales
    

安装中文字符集(现代化)

  1. 安装中文包
    1
    
    apt-get install language-pack-zh*
    
  2. 修改字符集,编辑/etc/locale.gen 取消相关注释
    1
    2
    3
    4
    5
    
    # 编辑
    sudo vim /etc/locale.gen
    # 取消注释
    en_US.UTF-8 UTF-8
    zh_CN.UTF-8 UTF-8
    
  3. 运行生成命令
    1
    
    sudo locale-gen
    
  4. 重启 或 注销。
本文由作者按照 CC BY 4.0 进行授权