如何拆分文本文件中给定列的字符串值

如何拆分文本文件中给定列的字符串值

我在 Linux 机器上有一个包含两列的文本文件:

  • 第1列 = id_no(大部分为5位,有的为6位);
  • 第 2 列 = Genetic_markers(全部长度为 50674 位);
12345 0102010205
54322 2221110051
123456 1122011510

我想将文件更改为如下所示:

 12345 0 1 0 2 0 1 0 2 0 5
 54322 2 2 2 1 1 1 0 0 5 1
123456 1 1 2 2 0 1 1 5 1 0
  1. 如何更改第一列,使其向右对齐(如数字所示)?

  2. 有人可以帮我用最可靠的方法来更改第二列数字之间的空格吗?请解释代码的元素及其作用。

谢谢

答案1

perl

$ perl -lane 'printf "%6s %s\n", $F[0], join " ", split "", $F[1]' <your-file
 12345 0 1 0 2 0 1 0 2 0 5
 54322 2 2 2 1 1 1 0 0 5 1
123456 1 1 2 2 0 1 1 5 1 0

使用-lane-n一次读取输入一条记录,并-e使用 中的记录运行表达式$_-a因为awk 将行拆分为@F字段,从记录中-l删除ine 分隔符),其行为类似于。lperlawk

在这里,我们使用printf空格将第一个字段左填充到 6 的长度,然后用空格将join第二个字段拆分为其字符组成部分。

答案2

为了右对齐,您需要找到文件中最长数字的长度,或者只选择一个大数字并使用它。例如,您可以填充 10 个空格:

$ printf '%d\n' 123
123
$ printf '%10d\n' 123
       123

如果这种方法足够好,您可以执行以下操作:

$ awk '{ gsub(/./," &",$2); printf "%10d%s\n",$1,$2}' file 
     12345 0 1 0 2 0 1 0 2 0 5
     54322 2 2 2 1 1 1 0 0 5 1
    123456 1 1 2 2 0 1 1 5 1 0

这里只发生了两件事:

  • gsub(/./," &",$2);: 这gsubG全局的.stitution) 函数将用您提供的任何替换项替换您给它的正则表达式的所有匹配项(在这里,我们只给它一个含义“任何字符”)。具有&特殊含义,表示“正则表达式匹配的任何内容”,因此&作为替换给出的效果是在每个字符之前插入一个空格。最后一个参数是输入,这里我们给它第二个字段$2
  • printf "%10d %s\n",$1,$2:我们用来printf打印格式化字符串。%10d意思是“打印我给你的数字并用10个空格填充”,%s意思是“打印这个字符串”。因此,我们告诉它打印第一个字段填充了 10 个空格,然后打印已被 修改的第二个字段gsub

如果只需要填充最小值,则需要读取文件两次。首先获取最长的第一个字段的长度:

$ awk -v max=0 '{ if(length($1) > max){ max=length($1) }} END{print max}' file 
6

有了这个,你就可以更具体:

$ awk '{ k=gsub(/./," &",$2); printf "%6d%s\n",$1,$2}' file 
 12345 0 1 0 2 0 1 0 2 0 5
 54322 2 2 2 1 1 1 0 0 5 1
123456 1 1 2 2 0 1 1 5 1 0

答案3

使用anyawk和GNU column(对于-R):

$ awk '{gsub(/./," &",$2)} 1' file | column -tR1
 12345  0  1  0  2  0  1  0  2  0  5
 54322  2  2  2  1  1  1  0  0  5  1
123456  1  1  2  2  0  1  1  5  1  0

-o' '如果您真的关心字段之间的空格,请添加:

$ awk '{gsub(/./," &",$2)} 1' file | column -o' ' -t -R1
 12345 0 1 0 2 0 1 0 2 0 5
 54322 2 2 2 1 1 1 0 0 5 1
123456 1 1 2 2 0 1 1 5 1 0

答案4

使用(以前称为 Perl_6)

~$ raku -ne '.split(" ") andthen put sprintf("%6d", .[0]), .[1].comb;'  file 

#OR

~$ raku -ne '.words andthen put sprintf("%6d", .[0]), .[1].comb;'  file

Raku 是 Perl 家族的一种编程语言。上面使用了 Raku 的-ne非自动打印逐行标志。默认情况下,该-n标志会删除行终点的分隔符。然后,您可以print省略尾随换行符(默认情况下),或者put,这会添加尾随换行符(想象一下put代表使用终止符打印)。

在第一个答案中,该行明确位于.split单空格( 的缩写$_.split)上。在第二个答案中,Raku 的.words例程用于按空格进行分割。此后,连接andthen重新加载$_,以便可以格式化每列以进行输出。第一列(即.[0])使用格式化,sprintf而第二列(即.[1])被comb编辑为单个字符并返回。


注意:如果“ID”确实是(十进制)无符号整数,那么在内部sprintf您可以使用u代替d,如 中所示sprintf("%6u", …)


输入示例:

12345 0102010205
54322 2221110051
123456 1122011510

示例输出:

 123450 1 0 2 0 1 0 2 0 5
 543222 2 2 1 1 1 0 0 5 1
1234561 1 2 2 0 1 1 5 1 0

注意:为了避免在文件可能包含空行时引发错误,您可以添加if条件来删除空行:

~$ raku -ne 'if .chars { .words andthen put sprintf("%6d", .[0]), .[1].comb};'  file

#OR

~$ raku -ne 'if $_ .= words {put sprintf("%6d", .[0]), .[1].comb};'  file

要保留空行,您可以使用 Raku 的三元操作员:

~$ raku -ne '.chars ??  ( .split(" ") andthen put sprintf( "%6d", .[0]), .[1].comb) !! "".put;'  file

#OR

~$ raku -ne '$_ .= split(" ", :skip-empty) ?? (put sprintf( "%6d", .[0]), .[1].comb) !! "".put;'  file 

https://docs.raku.org/routine/sprintf
https://docs.raku.org/routine/%3F%3F%20%21%21
https://raku.org

相关内容