TPSV

尽管 CSV(Comma-Separated-Values,逗号分隔值)无处不在,但它的近亲 TSV (Tab-Separated-Values,制表符分隔值) 在许多情况下比 CSV 具有直接的优势。 制表符通常不会出现在编码的数据中,从而避免了引用(quoting)的需要。 此外,如果您的列都具有相似的宽度,则可以在任何文本编辑器中配置制表符宽度,以便所有列都很好地对齐。 不幸的是,在许多情况下,列的宽度并不相似。

TPSV 代表 Tab-Pipe-Separated-Values(制表符-管道符分隔值),旨在成为比 CSV 和 TSV 更易于使用的替代方案。 与这两种格式一样,TPSV 能够描述表格数据,其中每个单元格都包含一个字符串值。 TPSV 的设计宗旨是易于解析,并且方便任何使用文本编辑器的人使用。

语法 (Syntax)

基本语法很简单。

  1. 单元格以 | 开头,并以一个或多个制表符结尾。
  2. 以单元格开头的行是一个行。 任何其他行都会被忽略。
  3. 第一行定义列数。

这就是您所需要的全部,但是还有一些规则可以使生活更轻松。

  1. 单元格太少的行,其余单元格为空字符串。
  2. 单元格太多的行,多余的单元格将被忽略。
  3. 行中的最后一列不需要以制表符结尾。

然后是最后一个可选规则,多行扩展。

  1. \ 而不是 | 开头的行被视为延续行。 其单元格的非空内容附加到上一行的单元格,并用换行符分隔。

示例 (Example)

这是一个演示该格式的 TPSV 示例。 灰色箭头是制表符。 此示例突出显示了几个方面。

编辑器配置 (Editor configuration)

建议:

适用于通用 tpsv 文件的 .editorconfig 示例:

[*.tpsv]
indent_style = tab
tab_width = 8
max_line_length = 999
insert_final_newline = true

与其他格式的比较 (Comparison with other formats)

相对于 TSV 的改进:

相对于 Markdown 管道表的改进:

解析 (Parsing)

这会将基本语法解析为 python 列表的迭代器。

[](https://chtenb.dev/<#cb2-1>)def parse_tpsv(file):
[](https://chtenb.dev/<#cb2-2>)  row_len = -1
[](https://chtenb.dev/<#cb2-3>)  for line in file:
[](https://chtenb.dev/<#cb2-4>)    if line.startswith('|'):
[](https://chtenb.dev/<#cb2-5>)      cells = [c.rstrip('\t') for c in line[1:].rstrip('\n').split('\t|')]
[](https://chtenb.dev/<#cb2-6>)      row_len = len(cells) if row_len < 0 else row_len # 确定列数
[](https://chtenb.dev/<#cb2-7>)      yield cells[:row_len] + [''] * (row_len - len(cells)) # 规范化行大小

要也解析多行扩展,可以这样做:

[](https://chtenb.dev/<#cb3-1>)def parse_tpsv_multiline(file):
[](https://chtenb.dev/<#cb3-2>)  row_len, row = -1, None
[](https://chtenb.dev/<#cb3-3>)  for line in file:
[](https://chtenb.dev/<#cb3-4>)    if line.startswith('|'):
[](https://chtenb.dev/<#cb3-5>)      if row:
[](https://chtenb.dev/<#cb3-6>)        yield row
[](https://chtenb.dev/<#cb3-7>)      cells = [c.rstrip('\t') for c in line[1:].rstrip('\n').split('\t|')]
[](https://chtenb.dev/<#cb3-8>)      row_len = len(cells) if row_len < 0 else row_len
[](https://chtenb.dev/<#cb3-9>)      row = cells[:row_len] + [''] * (row_len - len(cells))
[](https://chtenb.dev/<#cb3-10>)    elif line.startswith('\\') and row:
[](https://chtenb.dev/<#cb3-11>)      cells = [c.rstrip('\t') for c in line[1:].rstrip('\n').split('\t|')]
[](https://chtenb.dev/<#cb3-12>)      for i, c in enumerate(cells[:row_len]):
[](https://chtenb.dev/<#cb3-13>)        row[i] += '\n' + c if c else '' # 如果需要,继续单元格内容
[](https://chtenb.dev/<#cb3-14>)  if row:
[](https://chtenb.dev/<#cb3-15>)    yield row