彻底解决CentOS中文乱码问题

1.CentOS中文乱码解决方法

开发反馈有台开发机中文显示乱码,需要解决,所以就有了这篇文章。系统版本是centos6.9,当时看到设置的字符是zh_CN.gb2312,但是中文显示,编辑都乱码,使用locale看没有zh_CN.utf8,但是有en_US.utf8,设置en_US.utf8显示还是乱码,继续。

按网上文档执行命令在线安装中文支持包,然后并没有什么卵用,根本安装不上。

yum -y groupinstall chinese-support

于是找了一台centos6.8的系统把/usr/share/i18n/这个文件夹下的两个目录都打包,然后覆盖有问题机器的目录,这已经完成了一大步,后面有解释。

然后执行命令: localedef -c -f UTF-8 -i zh_CN zh_CN.utf8

1.1 查看中文包是否安装

[root@study ~]# yum grouplist |grep zh
   Chinese Support [zh]

1.2 查看当前语言支持

root@study ~# > echo $LANG
en_US.UTF-8

1.3 查看安装的语言包

root@study ~# > locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

1.4 查看支持的字符集

root@study ~# > locale -a |grep zh
zh_CN
zh_CN.gb18030
zh_CN.gb2312
zh_CN.gbk
zh_CN.utf8
zh_HK
zh_HK.big5hkscs
zh_HK.utf8
zh_SG
zh_SG.gb2312
zh_SG.gbk
zh_SG.utf8
zh_TW
zh_TW.big5
zh_TW.euctw
zh_TW.utf8

1.5 修改配置为中文字符集

LANG="zh_CN.UTF-8"
SUPPORTED="zh_CN.GB2312:zh_CN.UTF-8:zh_CN:zh:en_US.UTF-8:en_US:en"
SYSFONT="latarcyrheb-sun16"

i18n中三个环境变量:

  • LANG变量,设置系统语言。

  • SUPPORTED变量决定系统支持的语言,即系统能够显示的语言。

  • SYSFONT变量表示系统字体。

执行source /etc/sysconfig/i18n生效,愿意重启也是可以的

网上执行到这一步都成功了,然而还是显示乱码,真是醉了。

1.6 执行测试命令

