文件实际上包含什么内容,它们是如何“读取”的?什么是“格式”,为什么我应该关注它们?

3
随着计算机的普及和编程的流行,越来越多的初学者似乎缺乏曾经在编程圈中被视为理所当然的某些基本理解。与此同时,随着技术的进步,这种理解的细节变得更加复杂(我个人在Unicode出现之前就开始编程了,更不用说JSON或XML了)。因此,为了有一个坚实的参考,询问以下问题似乎是恰当的:
究竟什么是文件?当我们说“打开”和“读取”文件时,我们到底得到了什么?我知道“数据”这个术语,但仅仅给一件事物命名并不能构成真正的解释。
更重要的是,我们如何理解数据?如果我尝试从文件中读取一些数据并将其输出到控制台,为什么它经常看起来像垃圾?为什么有些其他文件似乎在垃圾中散布了一些文本,而另一些则似乎大部分或完全是文本?为什么要求程序读取图像文件以显示图像不足够?再次,我知道术语“格式”,但这并不能解释概念。例如,如果我们说我们根据其格式理解数据,那么这只会引出两个问题-我们如何确定格式,它实际上如何帮助?

相关:二进制文件“乱码”的确切原因是什么?Python中的“字节串”(`bytes`数据类型)是什么?


评论不适合进行长时间的讨论;此对话已被移至聊天室 - deceze
1个回答

2

数据、位和字节

每个必须购买硬件或安排网络连接的人都应该对“位”和“字节”的概念有所了解。它们用于测量存储设备的容量和传输速率。简而言之,它们用于测量数据:可以存储在磁盘上的数据量,或每秒沿着电缆(或通过无线连接)传输的数据量。

数据本质上是信息 - 某种知识的记录。是信息的基本单位,代表最小可能的知识数量:回答是或否的问题、在两个选项之间进行选择、记录在两个选择之间做出的决定。(至少需要两种可能性;如果只有一种可能性,那么就没有回答、选择或决定的必要,因此看到单一的可能性出现并不能获得任何知识。)

一个字节(byte)通俗来说是一组标准尺寸的位(bit)。现今,几乎所有人都将一个字节定义为8个位,这主要是因为所有当代消费类硬件都是围绕着这个概念设计的。在某些非常特定的技术上下文中(例如某些 C 或 C++ 语言标准文档),"byte" 可能有更广泛的意义,并且为了精确表示8个位的分组,会使用 "octet"。我们在此仍然使用 "byte",因为现在不需要担心古老的硬件或者特殊的编译器实现。
数据存储设备 - 包括永久性的硬盘驱动器和固态硬盘,以及临时性的随机访问存储器 - 使用大量个体组件(取决于设备)来表示数据,每个组件在概念上可以处于两种状态之一(我们通常使用“开或关”,“1或0”等作为比喻)。因为需要在这两种状态之间做出决策,所以该组件表示一个数据位。数据不是物理实体 - 它不是组件本身。它是该组件的状态:回答问题“这个组件当前配置为两种可能方式中的哪一种?”的答案。
如何利用数据
如果我们只对两个可能的数字感兴趣,那么很容易看出如何使用一个位来表示一个数字。假设这些数字是0和1;然后我们可以问,“数字是1吗?”,根据回答这个问题的位,我们知道表示的是哪个数字。
事实证明,这就是我们需要表示各种数字的全部内容。例如,如果我们需要表示来自{0,1,2,3}的数字,我们可以使用两个位:一个告诉我们所表示的数字是否在{0,1}{2,3}中,另一个告诉我们它是否在{0,2}{1,3}中。如果我们能回答这两个问题,就可以确定该数字。这种技术通过二进制算术进行概括,以表示任何整数:基本上,每个位对应于几何序列1、2、4、8、16...中的一个值,然后我们只需(隐含地)将位选择的值相加即可。通过稍微调整这个约定, 我们也可以表示负整数。如果让一些位也对应于二进制小数(如1/2、1/4、1/8...),我们可以根据所用的小数部分位数,尽可能接近地逼近实数(包括有理数)。或者,我们可以使用单独的位组来表示有理数的分子和分母,或者复数的实部和虚部。
此外,一旦我们能够表示数字,就可以表示各种问题的答案。例如,我们可以就文本中使用的符号序列达成一致;因此,一个数字代表序列中该位置上的符号。因此,我们可以使用一定数量的比特表示一个符号;通过重复表示单个符号,我们可以表示文本。
类似地,我们可以表示某一时刻声波的高度;通过重复这个过程,每秒几万次,我们可以表示人类可听到的声音。
同样地,通过研究人眼的工作原理,我们发现我们可以分析颜色,将其描述为代表颜色“成分”的三个强度值(即数字)的组合。通过在一个二维网格上描述许多相距不远的点的颜色(就像声波一样),我们可以表示图像。通过考虑随时间变化的图像(每秒几十次),我们可以表示动画。
等等,等等。
选择一种解释
然而,这里存在一个问题。所有这些只是可能性,数据可能代表什么。我们如何知道它实际上代表了什么?
明显地,计算机存储的原始数据本身并不代表任何具体的东西。因为它们都以相同的比特序列形式存在,所以我们可以采用上述任意一种方案对任意数据块进行解释。
然而,这种方式很可能不会呈现出任何有意义的东西。
然而,解释的选择是一个选择…这意味着它可以被编码和记录在这种原始数据形式中。我们称这样的数据为元数据:告诉我们其他数据的含义的数据。这可以采取许多形式:文件名和文件夹结构的名称(告诉我们这些文件之间的关系,以及用户打算如何跟踪它们);文件名的扩展名、文件开头的特殊数据或其他在文件系统中做的注释(告诉我们它是什么类型的文件,与文件格式相对应-继续阅读);文档(人类可以阅读的东西,以便理解另一个文件的工作方式);和计算机程序(告诉计算机采取哪些步骤,以向用户呈现文件内容)。

