分割一条线然后重新排列

分割一条线然后重新排列

我有一个包含 130 个字段的文件,用分号分隔。我想以某种方式重新排列它们。

考虑下面的例子:

文件样本.txt

1;2;3;4;8;5;6;7;9;10;11;
11;12;13;14;18;15;16;17;19;20;21;

所需的输出(文件请求操作.txt):

1;2;3;4;5;6;7;8;9;10;11;
11;12;13;14;15;16;17;18;19;20;21;

请注意,第八个元素放错了位置。我所做的只是简化流程。问题是有 121 个字段,因此我无法使用简洁的 AWK 命令对整个文件进行单行文本操作。

我已经尝试过以下方法并且它有效。您能建议一个更有效或更可读的解决方案吗?我请求您也解释一下您的解决方案。

每个字段可以包含由空格/字符串分隔的数字和字符串,其中包含$#等。

#!/bin/bash

file="sample.txt"

while read -r line
do

    array=($(echo "$line" | sed 's/;/ /g'))

    printf -v first '%s;' "${array[@]:0:4}"
    printf -v last '%s;' "${array[@]:8:12}"
    printf -v second '%s;' "${array[@]:5:3}"
    printf -v third '%s;' "${array[@]:4:1}"

    echo "${first}${second}${third}${last}" >> req_op.txt

done < $file

实际字段数:

输入:

1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|143|138|139|140|141|142|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162

输出:

1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162

我修改了@Quasímodo共享的sed命令;现在它按预期工作了。

sed -E 's~(([^\|]*\|){137})([^\|]*\|)(([^\|]*\|){5})~\1\4\3~' sample.txt

答案1

使用 Perl:

$ perl -F';' -lne 'splice @F, 7, 0, (splice @F, 4, 1); print join ";", @F' sample.txt 
1;2;3;4;5;6;7;8;9;10;11
11;12;13;14;15;16;17;18;19;20;21

参见示例在 Perl 中拼接切片和切块数组

答案2

使用 awk

awk 'BEGIN{FS=OFS=";"}{$8=$8 FS $5;$5=RS;sub(RS FS,"");print}' sample.txt > req_op.txt

展开版本,附评论:

awk '
  BEGIN{FS=OFS=";"} #Sets input (FS) and output (OFS) field separators
  {                 #For each line
    $8=$8 FS $5     #Append the 5th field after the 8th field
    $5=RS           #Put a newline (the record separator) in the 5th field
    sub(RS FS,"")   #Remove the newline and its following FS
    print           #Print the resulting line
  }     
' sample.txt > req_op.txt

为什么选择记录分隔符(在您的例子中是换行符)来临时替换第五个字段?因为它是唯一一个肯定不会出现在记录中的角色。然后,sub(RS FS,"")一定要删除第 5 个字段,即使某处有空字段。

如果您不理解该sub行,请将其删除并查看输出会发生什么。

使用 Sed

  • 使用扩展正则表达式:

    sed -E 's|(([^;]*;){4})([^;]*;)(([^;]*;){3})|\1\4\3|' sample.txt > req_op.txt
    
  • 使用基本的正则表达式,符合 POSIX 标准,它基本上与上面相同,但每个都(){}需要转义(叹气!):

    sed 's|\(\([^;]*;\)\{4\}\)\([^;]*;\)\(\([^;]*;\)\{3\}\)|\1\4\3|' sample.txt > req_op.txt
    

s是sed的替换命令。它后面的字符是分隔符(我选择了|)。它界定正则表达式槽、替换槽和标志槽(在本例中为空)。

正则表达式的一些元素解释:

  • [^;]*;:除分号之外的任何字符出现零次或多次,后跟分号。
  • ([^;]*;){4}:以上表达式位于捕获组中,应精确重复 4 次。
  • (([^;]*;){4})\1:上述表达式位于外部捕获组中,并在替换表达式中再现;内部捕获组将被替换\2

所以,第一行发生的1;2;3;4;8;5;6;7;9;10;11;

  • \1得到1;2;3;4;
  • \3得到8;
  • \4得到5;6;7;

并且它们被重新排序为\1\4\3.

有关反向引用的更多信息,请阅读使用 \1 保留模式的一部分 (顺便说一句,该网页是一个很好的 sed 教程)。

答案3

使用perl,您还可以执行以下操作:

perl -F';' -lape '$_ = join ";", @F[0..3,5..7,4,8..10]' sample

或者为了你的实际的输入:

perl -F'[|]' -lape '$_ = join "|", @F[0..136,138..142,137,143..161]' input

答案4

Python

#!/usr/bin/python
k=open('filename','r')
r=[]
v=[]
for i in k:
    r=[]
    v=[]
    j=i.strip().split(";")
    for g in j:
        if (g != ''):
            r.append(int(g.strip()))
    r.sort()
    e=r
    for d in e:
        v.append(str(d))
        v.append(str(";"))
    print "".join(v)

输出

1;2;3;4;5;6;7;8;9;10;11;
11;12;13;14;15;16;17;18;19;20;21;

相关内容