您所描述的问题是一系列(非常常见
1但可惜)的错误和误解。让我尝试详细说明正在发生的事情,希望您能花时间阅读所有材料:内容很长,但这些都是任何程序员都应该掌握的非常重要的基础知识。如果您没有完全理解其中的所有内容,请不要绝望:只需尝试玩弄它,一周或两周后回来,练习一下,看看会发生什么🙂。
字符“编码”和字符“集合”的概念之间有一个关键区别。除非您真正理解这种差异,否则您永远无法真正理解正在发生的事情。Joel Spolsky(Stackoverflow的创始人之一)在一篇文章中解释了这种差异:
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)。在继续阅读本文,在继续编程之前,首先阅读该文章。说实话,阅读并理解它:标题并非夸张。您必须绝对了解这方面的知识。
之后,我们来继续:
当一个 C 程序运行时,一个用于保存类型为“char”的值的内存位置,与任何其他内存位置一样,都包含一个由1和0组成的序列。变量的“类型”只对编译器有意义,对于只看到1和0而不了解更多内容的运行程序而言并没有什么意义。换句话说,当您通常认为“字母”(字符集合中的元素)驻留在某个内存位置时,实际上存在的是一个二进制位序列(字符编码中的元素)。
每个编译器都可以自由地使用它所选择的任何编码来表示内存中的字符。因此,它可以把我们称为“换行符”的东西内部表示为任何它选择的数字。例如,假设我编写了一个编译器,我可以约定每次想要将“换行符”存储在内部时,我将其存储为数字6,这只是二进制中的0x6(或110)。
写入文件是通过同时告诉操作系统
2四件事情来完成的:
- 您要写入文件(
fwrite()
)
- 您要写入的数据从哪里开始(作为
fwrite
的第一个参数)
- 您要写入多少数据(第二个和第三个参数相乘)
- 您要写入哪个文件(最后一个参数)
注意,这与数据的“类型”无关:您的操作系统不知道也不关心字符集,并不知道任何有关字符集的信息。它只看到从某个位置开始的一系列二进制数字,并将其复制到文件中。
以“二进制”模式打开文件实际上是处理文件的正常直观方式,一个初学者程序员所期望的方式:你指定的内存位置会被一对一地复制到文件中。如果你写入一个曾经存储了编译器决定存储为“char”类型的变量的内存位置,那么这些值将会被一对一地写入到文件中。除非你知道编译器如何在内部存储值(它将换行符、字母'a'、'b'等与哪个值相关联),否则这是没有意义的。将这与乔尔关于文本文件没有了解其编码就是无用的类似观点进行比较:同样的问题。
在“文本”模式下打开文件几乎等同于二进制模式,只有一个(且仅有一个)区别:每当写入一个值等于编译器内部使用的换行符值(6,在我们的例子中),它就会将不同于该值的内容写入文件:不是该值,而是操作系统认为的换行符。在Windows中,这是两个字节(13和10,或0x0d 0x0a)。请再次注意,如果您不知道编译器的内部表示方式,则这仍然是没有意义的。
在这一点上,请注意,除了编译器指定为字符的数据之外,以文本模式将任何东西写入文件都是一个坏主意:在我们的例子中,一个6可能只是你要写入的值之一,在这种情况下,输出会以我们绝对不想要的方式被更改。
(不)幸运的是,大多数(全部?)编译器实际上都使用相同的字符内部表示:这个表示是US-ASCII,它是所有默认值的原型。这就是您可以编译带有任意随机编译器的程序并将某些“字符”写入文件,然后使用文本编辑器打开它的原因:它们都使用/理解US-ASCII,并且它们恰好可以工作的原因。
现在来把这与您的示例联系起来:为什么在二进制模式下和文本模式下写入“test”没有区别?
因为“test”中没有换行符,这就是为什么!
当您“打开文件”然后“看到”字符时,这意味着您用于检查该文件中的一系列二进制数字的程序(因为硬盘上的所有内容都是一系列二进制数字)决定将其解释为US-ASCII,并且这恰好是您的编译器在其内存中对该字符串进行编码的方式。加分项:编写一个程序,将文件中的二进制位读入内存,并将每个BIT(一个字节由多个位组成,要提取它们,您需要知道“按位”运算符技巧,请谷歌!)作为“1”或“0”打印给用户。请注意,“1”是字符1,在您选择的字符集中,因此您的程序必须将一位(数字1或0)转换为表示终端仿真器使用的编码中字符1或0所需的位序列,以便在上面查看程序标准输出的终端仿真器。好消息是:您可以假设在所有地方都使用US-ASCII而采取许多捷径。该程序将向您展示您想要的内容:编译器在内部表示“测试”的位序列。
对于新手来说,这些东西确实很令人望而生畏,我知道即使是我也花了很长时间才知道字符集和编码之间的区别,更不用说它们是如何工作的了。希望我没有让你失去动力,如果我这样做了,只要记住您永远无法失去已经拥有的知识,只能获得它(好的,不总是正确的:P)。在生活中,一个陈述引发更多问题而不是回答是很正常的,苏格拉底知道这一点,他的智慧无缝地适用于现代技术2400年后。
祝你好运,继续提问。对其他读者:如果您看到错误,请随时改进此帖子。
Hraban
1. 告诉您“以二进制形式保存文件可能更小”的人,例如,可能严重误解了这些基础知识。除非他指的是在保存之前压缩数据,否则他只是使用“二进制”来代替“压缩”的一个令人困惑的词语。
2. “告诉操作系统某些信息”通常称为系统调用。