什么是(文件)格式?

简而言之,格式是描述解释某些数据(通常是文件内容)的方法的一组规则。当我们说一个文件“处于”特定的格式中时,我们指的是它a)根据该格式具有有效的解释(通常不是所有可能的数据块都符合要求),并且b)旨在以这种方式进行解释。
换句话说,格式是由某些元数据表示的含义。
格式可以是某种其他格式的子集或细化。例如,JSON文档也是使用UTF-8编码的文本文档。JSON格式通过描述如何使用特定的文本序列来表示结构化数据,为所代表的文本添加了额外的含义。编程语言也可以被认为是这种格式:它通过解释如何将文本转换为计算机可以遵循的指令来为文本添加额外的含义。(计算机的“机器码”也是一种格式,直接由硬件解释而不是由程序解释。)
回顾:我们已经确定计算机程序可以是一种元数据,编程语言可以是一种格式,元数据代表一种格式。为了闭环:当然,一个计算机程序可以实现一种编程语言——这就是编译器。

一个格式也可能涉及多个步骤,由不同的标准解释。例如,Unicode 是事实上的标准文本格式,但它仅描述抽象数字如何对应文本符号。它并没有直接说明如何将位转换为数字(这确实需要指定;“将每个字节视为 0..255 的数字” a) 仍然会选择许多可能的方法来做到这一点;b) 不足够,因为可能的文本符号要多得多)。为了表示文本,我们还需要一种编码方式, 即数据格式的其余规则,特别是将位转换为数字。 UTF-8 是其中一种编码方式已成为主流

我们读取文件时实际发生了什么?

原始数据从磁盘上的文件传输到程序的内存中。

就是这样。

一些编程语言为常见情况提供方便功能,比如把数据视为文本。这可能意味着对数据进行轻微处理(因为操作系统对于哪些文本符号以及以何种顺序表示“行尾”存在差异),并使用某种编码将数据加载到语言的内置“字符串”数据结构中。(是的,即使编码是“每个字节表示从0到255之间的一个数字,该数字代表相应的Unicode代码点”,那也是一种编码,即使它不能表示所有文本,因此不是一个合适的Unicode编码,并且即使程序员没有指定,它也被使用不存在所谓的“纯文本”,忽略这一点会导致各种各样奇怪后果。)

但从根本上说,阅读实际上只是数据的传输。文本转换通常被视为特殊,因为很长一段时间里,程序员们在正确处理文本作为数据解释方面做得很草率;几十年来,将数据解释为文本——每个文本符号一个字节(顺便说一下,"字符"并不意味着与 Unicode 代码点相同)——已经非常成熟,以至于每个人都开始忘记他们实际上正在使用它。尽管这种方案实际上仅规定了一个字节的一半可能值的含义,并且留下另一半 由本地解释,但程序员们却忘记了这一点,而且这种方案对许多世界语言来说仍然极其不足,因此许多其他国家的程序员 想出了自己的解决方案。解决方案——Unicode 标准,在上面提到了多次——于1991年首次发布,但今天仍有一些程序员漠视它。
足够的抱怨就到此为止吧。
文件解释是如何工作的?
为了显示图像、渲染网页、播放声音或从文件中实现其他任何事情,我们需要:
- 有数据,这些数据实际上是用来表示相应事物的; - 知道数据所使用的格式来表示该事物; - 载入数据(读取文件,或从网络连接读取数据,或通过其他进程创建数据); - 根据格式处理数据。
这种情况甚至在最简单的情况下也会发生,并且可能涉及多个程序。例如,一个简单的命令行程序,从用户输入文本(从“标准输入流”)并输出文本返回(到“标准输出流”),通常实际上并没有导致文本出现在屏幕上,或者弄清楚键盘上按下了哪些键。相反:操作系统解释来自键盘的信号,以创建可读数据;程序将其响应写入输入后,另一个程序(终端)将把文本转换为像素颜色值(从字体中获取帮助选择图像);然后操作系统将安排发送适当的数据到显示器(根据终端窗口的位置等)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接