【问题标题】:Replacing a character in a single field when using more than one multi-character delimiter使用多个多字符分隔符时替换单个字段中的字符
【发布时间】:2021-04-03 06:34:42
【问题描述】:

目标:我想将文件中字符串“antisense”和“::”之间的每个“-”实例替换为“.”。

我的文件的代表性示例:

>-::NC_009089.1:17609-17804(+)
ATTAAATAGAAAAAATGAATTTAATATAAAAAATTAAAGAAAATTCTAAAAAAAAAAAGATAAGGTCTTA
>antisense_tadA::NC_009089.1:19643-19848(-)
TTTATAAAAATATTTAGTGTTTTTTTTAAATTAGTTCTAAAATAATTTTTAGATATTCATACAAGAGTGT
>-::NC_009089.1:20139-20394(-)
GCTGTTTTTCTATATATGAATTTTGCTACTTTTACATTATTATTATTAAAATAATCTAATTTAAACTCAT
>antisense_recR::NC_009089.1:22931-23105(+)
TCATCTATAATCGCTTTAGATAAAGCTTCCACATCATTAGTATTCATATTAATAATATGAAAAGCCAATC
>antisense_16s_rRNA::NC_009089.1:25279-26010(-)
CTCTATTTTCCTTTTTATTCTATATTTAAATTTTTTATTTACAAGAATATTTTTAATATAACATATTATG
>antisense_tRNA-Leu_tRNA-Met::NC_009089.1:30389-30422(+)
TTTACATAGAGTTAACACTCTAAAAACTGCACA
>antisense_tRNA-Arg_tRNA-Gly_tRNA-Asp_tRNA-Val::NC_009089.1:30559-31181(-)
CTTAACTTCTGTGTTCGGAATGGGAACAGGTGTATCCTCTTTCCCACCAAGTACCATCAGCGCTAAAGAG

期望的输出:

>-::NC_009089.1:17609-17804(+)
ATTAAATAGAAAAAATGAATTTAATATAAAAAATTAAAGAAAATTCTAAAAAAAAAAAGATAAGGTCTTA
>antisense_tadA::NC_009089.1:19643-19848(-)
TTTATAAAAATATTTAGTGTTTTTTTTAAATTAGTTCTAAAATAATTTTTAGATATTCATACAAGAGTGT
>-::NC_009089.1:20139-20394(-)
GCTGTTTTTCTATATATGAATTTTGCTACTTTTACATTATTATTATTAAAATAATCTAATTTAAACTCAT
>antisense_recR::NC_009089.1:22931-23105(+)
TCATCTATAATCGCTTTAGATAAAGCTTCCACATCATTAGTATTCATATTAATAATATGAAAAGCCAATC
>antisense_16s_rRNA::NC_009089.1:25279-26010(-)
CTCTATTTTCCTTTTTATTCTATATTTAAATTTTTTATTTACAAGAATATTTTTAATATAACATATTATG
>antisense_tRNA.Leu_tRNA.Met::NC_009089.1:30389-30422(+)
TTTACATAGAGTTAACACTCTAAAAACTGCACA
>antisense_tRNA.Arg_tRNA.Gly_tRNA.Asp_tRNA.Val::NC_009089.1:30559-31181(-)
CTTAACTTCTGTGTTCGGAATGGGAACAGGTGTATCCTCTTTCCCACCAAGTACCATCAGCGCTAAAGAG

