字符集和编码II: fat/msdos/vfat | I remember a meadow one morning in May

# I remember a meadow one morning in May

## With a sky full of dreams that sailed in that day

## 字符集和编码II: fat/msdos/vfat

作者: subi 日期: 2008-08-05 发表评论 (6) 查看评论

具体到文件名乱码的问题，需要明确两点

1. 第一，文件名作为一个字符串，需要被编码后存入文件系统；
2. 第二，Linux内核无非是个特殊的应用程序，它读取文件名，再把文件名以编码后的形式传递出去。

但Linux内核只能逐字节处理编码流（而Windows NT内核是UCS-2的，逐2字节处理编码流），因此必须采用某种单字节编码（这包括所有的不定长编码）进行输出——这就是Linux内核所谓的NLS，对应于Windows的codepage。

在对文件名的处理上，fat和vfat的区别在于：fat/msdos只支持短文件名（8.3命名法），而vfat加入了对长文件名和UNICODE的支持。

为了保持与fat的兼容性，在vfat中，每个文件同时拥有“长”文件名和短文件名，其中短文件名不区分大小写（实际上是不允许小写字母出现在文件 名中）。可以这么理解，对于vfat，“长文件名”是文件真正的名字，“短文件名”则是提供兼容性的名字。举例来说，文件“真名”为abc.txt，它的 短文件名是ABC.TXT；文件“真名”为alongname.txt，它的短文件名则是ALONGN~1.TXT。

无论是fat还是vfat，短文件名按codepage编码存储，长文件名按UNICODE编码存储。因此，如果文件的真名（也就是长文件名）是 s
，短文件名是 s′
，则它们在文件系统中分别被存储为

s, φ enc( s′).

为了访问fat/vfat文件系统，我们需要用内核的msdos或vfat模块。它们有三个跟字符集有关的内核参数：codepage，iocharset，utf8。我们来确定它们对应着什么样的编码或解码函数。

- codepage=enc：用来转换短文件名中字节码为128~255的代码页。这一选项指定的是短文件名的解码函数 φ−1 enc
  （因为短文件名被codepage编码了），解码的结果是短文件名被用unicode表示；
- iocharset=enc：用来转换长文件名和解码后的短文件名。也就是说，这一选项指定编码函数 φ enc
  ，使得内核最终的输出是经过 φ enc
  编码的；
- utf8：几乎等于iocharset=utf8

这样，参数有如下的具体选择：

- codepage：这一选项只跟短文件名有关。短文件名可以通过mount -t msdos看到，也可以用windows命令行看到。不在乎短文件名的同学，这个选项完全无所谓；在乎短文件名并且使用简体中文的同学，请写

      codepage=936

- iocharset：分情况讨论。
  - 若locale不是utf8，则选择相应的locale，比如GB系就应该选

        iocharset=cp936

  - 若locale是utf8的，可以选择

        iocharset=utf8

    但这样做的缺点是会导致vfat模块将允许短文件名使用小写字母，这与windows是不兼容的（使用iocharset=utf8时，内核会出一条警告信息的——这比较没道理，估计是个历史遗留问题）；

- utf8：一个解决办法是使用utf8参数，只要iocharset!=utf8，vfat就会处理大小写的问题；而utf8参数则会最终处理字 符集的转换。utf8参数只能显式地通过mount调用（不能在编译内核时预先指定，当然也可以写在/etc/fstab里），我的意思是这个选项不能偷 懒不写。

最后是结论：

1. codepage=936，这一选项可以在内核默认设置；
2. 本地locale是gb系的，参数应为

       mount -t vfat -o iocharset=cp936

3. 本地locale是utf8的，如果能忍受内核每次都要警告，并且不跟windows交互，可以

       mount -t vfat -o iocharset=utf8

4. 要是不能忍3或者交互时出现奇怪的问题，那就

       mount -t vfat -o iocharset=cp936,utf8

   实际上只要内核默认的iocharset不是utf8，直接写

       mount -o utf8

   就可以，这里iocharset=xxx的作用仅仅是处理大小写，所以怎么填都没关系。

我们来看一下这些选项究竟做了些什么事情，假设一个FAT文件系统在简体中文的windows上用过，而Linux程序的locale设置为UTF-8，文件名为 s
，其短文件名为 s′

1. 在文件系统上（也就是在硬盘里），文件名被存为 s
   ，短文件名被存为 φ cp936( s′)
   ；
2. 内核通过codepage=936选项，将映射 φ−1 cp936
   作用于 φ cp936( s′)
   ，得到
   φ−1 cp936∘ φ cp936( s′)= s′;
3. 内核通过utf8或iocharset=utf8选项，将映射 φ utf 8
   作用于 s, s′
   ，最终向应用程序输出
   φ utf 8( s′), φ utf 8(s);
4. 应用程序（如ls、nautilus）通过locale的设置，将 φ−1 utf 8
   作用于内核输出，得到我们所看见的字符串 s, s′
   .

FLOSS i18n
发表评论？

## 6 条评论。

1. smbfs/cifs - pingback on 2013-01-31 在 15:10
2. 五一的破事：smbfs/cifs | I remember a meadow one morning in May - pingback on 2012-12-14 在 11:54
3. dylanninin 2012-12-12 在 17:10

   遇到一例samba服务器之间传送文件的乱码现象，两边的编码不一致导致。感谢你的无聊还可以帮助解决一些人的问题！

   回复
   - subi 2012-12-14 在 10:42

     有用就好～～

     主要是服务器上同时有gbk和utf8的文件名，这真的蛋疼！

     回复
4. 科士威 2010-06-18 在 23:21

   科士威顶这个好贴~~~~我的网站是http://www.evqurp.com,欢迎大家来我共创事业！

   回复
5. 五一的破事：smbfs/cifs | I remember a meadow one morning in May - pingback on 2009-12-18 在 17:40

### 发表评论 取消回复

昵称

邮箱

网址

注意 - 你可以用以下 HTML tags and attributes:
`<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">`

### Trackbacks and Pingbacks:

- smbfs/cifs - Pingback on 2013/01/31/ 15:10
- 五一的破事：smbfs/cifs | I remember a meadow one morning in May - Pingback on 2012/12/14/ 11:54
- 五一的破事：smbfs/cifs | I remember a meadow one morning in May - Pingback on 2009/12/18/ 17:40

Switch to our mobile site

