如何合併文件中固定間隔的行?

如何合併文件中固定間隔的行?

我有一個文件,其內容如下所示:

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表示參數。函數的主體是透過用機器產生的參數名稱替換這些特殊的數字參數而從原始的準字串表達式派生的。

相關內容