Linux 系统中的文件编码问题

作者: , 共 3277 字

由于 UNICODE 的优势,目前推荐都用 UTF-8 进行编码。

1. bash 相关

1.1. 查询文件所用编码

1.1.1. file 命令

file可以查看文件的类型,包括编码:

$ file ~/.vimrc
/home/xxx/.vimrc: UTF-8 Unicode (with BOM) text

但根据测试,这个东西不是特别准,会出现误判情况。另外对于 ANSI 编码,无法区分到具体编码类型。

1.1.2. 批量找出文件编码

下面这条语句可以查询当前目录下所有非 UTF-8 编码的文件:

find . -type f | xargs -I {} bash -c "iconv -f utf-8 -t utf-16 {} &>/dev/null || echo {}"

也可以指定文件类型,比如下面找出当前目录下所有非 UTF-8 编码的 cpp 文件:

find . -name *.cpp -type f | xargs -I {} bash -c "iconv -f utf-8 -t utf-16 {} &>/dev/null || echo {}"

1.2. iconv 更换编码

iconv是 Linux 内置的编码转换的命令;

iconv -f from-encoding -t to-encoding inputfile -o outputfile

2. vim 相关

2.1. \~/.vimrc 的编码置

在 Vim 中,有四个与编码有关的选项,它们是: fileencodings、fileencoding、encoding 和 termencoding。在实际使用中,任何一个选项出现错误,都会导致出现乱码。因此,每一个 Vim 用户都应该明确这四个选项的含义。

引用自:http://edyfox.codecarver.org/html/vim_fileencodings_detection.html

2.1.1. encoding

encodingVim内部使用的字符编码方式。当我们设置了encoding之后,Vim内部所有的buffer、寄存器、脚本中的字符串等,全都使用这个编码。Vim在工作的时候,如果编码方式与它的内部编码不一致,它会先把编码转换成内部编码。如果工作用的编码中含有无法转换为内部编码的字符,在这些字符就会丢失。因此,在选择Vim的内部编码的时候,一定要使用一种表现能力足够强的编码,以免影响正常工作。

由于encoding选项涉及到Vim中所有字符的内部表示,因此只能在Vim启动的时候设置一次。在Vim工作过程中修改encoding会造成非常多的问题。如果没有特别的理由,请始终将encoding设置为utf-8。为了避免在非UTF-8的系统如Windows下,菜单和系统提示出现乱码,可同时做这几项设置:

set encoding=utf-8
set langmenu=zh_CN.UTF-8
language message zh_CN.UTF-8
2.1.2. termencoding

termencoding 是Vim用于屏幕显示的编码,在显示的时候,Vim会把内部编码转换为屏幕编码,再用于输出。内部编码中含有无法转换为屏幕编码的字符时,该字符会变成问号,但不会影响对它的编辑操作。如果termencoding没有设置,则直接使用encoding不进行转换。

举个例子,当你在 Windows 下通过 telnet 登录 Linux 工作站时,由于 Windows 的 telnet 是 GBK 编码的,而 Linux 下使用 UTF-8 编码,你在 telnet 下的 Vim 中就会乱码。此时有两种消除乱码的方式:一是把 Vim 的 encoding 改为 gbk ,另一种方法是保持 encoding 为 utf-8 ,把 termencoding 改为 gbk ,让 Vim 在显示的时候转码。显然,使用前一种方法时,如果遇到编辑的文件中含有 GBK 无法表示的字符时,这些字符就会丢失。但如果使用后一种方法,虽然由于终端所限,这些字符无法显示,但在编辑过程中这些字符是不会丢失的。

对于图形界面下的 GVim ,它的显示不依赖 TERM ,因此 termencoding 对于它没有意义。在 GTK2 下的 GVim 中, termencoding 永远是 utf-8 ,并且不能修改。而 Windows 下的 GVim 则忽略 termencoding 的存在。

2.1.3. fileencoding

当 Vim 从磁盘上读取文件的时候,会对文件的编码进行探测。如果文件的编码方式和 Vim 的内部编码方式不同, Vim 就会对编码进行转换。转换完毕后, Vim 会将 fileencoding 选项设置为文件的编码。当 Vim 存盘的时候,如果 encoding 和 fileencoding 不一样, Vim 就会进行编码转换。因此,通过打开文件后设置 fileencoding ,我们可以将文件由一种编码转换为另一种编码。但是,由前面的介绍可以看出, fileencoding 是在打开文件的时候,由 Vim 进行探测后自动设置的。因此,如果出现乱码,我们无法通过在打开文件后重新设置 fileencoding 来纠正乱码。

