【问题标题】:Force Git to read ANSI files containing NUL强制 Git 读取包含 NUL 的 ANSI 文件
【发布时间】:2021-01-04 22:15:35
【问题描述】:

有很多很多地方描述了如何“强制”Git 将文件作为文本读取。通常,该解决方案涉及向.gitattributes 添加过滤器以将text 属性应用于文件。示例包括:

* text
* text=auto
* text diff merge
* text=auto diff merge

但如果文件包含 NUL,此解决方案似乎不起作用。这是一个带有 ANSI 编码和尾随空字节的示例文件文本文件:

它完全可以作为文本文件读取,只是 Git 无法读取。上面的每个示例过滤器都将失败,并且 Git 将无论如何都标识为“二进制”。我认为这是因为它在前 8000 个字符 (ref) 中对 NUL 进行了硬编码检查。

当然,只要我将文件转换为 UTF-8,Git 就会愉快地将其识别为文本。这是转换后的同一个文件:

坦率地说,我不介意不使用 ANSI 编码。我只是想避免在 Notepad++ 中不断打开文件只是为了修复文件编码。有没有办法让 Git 自动处理编码转换?

【问题讨论】:

  • 没有主要的单字节编码(无论是否所谓的“ANSI”),其中 NUL 不是 NUL。同样的字节也是 UTF-8 中的 NUL。在将它们转换为 UTF-8 时,您的工具实际上会错误地去除这些字符。根据 POSIX,这根据定义不是文本文件,因为 NUL 在文本文件中永远不会有效。

标签: git character-encoding nul


【解决方案1】:

这里有几个问题。首先是这些绝对是 not 文本文件,因为它们包含一个 NUL 字节。没有主要的单字节编码允许 NUL 字节表示 NUL 以外的任何内容,因为 C 以该字节终止其字符串,并且将其用于其他目的意味着该编码中的文本不适合正常的 C 字符串。出于这个原因,POSIX 专门将包含 NUL 字节的文件排除在文本文件之外。

您用来将“ANSI”文件转换为 UTF-8 的工具实际上是去除 NUL 字节,这就是它们工作的原因。 UTF-8 中的 NUL 字节与单字节编码中的含义完全相同:NUL。所以这是可行的,因为您的工具正在剥离它们而不是正确转换它们。

在这种情况下,您还不清楚您要求 Git 做什么。 text 属性要求 Git 执行行尾规范化。但是,如果您的文件包含 NUL 字节,那么 Git 仍然会认为它是用于差异和合并目的的二进制文件,因为 text 属性无法控制这一点。您还需要 diff 和 merge 属性。

当然,如果您真的不想要或不需要 NUL 字节,并且这些字节应该是人类可读的,那么您最好还是去掉 NUL 字节并转换为 UTF-8。在 2020 年,不再有任何理由使用单字节编码。如果这是您想要做的,那么您可以剥离 NUL 字节并通过执行以下操作转换为 UTF-8(假设您使用的是 Git Bash、WSL 或 Linux 系统):

$ tr -d '\0' FILENAME | iconv -f WINDOWS-1252 -t UTF-8 > FILENAME.tmp && \
  mv FILENAME.tmp FILENAME

这还假设您使用的“ANSI”编码实际上是 Windows-1252。 IANA(字符集寄存器)不知道任何称为“ANSI”的编码,但 Windows-1252 是最常见的字符集。

最后,如果您绝对必须处理非 UTF-8 文件,您可以在 gitattributes 中使用 working-tree-encoding 值指定工作树编码。不过,这并不能解决您的 NUL 问题,而且 UTF-8 在几乎所有情况下都是更好的选择。

【讨论】:

  • No.No.大多数编码中都允许使用 NULL,它不是以 C 结尾的字符串。 C选择这样的约定,也叫ASCIIZ。其他协议只是使用新行作为终止(或特定的转义)。 Pascal、Python(和许多其他语言)允许您在字符串中包含 NULL,对于 ASCII 字符串也可以。有 UTF-8 编码(不是来自 Unicode 的官方编码,但兼容)允许字符串中的 NULL 和 \0 终止字符串。
  • 我不会争论 C 以外的任何语言。我完全清楚 NUL 在许多语言的字符串中都是允许的。然而,它是prohibited in text files by POSIX,我坚持我的说法,即没有主要的单字节编码使用零字节作为 NUL 以外的任何东西。
  • ASCII 允许 NULL。问题与 POSIX 无关(没有人会在 POSIX 中使用“ANSI”进行编码),并且所讨论的语言不是 C(但如果有人使用 #define If if 等等)。 Linux 使用 null(例如在/proc 中,作为字段分隔符(其余为文本),而git 不仅仅是文本文件(在 posix 定义中,实际上它允许不同的行尾字符)。
  • 我完全理解 Git 在这种情况下是如何工作的,并且它可以处理任何类型的文件;我是核心贡献者。 “文本文件”的普遍理解定义(与“二进制文件”相对)不包括带有 NUL 的字节;使用标准定义(例如 POSIX)来定义术语“文本文件”是完全合理的。例如,file 会将这些文件称为“数据”,因为它们包含 NUL,而不是“文本”;有关详细信息,请参阅手册页。
  • @bk2204 file 显然认为文件具有奇数个 NUL 字符数据 printf 'foo\0 | file - -> data 但偶数个 ascii printf 'foo\0\0' | file - ?‍♂️
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-24
  • 1970-01-01
  • 2011-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多