【问题标题】:Visually align TSV using tabs使用制表符在视觉上对齐 TSV
【发布时间】:2021-10-08 16:12:54
【问题描述】:

我有一个包含字段的文本文件,这些字段由一些后续选项卡分隔(以便字段在视觉上对齐)。我想从另一个(未对齐,纯 tsv)文件中添加很多新字段,同时保持所有内容对齐。很多值中都包含空格,因此只有制表符(假定宽度为 8)可用于对齐,因为我希望稍后能够通过在任意数量的后续制表符上拆分每一行来解析文件。这意味着我不能使用像 columntsv-pretty 这样的工具,因为它们使用空格进行对齐。有什么工具或简短的脚本可以用来实现我想要的吗?

例子:

文件 1:

AA      BB      CCC
AAAA    BBB     CCC
AA      BBBB    CC

文件 2:

DD  EE  FF
DDDD    EE  FFFF
DD  EEEE    FF

结果:

AA      BB      CCC
AAAA    BBB     CCC
AA      BBBB    CC
DD      EE      FF
DDDD    EE      FFFF
DD      EEEE    FF

【问题讨论】:

  • awk 是一个很好的起点。您对 awk 进行了哪些尝试,为什么没有按预期工作?
  • 好吧,我什至不确定从哪里开始使用 AWK 来做到这一点。使用 awk 解析文件很容易,但我不知道如何将带有标签的字段加入到它们对齐的位置。
  • @Discussian 看看 [stackoverflow.com/questions/44338483/… 接受的答案。这可能是awk 解决方案的起点。
  • @Discussian 您可以首先尝试仅分析您的第一个文件(甚至是它的第一条记录)并提取每个字段的起始列号,假设选项卡宽度为 8。FPAT GNU awk 变量可能会有所帮助,因为它允许定义的不是分隔字段的内容,而是字段是什么。在您的情况下,字段可以定义为非制表符字符的非空序列,后跟一个或多个制表符,或记录的结尾。 FPAT="[^\t]+(\t+|$)".
  • @Discussian 将这些起始列号存储在数组中后,您可以使用默认字段定义处理第二个文件,并为每条记录和每个字段打印该字段,然后打印多个选项卡这取决于字段宽度。这一切都不应该太难。如果您在其中的特定部分遇到问题,您已经知道在哪里可以就您的编码问题提出特定问题

标签: csv awk command-line-interface


【解决方案1】:

视觉对齐是供人使用的,请勿以该格式保存文件,而是在您需要查看文件时使用column 为您设置格式。

首先需要去掉第一个文件中多余的标签并合并文件

$ cat <(tr -s '\t' <file1) file2 > file12

它将通过分隔符(制表符)对齐列。现在您可以在任何时候使用column -ts$'\t' file12 来查看将为您对齐列的文件。

这假设您没有丢失的字段。

【讨论】:

    【解决方案2】:

    我问这个问题是希望有一个现有的工具或一个简单的 awk/perl 单行器可以做我想做的事情。看起来没有,所以我在 Go 中编写了一个简单的工具,用于我的输入。它不能处理一个好的 tsv 解析器应该处理的很多事情(比如转义),但也许它对其他人仍然有用:

    package main
    
    import (
        "bufio"
        "fmt"
        "math"
        "os"
        "strings"
    )
    
    const tabWidth = 8
    
    func tsvAlign(filenames []string) (err error) {
        var lines [][]string
        for _, filename := range filenames {
            file, err := os.Open(filename)
            if err != nil {
                return err
            }
            defer file.Close()
    
            scanner := bufio.NewScanner(file)
            for scanner.Scan() {
                lines = append(lines, strings.FieldsFunc(scanner.Text(), func(c rune) bool { return c == '\t' }))
            }
        }
    
        maxFieldWidths := make([]int, len(lines[0])-1, len(lines[0])-1)
        for i := 0; i < len(lines[0])-1; i++ {
            for _, line := range lines {
                if len(line[i]) > maxFieldWidths[i] {
                    maxFieldWidths[i] = len(line[i])
                }
            }
        }
    
        for _, line := range lines {
            for i, field := range line[:len(line)-1] {
                padding := int(math.Ceil(float64(maxFieldWidths[i]+tabWidth-maxFieldWidths[i]%tabWidth)/8 - float64(len(field))/8))
                fmt.Print(field, strings.Repeat("\t", padding))
            }
            fmt.Println(line[len(line)-1])
        }
    
        return err
    }
    
    func main() {
        if len(os.Args) < 2 {
            fmt.Fprintln(os.Stderr, "ERROR: No arguments provided")
            return
        }
        err := tsvAlign(os.Args[1:])
        if err != nil {
            fmt.Fprintln(os.Stderr, "ERROR: ", err)
        }
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-08
      • 2018-09-23
      • 1970-01-01
      • 2012-02-29
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      相关资源
      最近更新 更多