我的问题是:为什么没有特定的“分隔符”字符?一个可以用于所有类型的分隔符。我们有用于换行符、打印设置等的特殊字符...
如果逗号、空格、制表符等是常见的文本字符,为什么我们有时会使用它们。这背后有什么历史吗?比如,也许在制作 ASCII 或类似字符时,它们并不需要分隔符?
(对我来说似乎有意义的是:有一个特殊的分隔符,它的唯一目的是在需要时“分隔”单独的值)
答案1
分隔符已存在于ASCII。十进制 28-31(十六进制 1C-1F)是分隔符。这包括文件、记录、组和单元分隔符。
我认为我们不会使用它们,因为输入键盘字符更容易,不需要多个键来输入一个字符。这也使得不同格式之间的交换更容易。逗号分隔值几乎可以在任何系统上使用,无论是否兼容 ASCII。
答案2
如上所述,ASCII 包含分隔符。问题是不是在输入数据时需要额外的键来包含分隔符 - 对于大写字母或其他特殊可打印字符(例如 !@#$),使用 Control 并不比使用 Shift 更难。问题是传统上这些控制字符不直接可见. 甚至制表符、回车符和换行符 - 可立即产生行动,不会产生可见的输出。
您无法区分电传打字机上的制表符和空格。您无法区分换行符和空格到行尾 + 换到下一行。同样,分隔符没有定义的可打印图像。它们可能会显示在某些(现代)文本编辑器中,并且可能会在各种设备中产生即时操作,但它们不会留下任何痕迹。
如果数据仅设计为机器可读,那么所有这些都不重要——也就是我们通常所说的二进制文件。但用于数据输入和系统间传输的文本通常有意设计为人类可读的。如果要使文本可读,分隔符必须可打印。
答案3
正如另一个答案中提到的,ASCII 确实有分隔符。在这里 [1] 中提到了这些:
代码点 | 姓名 |
---|---|
U+001C | 文件分隔符 |
U+001D | 组分隔符 |
U+001E | 记录分隔符 |
U+001F | 单位分隔符 |
并且使用这些。例如,U+001C(八进制 34)是SUBSEP
GNU AWK 的默认 [2] 字符串。
答案4
事实是,有一个事实上ASCII 中的通用分隔符:空字符。Unix 和 C 语言表明,您可以构建一个完整的平台,其中空字符从字符串中被消除,作为字符串表示中的终止符。其他平台也纷纷效仿,例如 Microsoft Windows。
如今,几乎可以肯定文本数据不包含空字节。如果数据包含空字节,则其为二进制而非文本。
如果您想要将一系列文本记录或字段存储在字节流中,并且用空值分隔它们,则几乎不会出现任何问题。空值不需要转义之类的无用操作。如果有人说他们想在文本字段中包含一个空字节,您可以像喜剧演员一样嘲笑他们。
现实中的零分离示例:
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?问题是我们需要多级分隔。好的,null 可以可靠地将字节流切成文本。但是在这些文本中,可能需要另一级分隔。有时单个字符串内部包含更多结构。路径包含用于分隔组件的斜杠。MAC 地址使用冒号分隔字节。诸如此类。电子邮件地址具有多层嵌套分隔符,例如符号local@domain
周围@
,然后是使用点分隔的域部分。括号中允许使用,以及诸如%
和 之类的内容!
。人们编写字符串处理代码来处理这些格式,并且由于受到 C 和 Unix 的影响,许多语言中的字符串处理代码都不喜欢空字节。
GNU Awk 的演示使用空字节作为字段分隔符,处理/proc/self/environ
。
$ awk -F'\0' \
'{ for (i = 1; i <= NF; i++)
printf("field[%d] = %s\n", i, $i) }' \
/proc/self/environ
field[1] = CLUTTER_IM_MODULE=xim
field[2] = XDG_MENU_PREFIX=gnome-
field[3] = LANG=en_CA.UTF-8
field[4] = DISPLAY=:0
field[5] = OLDPWD=/home/kaz/tftproot
field[6] = GNOME_SHELL_SESSION_MODE=ubuntu
field[7] = EDITOR=vim
[ snip ... ]
field[54] = PATH=/home/kaz/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/kaz/bin:/home/kaz/bin
field[55] = GJS_DEBUG_TOPICS=JS ERROR;JS LOG
field[56] = SESSION_MANAGER=local/sun-go:@/tmp/.ICE-unix/1986,unix/sun-go:/tmp/.ICE-unix/1986
field[57] = GTK_IM_MODULE=ibus
field[58] = _=/usr/bin/awk
field[59] =
由于末尾有空字节,我们得到了一个额外的空白字段,因为 Awk 将其视为字段分隔符,而不是终止符。然而,这恰恰是因为 GNU Awk 允许空字节成为字符串的组成部分。-F '\0'
根据 POSIX 规范,该参数不需要工作。POSIX 在题为“awk 中的转义序列”那
\ddd
:一个字符后跟一个、两个或三个八进制数字的最长序列 (01234567)。如果所有数字均为 0(即 NUL 字符的表示),则行为未定义。
因此,依赖 Awk 用空字节来分隔字段或记录是完全不可移植的。这种语言问题可能是我们不更多地使用空字符的原因之一。