文件实际上包含什么,它们是如何“读取”的?什么是“格式”,我为什么要担心它们?

What do files actually contain, and how are they "read"? What is a "format" and why should I worry about them?

提问人:Karl Knechtel 提问时间:1/11/2023 最后编辑:Karl Knechtel 更新时间:4/4/2023 访问量:134

问:

随着使用计算机变得越来越容易,特别是进入编程领域,越来越多的初学者似乎缺乏某些基本理解,而这些理解曾经在编程界被认为是理所当然的。与此同时,随着技术的进步,这种理解的细节变得更加复杂(我个人在Unicode出现之前就开始编程了,更不用说JSON或XML了)。因此,为了有一个可靠的参考,似乎应该问:

文件到底是什么?当我们说我们“打开”和“阅读”一个文件时,我们是什么意思——我们从中得到了什么?我知道“数据”这个词,但仅仅给某物起个名字并不是一个真正的解释。

更重要的是,我们如何理解数据?如果我尝试简单地从文件中读取一些数据并将其输出到控制台,为什么它经常看起来像垃圾?为什么其他一些文件似乎有一些文本散落在垃圾中,而另一些文件似乎大部分或完全是文本?为什么仅仅要求程序读取图像文件(例如图像文件)以显示图像是不够的?同样,我知道“格式”一词,但这并不能解释这个概念。例如,如果我们说,我们根据数据的格式来理解数据,那么这只会引发另外两个问题——我们如何确定格式,以及它实际上如何提供帮助?


相关新闻: 究竟是什么原因导致二进制文件“乱码”?Python 中的“bytestring”(“bytes”数据类型)是什么?.

与文件 语言无关 的概念 数据格式

评论

0赞 deceze 1/11/2023
评论不用于扩展讨论;此对话已移至 Chat

答:

2赞 Karl Knechtel 1/11/2023 #1

数据、位和字节

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

数据本质上是信息——某种知识的记录。比特是信息的基本单位,代表尽可能少的知识量:是或否问题的答案,两个选项之间的选择,两个选项之间的决策记录。(至少需要有两种可能性;只有一种可能性,就不需要回答、选择或决定,因此,通过看到这种可能性的出现,什么也学不到。

字节只是标准大小的一组位。如今,几乎每个人都将字节定义为 8 位,主要是因为所有当代消费类硬件都是围绕这个概念设计的。在某些非常特定的技术上下文中(例如某些 C 或 C++ 语言标准文档),“字节”可能具有更广泛的含义,并且八位字节用于精确 8 位分组。我们在这里将坚持使用“byte”,因为我们现在不需要担心古老的硬件或特殊的编译器实现。

数据存储设备 - 包括 HDD 和 SSD 等永久性设备,以及 RAM 等临时设备 - 使用大量单独的组件(取决于设备)来表示数据,每个组件在概念上都可以处于两种状态中的任何一种(我们通常使用“开或关”、“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...

此外,一旦我们能表示数字,我们就可以表示各种问题的答案。例如,我们可以就文本中使用的符号序列达成一致;然后,隐式地,一个数字表示序列中该位置的符号。因此,我们可以使用一定数量的位来表示符号;通过重复表示单个符号,我们可以表示文本。

同样,我们可以表示给定时刻声波的高度;通过每秒重复这个过程数万次,我们可以表示人类可以听到的声音。

同样,在研究了人眼的工作原理后,我们发现我们可以将颜色分析为代表颜色“成分”的三个强度值(即数字)的组合。通过描述相距不远的许多点的颜色(如声波,但在二维网格中),我们可以表示图像。通过考虑跨时间的图像(每秒几十次),我们可以表示动画。

等等,等等。

选择解释

不过,这里有一个问题。所有这些都只是在谈论数据可能代表的可能性。我们怎么知道它代表什么?

显然,计算机存储的原始数据本身并不代表任何特定的东西。因为它们都是相同的常规比特序列形式,所以没有什么能阻止我们获取任何任意数据块并通过上述任何方案对其进行解释。

它只是......这样一来,它就不太可能看起来像是任何有意义的东西。

然而,解释的选择是一种选择......这意味着它可以以这种原始数据形式进行编码和记录。我们说这样的数据是元数据:告诉我们其他数据含义的数据。这可以采取多种形式:我们的文件名称和文件夹结构(告诉我们这些文件如何相互关联,以及用户打算如何跟踪它们);文件扩展名、文件开头的特殊数据或文件系统内的其他注释(告诉我们它是什么类型的文件,对应于文件格式 - 继续阅读);文档(人类可以阅读以了解另一个文件如何工作的内容);和计算机程序(告诉计算机要采取哪些步骤的数据,以便将文件的内容呈现给用户)。

什么是(file)格式?

很简单,格式是一组规则,用于描述解释某些数据(通常是文件内容)的方法。当我们说一个文件“采用”一种特定格式时,我们的意思是它 a) 根据该格式具有有效的解释(通常,并非每个可能的数据块都满足要求)和 b) 旨在以这种方式解释。

