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

  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的代码页。这一选项指定的是短文件名的解码函数φ−1enc(因为短文件名被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选项,将映射φ−1cp936作用于φcp936(s′),得到
    φ−1cp936∘φcp936(s′)=s′;
  3. 内核通过utf8或iocharset=utf8选项,将映射φutf8作用于s,s′,最终向应用程序输出
    φutf8(s′),φutf8(s);
  4. 应用程序(如ls、nautilus)通过locale的设置,将φ−1utf8作用于内核输出,得到我们所看见的字符串s,s′.

最新文章

  1. 【bzoj1178】 Apio2009—CONVENTION会议中心
  2. 【bzoj1082】 SCOI2005—栅栏
  3. from live writer
  4. ArrayList源码
  5. 【转】android adb命令
  6. discuz 和 wordpress 整合注意问题
  7. querySelectorAll 方法相比 getElementsBy 系列方法有什么区别
  8. sort uniq妙用
  9. Linux下使用Photorec恢复误格U盘
  10. Spring Boot简单xml配置集成mybatis
  11. Docker的基本概念
  12. opencart分类筛选逻辑修改为并且条件
  13. Human Motion Analysis with Wearable Inertial Sensors——阅读2
  14. 解决 shopnc b2b2c 版权问题 修改路经ULR及目录文件夹思路及教程
  15. git设置HTTP代理
  16. redis学习笔记之redis简介
  17. 创建一个简单的 MDM server(1)
  18. Java Spring-事务管理
  19. OpenTLD在VS2012和opencv246编译通过
  20. pictureBox

热门文章

  1. vim-进入插入模式快捷键
  2. 7.Web Service 调用天气代码
  3. C++的继承和Java继承的比较
  4. 洛谷 P2105 K皇后
  5. 洛谷 P2837 晚餐队列安排
  6. POJ 2481 Cows (线段树)
  7. php实现希尔排序(总结)
  8. Filebeat之input和output(包含Elasticsearch Output 、Logstash Output、 Redis Output、 File Output和 Console Output)
  9. 轻松学习Linux之Shell预定义变量
  10. BZOJ1492: [NOI2007]货币兑换Cash(CDQ分治,斜率优化动态规划)