Dav*_*ave 28 csv text-delimiter
我的问题是:为什么没有特定的“分隔符”字符?一种将用于所有类型的定界。我们有用于换行、打印设置等的特殊字符...
如果这些是常见的文本字符,为什么我们有时会使用逗号、空格、制表符等。这背后有历史吗?就像他们在制作 ASCII 等时可能不需要分隔符一样?
(对我来说似乎有意义:有一个特殊的分隔符,它的唯一目的是在需要时“分隔”单独的值)
Kel*_*ari 80
分隔符已存在于ASCII 中。十进制 28-31(十六进制 1C-1F)是分隔符。这包括文件、记录、组和单位分隔符。
我假设我们不使用它们,因为输入不需要多个键来输入单个字符的键盘字符会更容易。这也允许不同格式之间更容易的交换。逗号分隔值几乎适用于任何系统,无论是否符合 ASCII。
man*_*act 52
如前所述,ASCII 包含分隔符。问题不在于在数据输入期间需要额外的键来包含分隔符 - 对于大写字母或其他特殊的可打印字符(例如 !@#$),Control 并不比 Shift 更难使用。问题是传统上那些控制字符不是直接可见的。即使制表符、回车和换行 - 产生即时动作,也不会产生可见输出。
您无法区分制表符和空格之间的电传打字机。您无法区分换行符和空格到行尾+换行到下一行。同样,分隔符没有定义的可打印图像。它们可能会显示在一些(现代)文本编辑器中,并且它们可能会在各种设备中产生即时动作,但它们不会留下痕迹。
如果数据仅设计为机器可读——即我们通常所说的二进制文件,所有这些都无关紧要。但是用于数据输入和系统之间传输的文本通常是有意为人类可读的。如果它是人类可读的,分隔符需要是可打印的。
Ste*_*nny 16
正如另一个答案中提到的,ASCII 确实有分隔符。看这里 [1],提到了这些:
| 代码点 | 姓名 |
|---|---|
| U+001C | 文件分隔符 |
| U+001D | 组分隔符 |
| U+001E | 记录分隔符 |
| U+001F | 单位分隔符 |
这些都被使用了。例如,U+001C(八进制 34)是SUBSEPGNU AWK的默认[2] 字符串。
小智 10
这主要是历史性的。
在信息学的旧时代,数据文件大多是固定宽度字段文件,因为它是 Fortran IV 和 COBOL 等语言的自然 IO:第一个字段为 n 个字符,第二个为 m,等等。
然后 C 语言提供了scanf在(组)空格上分割输入的功能,人们开始对包含数字的数据文件使用自由格式。但是,当某些字段可能包含空格(scanf被称为穷人的解析器)时,这会导致结果混乱。因此,由于拆分的另一个标准函数是strtok使用单个分隔符,因此大多数(说英语的)人开始使用逗号 ( ,) 作为分隔符,因为在文本编辑器中手动编写逗号分隔值文件很容易。
然后国家语言支持进入游戏......在一些欧洲语言(法语)中,小数点是逗号。IT 人员习惯使用小数点,但很少有技术人员不习惯,因此法语版本的 Windows 开始将分号 ( ;)定义为分隔符,以允许在十进制数中使用逗号。
与此同时,有些人意识到,当字段的长度总是很近时,制表符(所有键盘上都存在)允许提供很好的垂直对齐,这就是第三种标准的原因。
最后,标准化开始成为事实,2005 年出现了 RFC 4180。它确实将逗号定义为官方分隔符,但由于 Windows 决定玩 NLS 游戏,想要处理真实文件的工具和库必须适应各种可能的分隔符。
这就是为什么在 2021 年,我们在 CSV 文件中有许多可能的分隔符的原因......
事实证明,在 ASCII中有一个事实上的通用分隔符:空字符。Unix 和 C 语言表明,您可以构建一个完整的平台,在该平台中,空字符从字符串中排除,在其表示中充当终止符。其他平台也纷纷效仿,如 Microsoft Windows。
今天,它几乎是铁一般的保证,没有文本数据包含空字节。如果数据包含空字节,则它是二进制而不是文本。
如果你想在字节流中存储一系列文本记录或字段,如果你用空值将它们分开,你几乎没有问题。空值不需要像转义这样的废话。如果有人过来说他们想在文本字段中包含一个空字节,您可以像喜剧演员一样嘲笑他们。
野外空值分离的例子:
Microsoft 允许注册表中的项目是多字符串:单个项目包含多个字符串。这被存储为串联在一起的空终止字符串序列,并带有一个额外的空字节来终止整个序列。如 in"the\0quick\0brown\0fox\0\0"表示字符串列表"the", "quick", "brown", "fox"。
在 Linux 内核上,每个进程的环境变量都可以通过/proc文件系统获得,如/proc/<pid>/environ. 此虚拟文件使用空分隔,如PATH=/bin:/usr/bin\0TERM=xterm\0....
一些 GNU 实用程序可以选择生成空分隔的输出,这正是允许它们用于编写更健壮的脚本的原因。GNUfind有一个-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] =
Run Code Online (Sandbox Code Playgroud)
由于末尾的空字节,我们得到了一个额外的空白字段,因为 awk 将其视为字段分隔符,而不是终止符。然而,这正是可能的,因为 GNU Awk 允许空字节成为字符串的组成部分。-F '\0'根据 POSIX 规范,该参数不需要工作。POSIX 在题为“awk 中的转义序列”的表中说
\ddd:一个字符,后跟最长的一个、两个或三个八进制数字字符序列 (01234567)。如果所有数字都是 0(即 NUL 字符的表示),则行为未定义。
因此,依靠 Awk 来分隔空字节上的字段或记录是完全不可移植的。这种语言问题可能是我们不更多使用空字符的原因之一。
| 归档时间: |
|
| 查看次数: |
6617 次 |
| 最近记录: |