您会注意到上面的所有行都应打印到输出,但在此示例中只有以“>”开头的最后两行看起来不同(其中每个“-”都替换为“. " 在字符串 "antisense" 和 "::" 之间)。这是因为以“>”开头的前五行在所描述的字段中没有包含“-”的字符串。

我一直在尝试使用 awk 来完成此任务,但我也愿意接受 sed 解决方案,如果它们更简单的话。我的一般方法是使用字符串“antisense”或“antisense_”作为一个分隔符,使用“:”或“::”作为另一个分隔符,从而使第 2 列($2)成为用于字符替换的字段。

以下是一些我的尝试及其错误输出

$ awk -F'>antisense_|:' '/^ *>antisense_/ {gsub("-", ".", $2); print}' file
    >antisense_tadA::NC_009089.1:19643-19848(-)
    >antisense_recR::NC_009089.1:22931-23105(+)
    >antisense_16s_rRNA::NC_009089.1:25279-26010(-)
     tRNA.Leu_tRNA.Met  NC_009089.1 30389-30422(+)
     tRNA.Arg_tRNA.Gly_tRNA.Asp_tRNA.Val  NC_009089.1 30559-31181(-)

$ awk 'BEGIN {OFS=FS="/antisense/|:"} {gsub("-", ".", $2)} 1' file
    >-::NC_009089.1:17609-17804(+)
    ATTAAATAGAAAAAATGAATTTAATATAAAAAATTAAAGAAAATTCTAAAAAAAAAAAGATAAGGTCTTA
    >antisense_tadA::NC_009089.1:19643-19848(-)
    TTTATAAAAATATTTAGTGTTTTTTTTAAATTAGTTCTAAAATAATTTTTAGATATTCATACAAGAGTGT
    >-::NC_009089.1:20139-20394(-)
    GCTGTTTTTCTATATATGAATTTTGCTACTTTTACATTATTATTATTAAAATAATCTAATTTAAACTCAT
    >antisense_recR::NC_009089.1:22931-23105(+)
    TCATCTATAATCGCTTTAGATAAAGCTTCCACATCATTAGTATTCATATTAATAATATGAAAAGCCAATC
    >antisense_16s_rRNA::NC_009089.1:25279-26010(-)
    CTCTATTTTCCTTTTTATTCTATATTTAAATTTTTTATTTACAAGAATATTTTTAATATAACATATTATG
    >antisense_tRNA-Leu_tRNA-Met::NC_009089.1:30389-30422(+)
    TTTACATAGAGTTAACACTCTAAAAACTGCACA
    >antisense_tRNA-Arg_tRNA-Gly_tRNA-Asp_tRNA-Val::NC_009089.1:30559-31181(-)
    CTTAACTTCTGTGTTCGGAATGGGAACAGGTGTATCCTCTTTCCCACCAAGTACCATCAGCGCTAAAGAG

$ awk 'BEGIN {OFS=FS="antisense|:"} {gsub("-", ".", $2)} 1' file
    >-::NC_009089.1:17609-17804(+)
    ATTAAATAGAAAAAATGAATTTAATATAAAAAATTAAAGAAAATTCTAAAAAAAAAAAGATAAGGTCTTA
    >antisense_tadA::NC_009089.1:19643-19848(-)
    TTTATAAAAATATTTAGTGTTTTTTTTAAATTAGTTCTAAAATAATTTTTAGATATTCATACAAGAGTGT
    >-::NC_009089.1:20139-20394(-)
    GCTGTTTTTCTATATATGAATTTTGCTACTTTTACATTATTATTATTAAAATAATCTAATTTAAACTCAT
    >antisense_recR::NC_009089.1:22931-23105(+)
    TCATCTATAATCGCTTTAGATAAAGCTTCCACATCATTAGTATTCATATTAATAATATGAAAAGCCAATC
    >antisense_16s_rRNA::NC_009089.1:25279-26010(-)
    CTCTATTTTCCTTTTTATTCTATATTTAAATTTTTTATTTACAAGAATATTTTTAATATAACATATTATG
    >antisense|:_tRNA.Leu_tRNA.Metantisense|:antisense|:NC_009089.1antisense|:30389-30422(+)
    TTTACATAGAGTTAACACTCTAAAAACTGCACA
    >antisense|:_tRNA.Arg_tRNA.Gly_tRNA.Asp_tRNA.Valantisense|:antisense|:NC_009089.1antisense|:30559-31181(-)
    CTTAACTTCTGTGTTCGGAATGGGAACAGGTGTATCCTCTTTCCCACCAAGTACCATCAGCGCTAAAGAG

我了解我的每次尝试为何/如何失败,但我不确定如何使用 awk 正确指定多个多字符分隔符,同时确保定义的分隔符在输入和输出文件之间保持不变(无替换) . 有什么想法或建议吗?

【问题讨论】:

    标签: awk sed


    【解决方案1】:

    对于显示的示例数据,此 awk 应该适合您:

    awk 'match($0, /antisense.*::/) {s = substr($0, RSTART, RLENGTH); 
    gsub(/-/, ".", s); $0 = substr($0, 1, RSTART-1) s substr($0, RSTART + RLENGTH)} 1' file
    
    >-::NC_009089.1:17609-17804(+)
    ATTAAATAGAAAAAATGAATTTAATATAAAAAATTAAAGAAAATTCTAAAAAAAAAAAGATAAGGTCTTA
    >antisense_tadA::NC_009089.1:19643-19848(-)
    TTTATAAAAATATTTAGTGTTTTTTTTAAATTAGTTCTAAAATAATTTTTAGATATTCATACAAGAGTGT
    >-::NC_009089.1:20139-20394(-)
    GCTGTTTTTCTATATATGAATTTTGCTACTTTTACATTATTATTATTAAAATAATCTAATTTAAACTCAT
    >antisense_recR::NC_009089.1:22931-23105(+)
    TCATCTATAATCGCTTTAGATAAAGCTTCCACATCATTAGTATTCATATTAATAATATGAAAAGCCAATC
    >antisense_16s_rRNA::NC_009089.1:25279-26010(-)
    CTCTATTTTCCTTTTTATTCTATATTTAAATTTTTTATTTACAAGAATATTTTTAATATAACATATTATG
    >antisense_tRNA.Leu_tRNA.Met::NC_009089.1:30389-30422(+)
    TTTACATAGAGTTAACACTCTAAAAACTGCACA
    >antisense_tRNA.Arg_tRNA.Gly_tRNA.Asp_tRNA.Val::NC_009089.1:30559-31181(-)
    CTTAACTTCTGTGTTCGGAATGGGAACAGGTGTATCCTCTTTCCCACCAAGTACCATCAGCGCTAAAGAG
    

    【讨论】:

    • 几乎完美。我注意到紧跟“::”之后,来自NCN 被切断了。这与/antisense.*::/ 部分有关吗?
    • 啊,是的,我刚刚使用/antisense.*:/ 进行了测试,效果很好。谢谢阿努巴瓦!
    • 啊,我意识到我在substr 的错误,请立即查看更新的答案
    • 这实际上是另一个很棒的解决方案!
    • 我摘下帽子。这是在输入中查找字段的好方法。我刚投了sed
    【解决方案2】:

    虽然awk 对很多事情都有好处,但通过sed 可以更好地处理您当前的问题,您可以简单地使用正常的替换形式sed '/match/s/find/replace/

    sed ':a /antisense/s/\(^[^:]*\)-\([^:]*\):/\1.\2:/;ta'
    

    其中:at 选项在成功替换时用于分支的标签,确保以"antisense" 开头到第一个':' 的行中的所有'-' 字符都被'.' 替换.

    添加-i 选项以使用sed -i ... file 编辑文件,或使用sed -i.bak ... file 在替换之前在file.bak 中创建原始文件的副本。

    示例

    $ echo "antisense_tRNA-Arg_tRNA-Gly_tRNA-Asp_tRNA-Val::NC_009089.1:30559-31181(-)" | 
      sed ':a /antisense/s/\(^[^:]*\)-\([^:]*\):/\1.\2:/;ta'
    antisense_tRNA.Arg_tRNA.Gly_tRNA.Asp_tRNA.Val::NC_009089.1:30559-31181(-)
    

    【讨论】:

    • 非常简洁的使用sed的例子。感谢分享大卫!它比awk快吗?或者是什么让它变得更好?
    • 两者都可以正常工作。 awk 作用于字段,而sed 作用于流 ("sed" - "stream editor")。您要么必须从数据中剔除字段,要么只对数据流进行操作。两者都很好。比较 1,000,000 行文件的性能会很有趣(可能不会太多)
    • 仅供参考:底部代码示例中的sed 命令有效,因为它指定替换“-”,但顶部代码示例需要“ _" 更改为 "-" 才能正常工作。简单修复!
    • 让我解决这个问题,我的眼睛原本将'-" 视为'_'(变老不是娘娘腔...:)
    • 注: ::: 不一样
    猜你喜欢
    • 2021-06-08
    • 1970-01-01
    • 2013-12-29
    • 2014-10-14
    • 2012-05-20
    • 1970-01-01
    • 2014-07-24
    • 2020-07-12
    • 1970-01-01
    相关资源
    最近更新 更多