换句话说:格式是一些元数据所表示的含义

格式可以是其他格式的子集或细化。例如,JSON 文档也是使用 UTF-8 编码的文本文档。JSON 格式通过描述如何使用特定文本序列来表示结构化数据,为所表示的文本添加了其他含义。编程语言也可以被认为是这种格式:它通过解释如何将文本翻译成计算机可以遵循的指令,为文本赋予额外的含义。(计算机的“机器代码”也是一种格式,它直接由硬件而不是程序解释。

(回想一下:我们确定计算机程序可以是一种元数据,编程语言可以是一种格式,而元数据代表一种格式。闭环:当然,可以有一个实现编程语言的计算机程序——这就是编译器。

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

当我们读取文件时,实际会发生什么?

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

就是这样。

某些语言提供了便利功能,用于将数据视为文本的常见情况。这可能意味着对数据进行一些轻量级的处理(因为操作系统不同意哪些文本符号以什么顺序表示“行尾”),并使用某种编码将数据加载到语言的内置“字符串”数据结构中。(是的,即使编码是“每个字节代表一个从 0 到 255(含)的数字,它代表相应的 Unicode 码位”,也是一种编码——即使它不代表所有文本,因此不是正确的 Unicode 编码——即使程序员没有指定它,它也被使用;没有“纯文本”这样的东西, 忽视这一点可能会产生各种奇怪的后果

但从根本上说,读数实际上只是数据传输。文本转换通常被视为特殊,因为很长一段时间以来,程序员在将文本正确视为数据解释方面很草率;几十年来,人们一直将数据解释为文本——每个文本符号一个字节(顺便说一句,“字符”Unicode码位的含义不同)——如此成熟,以至于每个人都开始忘记他们实际上在使用它。程序员忘记了这一点,即使它实际上只指定了字节可能值的一半含义,而将另一半留给本地解释,即使该方案对于许多世界语言来说仍然非常不够,以至于许多其他国家的程序员提出了自己的解决方案。解决方案——上面多次提到的Unicode标准——在1991年首次发布,但今天仍然有一些程序员轻率地忽略了它。

但咆哮已经够多了。

如何解释文件?

为了显示图像、渲染网页、播放声音或文件中的任何其他内容,我们需要:

  • 拥有实际用于表示相应事物的数据;
  • 了解数据用于表示事物的格式;
  • 加载数据(读取文件,或从网络连接读取数据,或通过其他进程创建数据);
  • 根据格式处理数据。

即使是最简单的情况也会发生这种情况,并且可能涉及多个程序。例如,一个简单的命令行程序从用户输入文本(从“标准输入流”)并将文本输出回(到“标准输出流”),通常实际上不会导致文本出现在屏幕上,或者弄清楚键盘上按下了哪些键。取而代之的是:操作系统解释来自键盘的信号,以创建可读的数据;在程序写出对输入的响应后,另一个程序(终端)会将文本转换为像素颜色值(从操作系统获得帮助以从字体中选择图像);然后操作系统将安排将适当的数据发送到显示器(根据终端窗口的位置等)。