root@study ~# >  strace -eopen ls xx
open("/etc/ld.so.cache", O_RDONLY)      = 3
open("/lib64/libselinux.so.1", O_RDONLY) = 3
open("/lib64/librt.so.1", O_RDONLY)     = 3
open("/lib64/libcap.so.2", O_RDONLY)    = 3
open("/lib64/libacl.so.1", O_RDONLY)    = 3
open("/lib64/libc.so.6", O_RDONLY)      = 3
open("/lib64/libdl.so.2", O_RDONLY)     = 3
open("/lib64/libpthread.so.0", O_RDONLY) = 3
open("/lib64/libattr.so.1", O_RDONLY)   = 3
open("/proc/filesystems", O_RDONLY)     = 3
open("/usr/local/glibc-2.14/lib/locale/locale-archive", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/glibc-2.14/share/locale/locale.alias", O_RDONLY) = 3
open("/usr/local/glibc-2.14/lib/locale/zh_CN.UTF-8/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/glibc-2.14/lib/locale/zh_CN.utf8/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/glibc-2.14/lib/locale/zh_CN/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/glibc-2.14/lib/locale/zh.UTF-8/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/glibc-2.14/lib/locale/zh.utf8/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/glibc-2.14/lib/locale/zh/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
ls: cannot access xx: No such file or directory
+++ exited with 2 +++

看了下好像是没找到字符集路径,然后做个软链 ln -sf /usr/lib/locale/locale-archive   /usr/local/glibc-2.14/lib/locale/locale-archive

root@study ~# >  strace -eopen ls xx
open("/etc/ld.so.cache", O_RDONLY)      = 3
open("/lib64/libselinux.so.1", O_RDONLY) = 3
open("/lib64/librt.so.1", O_RDONLY)     = 3
open("/lib64/libcap.so.2", O_RDONLY)    = 3
open("/lib64/libacl.so.1", O_RDONLY)    = 3
open("/lib64/libc.so.6", O_RDONLY)      = 3
open("/lib64/libdl.so.2", O_RDONLY)     = 3
open("/lib64/libpthread.so.0", O_RDONLY) = 3
open("/lib64/libattr.so.1", O_RDONLY)   = 3
open("/proc/filesystems", O_RDONLY)     = 3
open("/usr/local/glibc-2.14/lib/locale/locale-archive", O_RDONLY) = 3
open("/usr/local/glibc-2.14/share/locale/locale.alias", O_RDONLY) = 3
open("/usr/share/locale/zh_CN.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/zh_CN.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/zh_CN/LC_MESSAGES/coreutils.mo", O_RDONLY) = 3
open("/usr/local/glibc-2.14/lib/gconv/gconv-modules.cache", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/glibc-2.14/lib/gconv/gconv-modules", O_RDONLY) = 3
ls: 无法访问xxopen("/usr/local/glibc-2.14/share/locale/zh_CN.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/glibc-2.14/share/locale/zh_CN.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/glibc-2.14/share/locale/zh_CN/LC_MESSAGES/libc.mo", O_RDONLY) = 3
: 没有那个文件或目录
+++ exited with 2 +++

这次终于是解决了,问题就出在glibc加载不了字符集。改成zh_CN.utf8全部显示成中文了,最后还是改成en_US.utf8看着舒服点。

如果LANG环境变量设置为en_US.UTF-8,系统的菜单、程序的工具栏语言、输入法默认语言就都是英文的。zh_CN.utf8环境下英文和中文混合显示,够怪的。

搞完后中文乱码终于解决了,不过ssh登陆老显示警告:

-bash: warning: setlocale: LC_CTYPE: cannot change locale

把/etc/profile下#export LC_ALL="en_CN.UTF-8" 注释掉执行source /etc/profile就行 ,ssh远程登陆就不会显示这个烦人的警告了。


2.Linux系统字符集分析

2.1 Linux字符集定义

在 Linux 中通过 locale 来设置程序运行的不同语言环境,locale 由 ANSI C 提供支持。locale 的命名规则为: <语言>_<地区>.<字符集编码> ,如zh_CN.utf8

Locale 是软件在运行时的语言环境, 它包括语言(Language), 地域 (Territory) 和字符集(Codeset)。一个locale的书写格式为: 语言_地域.字符集. 所以说,locale总是和一定的字符集相联系的。

locale把按照所涉及到的文化传统的各个方面分成12个大类,这12个大类分别是:

1、语言符号及其分类(LC_CTYPE)

2、数字(LC_NUMERIC)

3、比较和排序习惯(LC_COLLATE)

4、时间显示格式(LC_TIME)

5、货币单位(LC_MONETARY)

6、信息主要是提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES)

7、姓名书写方式(LC_NAME)

8、地址书写方式(LC_ADDRESS)

9、电话号码书写方式(LC_TELEPHONE)

10、度量衡表达方式(LC_MEASUREMENT)

11、默认纸张尺寸大小(LC_PAPER)

12、对locale自身包含信息的概述(LC_IDENTIFICATION)。  

其中,与中文输入关系最密切的就是 LC_CTYPE, LC_CTYPE 规定了系统内有效的字符以及这些字符的分类,诸如什么是大写字母,小写字母,大小写转换,标点符号、可打印字符和其他的字符属性等方面。而locale定义zh_CN中最最重要的一项就是定义了汉字(Class “hanzi”)这一个大类,当然也是用Unicode描述的,这就让中文字符在Linux系统中成为合法的有效字符,而且不论它们是用什么字符集编码的。

在en_US的locale定义中,并没有定义汉字,所以汉字不是有效字符。所以如果要输入中文必须使用支持中文的locale,也就是zh_XX,如zh_CN,zh_TW,zh_HK等等。  

另外非常重要的一点就是这些分类是彼此独立的,也就是说LC_CTYPE,LC_COLLATE和 LC_MESSAGES等等分类彼此之间是独立的,可以根据用户的需要设定成不同的值。这一点对很多用户是有利的,甚至是必须的。例如,需要一个能够输入中文的英文环境,所以我可以把LC_CTYPE设定成zh_CN.GB18030,而其他所有的项都是en_US.UTF-8。  

设定locale就是设定12大类的locale分类属性,即 12个LC_*。除了这12个变量可以设定以外,为了简便起见,还有两个变量:LC_ALL和LANG。

Gentoo在locale定义的时候掩盖了一些东西,也就是locale的生成工具:localedef。在编译完glibc之后你可以用这个localedef 再补充一些locale,就会更加理解locale了。具体的可以看 localedef 的manpage。  

  The  localedef  program reads the indicated charmap and input files, compiles them to a form usable by the locale(7) functions in the C library, and places the six output files in the outputpath directory.        
  If no charmapfile is given, POSIX is used by default.  If no inputfile is given, or if it is given as -, localedef reads from standard input.

localedef -f 字符集 -i locale定义文件 生成的locale的名称

例如 # localedef -f UTF-8 -i zh_CN zh_CN.UTF-8

2.2 locale定义解析

查看系统locale设置

root@study ~# > locale
LANG=en_US.UTF8
LC_CTYPE="en_US.UTF8"
LC_NUMERIC="en_US.UTF8"
LC_TIME="en_US.UTF8"
LC_COLLATE="en_US.UTF8"
LC_MONETARY="en_US.UTF8"
LC_MESSAGES="en_US.UTF8"
LC_PAPER="en_US.UTF8"
LC_NAME="en_US.UTF8"
LC_ADDRESS="en_US.UTF8"
LC_TELEPHONE="en_US.UTF8"
LC_MEASUREMENT="en_US.UTF8"
LC_IDENTIFICATION="en_US.UTF8"
LC_ALL=

在locale环境中,有一组变量,代表国际化环境中的不同设置:

LANG=en_US.UTF-8    // LC*的默认值,是最低级别的设置,如果LC没有设置,则使用该值。类似于 LC_ALL。

LC_CTYPE="en_US.UTF-8"  //用于字符分类和字符串处理,控制所有字符的处理方式,包括字符编码,字符是单字节还是多字节,如何打印等。是最重要的一个环境变量

LC_NUMERIC="en_US.UTF-8"    // 非货币的数字显示格式

LC_TIME="en_US.UTF-8"    // 时间和日期格式

LC_COLLATE="en_US.UTF-8"   //环境的排序和比较规则

LC_MONETARY="en_US.UTF-8"   // 货币格式

LC_MESSAGES="en_US.UTF-8"   //提示信息的语言。另外还有一个LANGUAGE参数,它与LC_MESSAGES相似,但如果该参数一旦设置,则LC_MESSAGES参数就会失效。LANGUAGE参数可同时设置多种语言信息,如LANGUANE="zh_CN.GB18030:zh_CN.GB2312:zh_CN"。 LC_PAPER="en_US.UTF-8"   //默认纸张尺寸大小

LC_NAME="en_US.UTF-8"    //姓名书写方式

LC_ADDRESS="en_US.UTF-8"    //地址书写方式

LC_TELEPHONE="en_US.UTF-8"    //电话号码书写方式

LC_MEASUREMENT="en_US.UTF-8"   //度量衡表达方式

LC_IDENTIFICATION="en_US.UTF-8"   //对locale自身包含信息的概述

LC_ALL=    //它是一个宏,如果该值设置了,则该值会覆盖所有LC_的设置值。注意,LANG的值不受该宏影响。

字符集就是字符,尤其是非英语字符在系统内的编码方式,也就是通常所说的内码,所有的字符集都放在/usr/share/i18n/charmaps,所有的字符集也都是用Unicode编号索引的。Unicode用统一的编号来索引目前已知的全部的符号。而字符集则是这些符号的编码方式,或者说是在网络传输,计算机内部通信的时候,对于不同字符的表达方式,Unicode是一个静态的概念,字符集是一个动态的概念,是每一个字符传递或传输的具体形式。就像 Unicode编号U59D0是代表姐姐的“姐”字,但是具体的这个字是用两个字节表示,三个字节,还是四个字节表示,是字符集的问题。例如:UTF-8 字符集就是目前流行的对字符的编码方式,UTF-8用一个字节表示常用的拉丁字母,用两个字节表示常用的符号,包括常用的中文字符,用三个表示不常用的字符,用四个字节表示其他的古灵精怪的字符。而GB2312字符集就是用两个字节表示所有的字符。需要提到一点的是Unicode除了用编号索引全部字符以外,本身是用四个字节存储全部字符,这一点在谈到挂载windows分区的时候是非常重要的一个概念。所以说你也可以把Unicode看作是一种字符集(我不知道它和UTF-32的关系,反正UTF-32就是用四个字节表示所有的字符的),但是这样表述符号是非常浪费资源的,因为在计算机世界绝大部分时候用到的是一个字节就可以搞定 26个字母而已。所以才会有UTF-8,UTF-16等等,要不然大同世界多好,省了这许多麻烦。

它们之间有一个优先级的关系: LC_ALL > LC_* > LANG 可以这么说,LC_ALL是最上级设定或者强制设定,而LANG是默认设定值。

1、如果你设定了LC_ALL=zh_CN.UTF-8,那么不管LC*和LANG设定成什么值,它们都会被强制服从LC_ALL的设定,成为 zh_CN.UTF-8。

2、假如你设定了LANG=zh_CN.UTF-8,而其他的LC=en_US.UTF-8,并且没有设定LC_ALL的话,那么系统的locale 设定以LC_=en_US.UTF-8。

3、假如你设定了LANG=zh_CN.UTF-8,而其他的LC*,和LC_ALL均未设定的话,系统会将LC*设定成默认值,也就是LANG的值 zh_CN.UTF-8 。 *

4、假如你设定了LANG=zh_CN.UTF-8,而其他的LC_CTYPE=en_US.UTF-8,其他的LC_,和LC_ALL均未设定的话,那么系统的locale设定将是:LC_CTYPE=en_US.UTF-8,其余的 LC_COLLATE,LC_MESSAGES等等均会采用默认值,也就是LANG的值,也就是LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=zh_CN.UTF-8。  

所以,locale是这样设定的:

1、如果你需要一个纯中文的系统的话,设定LC_ALL= zh_CN.XXXX,或者LANG= zh_CN.XXXX都可以,当然你可以两个都设定,但正如上面所讲,LC_ALL的值将覆盖所有其他的locale设定,不要作无用功。

2、如果你只想要一个可以输入中文的环境,而保持菜单、标题,系统信息等等为英文界面,那么只需要设定 LC_CTYPE=zh_CN.XXXX,LANG= en_US.XXXX就可以了。这样LC_CTYPE=zh_CN.XXXX,而LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=en_US.XXXX。

3、假如你高兴的话,可以把12个LC*一一设定成你需要的值,打造一个古灵精怪的系统: LC_CTYPE=zh_CN.GBK/GBK(使用中文编码内码GBK字符集); LC_NUMERIC=en_GB.ISO-8859-1(使用大不列颠的数字系统) LC_MEASUREMEN=de_DE@euro.ISO-8859-15(德国的度量衡使用ISO-8859-15字符集) 罗马的地址书写方式,美国的纸张设定……。估计没人这么干吧。

4、假如你什么也不做的话,也就是LC_ALL,LANG和LC_*均不指定特定值的话,系统将采用POSIX作为lcoale,也就是C locale。

locale定义在/usr/share/i18n/locales目录下,比如zh_CN

/usr/share/i18n/locales/zh_CN

字符集定义在/usr/share/i18n/charmaps目录下,比如GB2312

/usr/share/i18n/charmaps/GB2312.gz

创建locale:

前面提到的locale定义和字符集定义相当于源代码,我们真正使用是基于locale定义 + 字符集定义得到编译好的locale,创建locale使用localedef,创建好的locale会被添加进/usr/lib/locale/locale-archive

字符编码配置文件 /etc/sysconfig/i18n 在 centos 7已经没有了,配置文件在 /etc/locale.conf


3.CentOS优化字符集

精简办法:

#查看语言支持列表
localedef --list-archive
#精简locale
cd /usr/lib/locale/
mv locale-archive locale-archive.old
localedef -i en_US -f UTF-8 en_US.UTF-8
# 添加中文支持(可选)
localedef -i zh_CN -f UTF-8 zh_CN.UTF-8
localedef -i zh_CN -f GB2312 zh_CN
localedef -i zh_CN -f GB2312 zh_CN.GB2312
localedef -i zh_CN -f GBK zh_CN.GBK
#下面这些也是可选的,可以丰富中文支持(香港/台湾/新加坡)
localedef -f UTF-8 -i zh_HK zh_HK.UTF-8
localedef -f UTF-8 -i zh_TW zh_TW.UTF-8
localedef -f UTF-8 -i zh_SG zh_SG.UTF-8

精简结果:

# pmap $(pgrep php-fpm|head -n 1) |grep local
00007f3d906d1000   1500K r----  /usr/lib/locale/locale-archive

变为1.5M,如果加上基本的中文支持,则是5M。

换个角度,如果你怕影响其他服务,删除中英文之外的其它语言算是比较保险的,办法是这样的:

localedef --list-archive |egrep -v ^"en_US|zh" |xargs localedef --delete-from-archive
mv -f /usr/lib/locale/locale-archive /usr/lib/locale/locale-archive.tmpl
build-locale-archive

这样一来,文件会缩小至10M

# du locale-archive
10892   locale-archive


4.CentOS安装指定字体

首先把要安装字体的字体包下载好,然后远程执行命令安装就行,可以写到一个脚本里:

ansible -i $hosts all  -m unarchive -a 'src=wqy-microhei.tar.gz dest=/usr/share/fonts/'
ansible -i $hosts  all  -m shell -a 'ls -l /usr/share/fonts/wqy-microhei'
ansible -i $hosts all  -m script -a 'fontcache.sh'

fontcache.sh
#!/bin/bash
cd /usr/share/fonts/wqy-microhei/
mkfontscale
mkfontdir
fc-cache -fv
fc-list :lang=zh

WenQuanYi Micro Hei Mono,文泉驛等寬微米黑,文泉驿等宽微米黑:style=Regular
WenQuanYi Micro Hei,文泉驛微米黑,文泉驿微米黑:style=Regular

                          

参考:

anzhihe 安志合个人博客,版权所有 丨 如未注明,均为原创 丨 转载请注明转自:https://chegva.com/3237.html | ☆★★每天进步一点点,加油!★★☆ | 

您可能还感兴趣的文章!

发表评论

电子邮件地址不会被公开。 必填项已用*标注