了解Unix文件名编码


25

我很难理解文件名编码的工作方式。在unix.SE上,我发现矛盾的解释。

文件名存储为字符

引用另一个答案: 关于Linux上文件系统字符编码的几个问题

正如您在问题中提到的[…],UNIX文件名只是一个字符序列;内核对编码一无所知,而编码完全是用户空间(即应用程序级)的概念。

如果文件名存储为字符,则必须进行某种编码,因为最后文件名必须以磁盘上的位或字节序列结尾。如果用户可以选择任何编码来将字符映射到馈送给内核的字节序列,则可以为有效文件名创建任何字节序列。

假设以下内容:用户使用随机编码X,它将文件foo转换为字节序列α并将其保存到磁盘。另一个用户使用编码ÿ。在这种编码中,α转换为/,不允许作为文件名。但是,对于第一个用户,该文件有效。

我认为这种情况不会发生。

文件名存储为二进制Blob

引用另一个答案: 在Linux上,文件名和路径使用哪种字符集编码?

正如其他人所指出的那样,这确实没有答案:文件名和路径没有编码。OS仅处理字节序列。各个应用程序可能选择将它们解释为以某种方式进行编码,但这有所不同。

如果系统不处理字符,如何在文件名中禁止使用特定字符(例如/NULL)?没有/ 编码就没有a的概念。

一种解释是,文件系统可以存储包含任何 字符的文件名,只有考虑到编码的用户程序才会阻止包含无效字符的文件名。反过来,这意味着文件系统和内核可以毫无困难地处理包含/。的文件名。

我也认为这是错误的。

编码在哪里进行,不允许使用特定字符的限制在哪里?


在所有编码中,Null均相同(0)。
凯文(Kevin)

2
@Kevin不太:不在UTF-16或UCS-4(= UTF-32)或大多数不是ASCII扩展名的其他多字节编码中。
吉尔斯(Gilles)'“ SO-不要邪恶”

1
实际上,Riccardo Murri的答案应该提到字节而不是字符。大多数文件系统确实存储字节。
吉勒斯(Gillles)'所以

@吉尔斯:又一次Ī̲看到你真的在看写的东西
Incnis Mrsi 2015年

Answers:


25

简短的答案:在Unix / Linux / BSD内核中施加的限制,namei()功能。编码发生在用户级程序一样xtermfirefoxls

我认为您是从错误的前提开始的。Unix中的文件名是带有任意值的字节字符串。只是不允许使用0x0(ASCII Nul)和0x2f(ASCII'/')这几个值,不是多字节字符编码的一部分,也不是任何值。“字节”可以包含代表字符的数字(以ASCII和其他一些编码形式),但是“字符”可以要求超过1个字节(例如,以Unicode的UTF-8表示形式位于0x7f以上的代码点)。

这些限制来自文件名打印约定和ASCII字符集。原始Unix使用ASCII'/'(数字0x2f)值的字节来分隔部分或完全限定的路径(例如,'/ usr / bin / cat'具有“ usr”,“ bin”和“ cat”部分) 。最初的Unix使用ASCII Nul终止字符串。除了这两个值以外,文件名中的字节可以采用任何其他值。您可以在Unicode的UTF-8编码中看到这一点的回声。可打印的ASCII字符(包括“ /”)在UTF-8中仅占用一个字节。以上代码点的UTF-8除了Nul控制字符外,不包含任何零值字节。UTF-8是为Plan-9(Unix的宝座)发明的。

较早的Unix(看起来像Linux)具有一个namei()功能,该功能一次仅查看一个字节的路径,然后将路径分成0x2F个值字节的片段,并停止在一个零值字节处。 namei()是Unix / Linux / BSD内核的一部分,因此这是强制执行异常字节值的地方。

请注意,到目前为止,我已经讨论了字节值,而不是字符。 namei()不会在字节上强制执行任何字符语义。这取决于用户级别的程序,例如ls,该程序可以根据字节值或字符值对文件名进行排序。xterm根据字符编码确定要为文件名点亮的像素。如果您不知道xterm自己拥有UTF-8编码的文件名,则在调用它时会看到很多胡言乱语。如果vim未编译为检测UTF-8(或其他任何UTF-16,UTF-32)编码,则在打开包含UTF-8编码字符的“文本文件”时,您会发现很多乱码。


正确,namei()是在1986年左右被废弃的。较新的UNIX系统使用lookuppn()基于VFS的系统。
schily

17

关键是,内核根本不在乎应用程序如何解释以文件名形式给出的数据。

假设我有一个C应用程序专门处理UTF-16字符串。然后,通过正确配置的输入法,将∯符号(Unicode 0x222F)输入“另存为”提示/对话框。

如果应用程序不执行任何形式的转换,而是以普通的旧C字符串(char*)将其发送给(例如)fopen写入模式,则内核将看不到∯,甚至无法想象。它将看到两个chars,一个接一个,另一个带有值0x22 0x2F(假设C库中为 8位字符,而没有funny)。
也就是说,从内核的角度来看,有效的char(")后跟/(ASCII 0x2F)。fopen将返回EISDIR(即“看起来像目录,您请求了写模式!”)。
如果输入∮(Unicode 0x222E),内核将看到两个很好的字符,并创建了一个文件,该文件通过使用ASCII的应用程序可以看到,该文件将被命名为".

如果我a以文件名的形式输入应用程序,并且应用程序以UTF-16的形式将其传递给内核,则内核将读取0x00 0x61,实际上甚至不考虑0x61,因为0x00该字符串已经终止了字符串。关心。错误消息将与空文件名相同(ENOENT我相信)。

因此,内核确实会将数据视为Blob。它是chars 的流。您选择的用户空间编码中的无效“字符”是在其Blob(传递给内核的二进制表示形式)中生成0x000x2F(“ null”和/)的字符。


如果我理解正确,那么就没有无效字符之类的东西。只有无效的字节序列。值0x000x2F在内核中进行了硬编码。反过来,这意味着目录不是用分隔/,而是0x2F使用的编码中映射到的任何字符。
马可(Marco)

是的,如果您希望以这种方式看到,那就是这个主意。(但是,这可能是不正确的。内核可能具有“本地编码”,/而不是0x2F- chars实际上可能不使用8位。)“传统”目录分隔符为/。在8位字节ASCII(例如,不是EBCDIC)系统上为0x27。
Mat

您假定使用UTF-16BE,而在UTF-16LE中,U + 0061将导致(以空终止)a字符串。
Incnis Mrsi 2015年

4

在设计Unix之后,字节与字符之间的分隔就很多了。在设计时,单词的使用仅传达了有关如何解释8位(或6位或9位)的信息,但未提及单词编码

文件名是字节序列。允许使用除0x2f“ /”以外的任何字节。包含0x00的字节甚至不能用作内核,因为它用作字符串终止符。应用程序可以根据其选择的编码来解释字节序列。如果听起来很乱,我想是。

http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html上有更多信息,您可能会觉得有用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.