如何基于字符转置文本文件

如何基于字符转置文本文件

有一些工具,例如datamesh在文件中转置矩阵csv,但我想基于字符交换行和列。所以一个文件

abcde
fghij
klmn
opqrs

应该成为

afko
bglq
chmq
dinr
ej s

请注意,由于第 3 行较短,因此必须在最后一行插入一个空格。

当然,我可以编写一些 C 程序来做到这一点,但我想我曾经遇到过一个可以做到这一点的工具,但我的搜索引擎无法帮助我找到它。

答案1

rs您可以使用纯转置 ( ) 模式下的实用程序来完成此操作-T- 如果您首先适当地间隔输入,即

$ sed -e 's/./& /g' -e 's/ $//' file
a b c d e
f g h i j
k l m n
o p q r s

(或者,如果您有 GNU sed,则可以使用sed 's/./ &/2g';另一个选项是使用循环sed -E ':a; s/([^ ])([^ ])/\1 \2/; ta');然后

$ sed -e 's/./& /g' -e 's/ $//' file | rs -Tng0
afko
bglp
chmq
dinr
ej s

重要的选项是:

  • -T纯转置
  • -n填充空条目
  • -g0将输出装订线宽度(列间距)设置为零

awk或者,使用空输入字段分隔符和默认输出字段分隔符进行输入拆分:

awk '{$1=$1} 1' FS= file | rs -Tng0

答案2

使用 awk 转置的通用解决方案如下。

为了正确工作,我们需要列数。
在将文件读入值数组时可以找到这一点:

#!/bin/bash
file=i4
delimiter=""
sep=""

transpose() { : # comment sed for newer awks.
              # Do this to separate characters in quite old awk
              # very old wak does not allow that the FS could be Null.
              #sed -e 's/./ &/g' "$file" |
              awk ' 
                   { for(i=1;i<=NF;i++){a[NR,i]=$i};{(NF>m)?m=NF:0} }
                   END { for(j=1; j<=m; j++)
                         { for(i=1; i<=NR; i++)
                           { b=((a[i,j]=="")?" ":a[i,j])
                             printf("%s%s",(i==1)?"":sep,b)
                           }
                           printf("\n")
                         }
                       }
                   ' FS="$delimiter" sep="$sep" cc="$countcols" <"$file"
             }

transpose

有了这个文件:

abc
fghij
klmn
opqrs

将打印:

afko
bglp
chmq
 inr
 j s

如果“字段分隔符”为空,awk 负责分隔字符。如果变量也为空,
则字符将打印在一行中。sep


如果可用的 awk 是较旧的,则 null FS 无效。使用以下两个命令。

要计算字符数,请在旧版 awks 中使用:

# Work with any POSIX awk to find the max character count in all rows.
countcols=$(awk '{l=length($0);(l>max)?max=l:0}END{print max}' < "$file")

要进行转置,可以在每个字符前面添加一个空格,并使用空格作为“字段分隔符”并避免空 FS:

sed -e 's/./ &/g' < "$file" |
awk ' {for(i=1;i<=cc;i++){if($i==""){$i=" "};r[i]=r[i]sep$i;};sep=""};
      END{for(i=1;i<=cc;i++)print(r[i])}
    ' cc="$countcols"

注释较新 awks 的 sed 行。

答案3

这是一个带有cut和 的解决方案paste。由于您没有任何分隔符(例如空格或制表符),因此需要进行一些修复sed

for COL in {1..5}; do cut -c $COL < infile | paste -s -d_ ; done | sed -e 's/__/_ /g' -e 's/_//g'

这里分成多行:

for COL in {1..5}; do
  cut -c $COL < infile | paste -s -d_
done | sed -e 's/__/_ /g' -e 's/_//g'

第一部分的输出如下所示:

for COL in {1..5}; do cut -c $COL < infile | paste -s -d_ ; done

a_f_k_o
b_g_l_p
c_h_m_q
d_i_n_r
e_j__s

一件烦人的事情是你必须在开始之前知道有多少列。

答案4

如果行字符较少,请用一些字符填充每行,然后删除多余的字符。

回声 abc | sed 's/./&@@@@/'| sed -r 's/(.{4})./\1/'

abc@

回声“”| sed's/./&@@@@/'| sed -r's/(.{4})./\1/'

@@@@

相关内容