2.1.4. fileencodings

编码的自动识别是通过设置 fileencodings 实现的,注意是复数形式。fileencodings 是一个用逗号分隔的列表,列表中的每一项是一种编码的名称。当我们打开文件的时候, VIM 按顺序使用 fileencodings 中的编码进行尝试解码,如果成功的话,就使用该编码方式进行解码,并将 fileencoding 设置为这个值,如果失败的话,就继续试验下一个编码。

因此,我们在设置 fileencodings 的时候,一定要把要求严格的、当文件不是这个编码的时候更容易出现解码失败的编码方式放在前面,把宽松的编码方式放在后面。

例如, latin1 是一种非常宽松的编码方式,任何一种编码方式得到的文本,用 latin1 进行解码,都不会发生解码失败——当然,解码得到的结果自然也就是理所当然的「乱码」。因此,如果你把 latin1 放到了 fileencodings 的第一位的话,打开任何中文文件都是乱码也就是理所当然的了。

2.2. 推荐设置汇总

set fileencodings=ucs-bom,utf-8,gb2312,gb18030,gbk,cp936,latin1
set termencoding=utf-8
set encoding=utf-8
set enc=utf-8
set fenc=utf-8

2.3. 指定编码打开文件

上面vim设置中,可以设定vim尝试编码的顺序,大部分情况下不会出问题。但偶然情况是不可避免的,此时需直接指定vim打开文件所用的编码。

理论上而言,可以在打开文件时指定编码:

vim file ++enc=utf-8

但这个不一定有效。如果无效,可以在打开文件后再指定编码:

:e ++enc=utf-8

2.4. 更换单个文件的编码

要更换单个文件的编码,只用先设置再保存即可:

:set fileencoding=utf-8
:w

2.5. 用 vim 批量更换编码

要批量更改文件编码,可使用vimargdo功能:

:args *.h *.cpp
:argdo set fenc=utf-8 | update

这个命令将当前目录下所有.h.cpp文件都保存为 utf-8 文件。这个有个缺点是不会修改子目录下的文件。要想将当前目录及所有子目录下的文件也修改掉,只能把前一句改成

:args *.h *.cpp */*.h */*.cpp

如果要修改孙目录的文件,就需要加更多的*/*/*.h

Q. E. D.

类似文章:
最近老遇到一个奇怪的问题。在 VS 2013 编译时,爆出很多警告:
IT » WSL, SSH
世界上最好的 Linux 发行版 Bash on Windows 已经升级到了 Ubuntu 18.04 ,并且提供越来越多的功能。下面是笔记,记录如何启动子系统的 SSHD 服务并设置开机自动启动,也顺带开机自动启动了子系统。
编程 » C++, 内存检查, Linux
获取程序占用的内存量,是一个诡异的需求。但程序写多了,有时候还真需要,尤其是代码运行出现问题的时候。
一个烦心事情是 pypyodbc 查询到的中文结果都显示为问号乱码,下面的代码可以解决这个问题。但会导致无法正常获取查询结果的字段名称(curser.description结果异常)。由于修改了 sys 全局设置,目前不清楚是否还有其它副作用。
现在相机的像素实在是太高了,上次去泰山玩,朋友的 1200 万像素的 D90 照出来的照片分辨率高达 4288×2848 ,即使转为 jpg 格式,每张都在 5M 以上。而现在电脑屏幕的分辨率最高也在 1920 以下吧,超高分辨率的照片除了打印大照片之外没什么用处,反而不方便传输、流通、保存。
编程 » Matlab
Matlab 在启动时会自动运行脚本startup.m。在这个脚本里可以自动修改当前目录,修改显示方式等等。比如
IT » Linux系统配置
查看当前 DNS :
基于@FixedIncomeAnalyst 的成果,我制作了一份完整的中国银行间和交易所债券编码和名称定义规则,并为每条规则找了个例子。如有错误请指出。
编程 » GIT
有时候不小心把一些大文件 commit 到了本地版本库,但往网络版本库同步时,因为文件过大被拒绝。如果在上传前,本地有多次提交,此时再在当前版本里删除这些大文件还不够,还需要把历史记录全删掉。效果要跟从来没提交过这些文件一样。
armadillo是一个线性代数 C++库,封装了blaslapack,提供更直观的接口。