我的问题是:为什么没有特定的“分隔符”字符?一种可用于所有类型的定界。我们有用于换行、打印设置等的特殊字符...
如果这些是常见的文本字符,为什么我们有时会使用逗号、空格、制表符等。这背后有历史吗?就像他们在制作 ASCII 或类似的东西时可能不需要分隔符?
(对我来说似乎有意义的是:有一个特殊的分隔符,它的唯一目的是在需要时“分隔”单独的值)
我的问题是:为什么没有特定的“分隔符”字符?一种可用于所有类型的定界。我们有用于换行、打印设置等的特殊字符...
如果这些是常见的文本字符,为什么我们有时会使用逗号、空格、制表符等。这背后有历史吗?就像他们在制作 ASCII 或类似的东西时可能不需要分隔符?
(对我来说似乎有意义的是:有一个特殊的分隔符,它的唯一目的是在需要时“分隔”单独的值)
定界符已存在于ASCII中。十进制 28-31(十六进制 1C-1F)是分隔符。这包括文件、记录、组和单元分隔符。
我假设我们不使用它们,因为键入不需要多个键来键入单个字符的键盘字符更容易。这也允许在不同格式之间更轻松地进行交换。逗号分隔的值几乎可以在任何系统上工作,无论是否兼容 ASCII。
如前所述,ASCII 包含分隔符。问题不在于在数据输入过程中需要额外的键来包含分隔符 - 对于大写字母或其他特殊可打印字符(例如,!@#$),控制并不比 Shift 更难使用。问题是传统上这些控制字符不是直接可见的。即使是制表符、回车和换行——它们会立即产生动作,也不会产生可见的输出。
您无法区分制表符和空格之间的电传打字机。您无法区分换行符和空格到行尾+换行到下一行。同样,分隔符没有定义的可打印图像。它们可能会显示在某些(现代)文本编辑器中,并且它们可能会在各种设备中立即产生动作,但它们不会留下痕迹。
如果数据只是被设计成机器可读的,那么所有这些都无关紧要——即我们通常所说的二进制文件。但是用于数据输入和系统之间传输的文本通常是有意为人类可读的。如果它将是人类可读的,则分隔符需要是可打印的。
正如另一个答案中提到的,ASCII 确实有分隔符。看这里[1],提到了这些:
这些都被使用了。例如,U+001C(八进制 34)是
SUBSEP
GNU AWK 的默认 [2] 字符串。这主要是历史原因。
在信息学的旧时代,数据文件主要是固定宽度字段文件,因为它是 Fortran IV 和 COBOL 等语言的自然 IO:第一个字段为 n 个字符,第二个字段为 m,等等。
然后 C 语言提供了一个
scanf
功能,可以在(组)空格上分割输入,人们开始对包含数字的数据文件使用自由格式。scanf
但是当某些字段可能包含空格(被称为穷人的解析器)时,这会导致混乱的结果。因此,由于另一个标准的拆分功能是strtok
使用单个分隔符,大多数(说英语的)人开始使用逗号 (,
) 作为分隔符,因为在文本编辑器中手动编写逗号分隔值文件很容易。然后国家语言支持进入游戏......在一些欧洲语言(法语)中,小数点是逗号。IT 人员习惯了小数点,但较少的技术人员不习惯,因此法语版本的 Windows 开始将分号 (
;
) 定义为分隔符,以允许十进制数中的逗号。与此同时,一些人意识到,当字段的长度总是很接近时,制表符(存在于所有键盘上)可以提供很好的垂直对齐,这就是第三个标准的原因。
最后,标准化开始成为事实,RFC 4180 于 2005 年出现。它确实将逗号定义为官方分隔符,但由于 Windows 决定玩 NLS 游戏,想要处理真实文件的工具和库必须适应各种可能的分隔符。
这就是为什么在 2021 年,我们在 CSV 文件中有许多可能的分隔符......
事实证明,ASCII中有一个事实上的通用分隔符:空字符。Unix 和 C 语言表明,您可以构建一个完整的平台,在该平台中,空字符从字符串中消除,在其表示中充当终止符。其他平台也纷纷效仿,例如 Microsoft Windows。
今天,它几乎是铁板钉钉的保证,没有文本数据包含空字节。如果数据包含空字节,则它是二进制而不是文本。
如果你想在字节流中存储一系列文本记录或字段,如果你用空值分隔它们,你将几乎没有问题。Null 不需要任何诸如转义之类的废话。如果有人过来说他们想在文本字段中包含一个空字节,你可以把他们当作喜剧演员一笑置之。
野外空值分离的例子:
Microsoft 允许注册表中的项目是多字符串:包含多个字符串的单个项目。这存储为一系列以空字符结尾的字符串,它们连接在一起,并带有一个额外的空字节来终止整个序列。如
"the\0quick\0brown\0fox\0\0"
表示字符串列表"the"
,"quick"
,"brown"
,"fox"
.在 Linux 内核上,每个进程的环境变量都可以通过
/proc
文件系统作为/proc/<pid>/environ
. 此虚拟文件使用空分隔符,例如PATH=/bin:/usr/bin\0TERM=xterm\0...
.一些 GNU 实用程序可以选择生成以空值分隔的输出,这正是使它们能够用于编写更健壮的脚本的原因。GNU
find
有一个-print0
谓词用于打印带有空终止而不是换行符分隔的路径。这些路径可以被馈送到xargs -0
从其标准输入中读取空分隔字符串并将它们转换为指定命令的命令行参数。这个组合将干净地传递所有文件名/路径,无论它们包含什么:因为路径不能包含空字节。为什么我们玩游戏与其他分离?制表符、逗号、分号等等,而不仅仅是使用 null?问题是我们需要多层次的分离。好的,所以空值将字节流可靠地切割成文本。但在这些文本中,可能需要另一个层次的划界。有时会发生单个字符串内部具有更多结构的情况。路径包含用于分隔组件的斜线。MAC 地址使用冒号分隔字节。之类的东西。电子邮件地址有多个级别的嵌套分隔符,例如符号
local@domain
周围@
,然后域部分用点分隔。括号是允许的,比如%
和!
. 人们编写字符串处理代码来处理这些格式,并且由于受 C 和 Unix 的影响,字符串处理代码在很多语言中都不会喜欢空字节。使用空字节作为字段分隔符的 GNU Awk 演示,处理
/proc/self/environ
.由于末尾的空字节,我们得到了一个额外的空白字段,因为 Awk 将其视为字段分隔符,而不是终止符。然而,这恰恰是可能的,因为 GNU Awk 允许空字节成为字符串的组成部分。根据 POSIX 规范,该参数
-F '\0'
不需要起作用。POSIX 在题为“awk 中的转义序列”的表中说因此,依靠 Awk 来分隔空字节上的字段或记录是完全不可移植的。这种语言问题可能是我们不更多使用空字符的原因之一。
为了进一步阐明@SergeBallesta 给出的历史,在新采用的 ASCII(我们正在谈论大型机)的初期,一般目的是标准化系统之间的输入代码,以便每个人都在同一页上。系统制造商之间为了保持他们的产品专有(本质上只能在他们的系统上使用的东西)发生了很多争执,这不利于便携性。这个问题主要与将程序或输入和输出从一个系统转移到另一个系统有关。例如,可以编写一个输出磁带,其中包含在一个系统上使用的数据输入文件、一些输出文件和一些 FORTRAN 程序文件,然后将该磁带带到由不同制造商制造的另一个系统,然后发现该磁带不可读. 房间里的大人物IBM,有一个很好的标准化平台,EBCDIC,它适用于 ASCII,只对 EBCDIC 字符集的二进制编码进行了微小的更改。每个人都同意这一点。到那时,唯一的标准化字符集是在一个打字机!
然而,回到牧场,编程在很大程度上与简单地读取程序员确定格式的输入数据、在程序中处理这些数据以及生成也是程序员确定格式的输出有关。不需要分隔符。格式化输入和输出最密集的用途之一是使用 FORTRAN 编程语言。例如,数据将在 80 列 Hollerith 卡上以特定的、有组织的输入格式键入,该格式由对程序的输入段进行编程的人确定。一切都是由程序员/用户格式化、标准化和设计的。输入数据没有逗号分隔之类的东西。输出打印在 132 列宽幅、边缘穿孔纸上。输出也在 80 个列卡上打孔。 标准化格式输入和输出不需要分隔符来分隔输入数据。一切都很好地列出来了。输入数据整齐地用制表符分隔;打印输出在带有标题的表格列中,一切都很好而且整洁。
必须记住,在 FORTRAN 中,程序员可以进行打印机托架控制的所有方面。事实上,程序员完全负责理解如何在 FORTRAN 程序中操作 132 列打印机,以及如何在计算机内存中表示输出,以便有效地输出到打印机或磁带,其中所述文件可能是实时的- 打印、在终端上查看或稍后打印。
随着个性化桌面计算的出现,这一切都发生了变化,因为数据输入和输出变得完全电子化。是的,仍然有格式化的输入和输出文件,但是编程环境变得更加符合用户的需求。输入预处理器(子程序)可以逐卡或逐行读取格式化输入为80 列卡片的图像或 132 列输出页面的文件,通过查找尾随以逗号分隔所需输入的空格,并重写到内存中的临时文件。不需要 FORTRAN 兼容的格式化硬拷贝输入。使用标准化的字符集,这一切都非常容易,而 ASCII 使这种标准化成为可能。事实上,做到这一点的关键是逗号,已经是 ASCII。然后可以通过专门使用来自逗号分隔数据文件的输入的软件重新读取临时文件。现在一切都变了!只需要短短的几年时间就可以将大型机推向历史,并将最先进的技术提升到全新的飞机上。