如何合并文件中固定间隔的行?

如何合并文件中固定间隔的行?

我有一个文件,其内容如下所示:

a1
b1
c1
aa
bb
cc
aaa
bbb
ccc
d1
e1
f1
dd
ee
ff
ddd
eee
fff
g1
h1
i1
gg
hh
ii
ggg
hhh
iii

以固定间隔(在本例中为 3)合并行并得到如下内容的最佳方法是什么:

a1 aa aaa
b1 bb bbb
c1 cc ccc
d1 dd ddd
e1 ee eee
f1 ff fff
g1 gg ggg
h1 hh hhh
i1 ii iii

从输入获取输出的算法是:

  • 首先我们得到第 1 行,即 a1。
  • 我们知道间隔是3
  • 所以第1行、第(1+3)行、第(1+3+3)行应该在同一行
  • 同样,第 2、5、8 行应位于同一行,依此类推。

那些a1,啊啊等只是随机的虚拟文本,它们可以是任何随机字符串。重点是两者之间有一个固定的间隔a1,啊啊

目前我使用 emacs 键盘宏来完成此任务。不过我想知道是否有更好的方法来解决这个问题。提前致谢。

答案1

如果你在gnu/anything 并且行数是 9 的倍数,你可以运行

split -l9 --filter='pr -3 -s" " -t' infile

这将输入分成九行,每一行都通过管道传输到pr -3 -s" " -t'列中......取决于编号。线条及其长度,您可能需要使用pr选项-w-l。请参阅man页面了解更多详情。

答案2

这是 awk 中的一个简单解决方案,通过硬编码以三行间隔提取三组:

{
  if (NR > 1 && (NR % 9) == 0) {
    print a "\n" b "\n" c " " $0
    a=""
    b=""
    c=""
  } else if (NR % 3 == 1) {
    if (NR % 9 > 1) {
      a=a" "$0
    } else {
      a=$0
    }
  } else if (NR % 3 == 2) {
    if (NR % 9 > 2) {
      b=b" "$0
    } else {
      b=$0
    }
  } else {
    if (NR % 9 > 3) {
      c=c" "$0
    } else {
      c=$0
    }
  }
}

将其保存到文件中并运行awk -f thatfile < input。我确信有更聪明的方法可以做到这一点,但我并不是每天都在 awk 中工作。

答案3

这有点棘手。我不知道有哪个实用程序可以做到这一点:

该管道(本质上)一次读取 9 行,并用于pr格式化为 3 列:

# there are 9 single hyphens below
paste -d: -- - - - - - - - - - < file | while read line; do
    tr : '\n' <<<"$line" | pr -s" " -T -3
done
a1 aa aaa
b1 bb bbb
c1 cc ccc
d1 dd ddd
e1 ee eee
f1 ff fff
g1 gg ggg
h1 hh hhh
i1 ii iii

这假设您的实际文本中没有冒号。

答案4

一种非常简单、清晰的方法TXR

@(repeat)
@x0
@x1
@x2
@y0
@y1
@y2
@z0
@z1
@z2
@  (output)
@x0 @y0 @z0
@x1 @y1 @z1
@x2 @y2 @z2
@  (end)
@(end)

跑步:

$ txr reshape.txr data
a1 aa aaa
b1 bb bbb
c1 cc ccc
d1 dd ddd
e1 ee eee
f1 ff fff
g1 gg ggg
h1 hh hhh
i1 ii iii

有很多方法可以压缩它,但你必须更加努力才能理解它们,例如:

@(repeat)
@  (collect :times 9)
@line
@  (end)
@  (bind (x y z) @(tuples 3 line))
@  (output)
@    (repeat)
@x @y @z
@    (end)
@  (end)
@(end)

另外,稍微了解 Awk 中操作的人可能会如何实现:

        { a[(NR-1)%9] = $0 }
!(NR%9) { print a[0], a[3], a[6]
          print a[1], a[4], a[7]
          print a[2], a[5], a[8] }

输出:

$ awk -f reshape.awk data
a1 aa aaa
[ ... ]
i1 ii iii

如果该编码员发现重复的print模式令人反感:

        { a[(NR-1)%9] = $0 }
!(NR%9) { for (i = 0; i < 3; i++)
            print a[i], a[i+3], a[i+6] }

TXR Lisp解决方案:

[(opip (tuples 3) (tuples 3) (mappend transpose)
       (mapcar (aret `@1 @2 @3`)) tprint)
 (get-lines)]

跑步:

$ txr reshape.tl < data

在命令行上:使用-t,删除tprint

$ txr -t '[(opip (tuples 3) (tuples 3) (mappend transpose)
                 (mapcar (aret `@1 @2 @3`)))
           (get-lines)]' < data

这是通过将输入放入管道中来实现的,该管道将其支持为三元组,然后是这些三元组的三元组(基本上是由嵌套列表组成的 3x3 矩阵)。这些矩阵被单独转置,然后将它们的行附加在一起以形成一个巨大的三元组列表。这些三元组通过aret部分应用运算符字符串插值转换为字符串,并且输出tprint将字符串列表视为要输出的行。语法

(aret `@1 @2 @3`)

扩展成类似的东西

(lambda (. args)
  (apply (lambda (arg1 arg2 arg3)
           `@arg1 @arg2 @arg3`)
         args))

基本上,它隐式创建一个单参数匿名函数,该函数将其参数视为参数列表,以应用于 3 参数匿名函数,其中@1,@2@3表示参数。函数的主体是通过用机器生成的参数名称替换这些特殊的数字参数而从原始的准字符串表达式派生的。

相关内容