【问题标题】:Insert a newline character every 10 characters in a string using Julia使用 Julia 在字符串中每 10 个字符插入一个换行符
【发布时间】:2017-03-25 13:35:06
【问题描述】:

我想在蛋白质序列中每 10 个字符插入一个换行符:

seq="MSKNKSPLLNESEKMMSEMLPMKVSQSKLNYEEKVYIPTTIRNRKQHCFRRFFPYIALFQ"

在 Perl 中,这很容易:

$seq=~s/(.{10})/$1\n/g ; # does the job!

perl -e '$seq="MSKNKSPLLNESEKMMSEMLPMKVSQSKLNYEEKVYIPTTIRNRKQHCFRRFFPYIALFQ"; $seq=~s/(.{10})/$1\n/g; print $seq'
MSKNKSPLLN
ESEKMMSEML
PMKVSQSKLN
YEEKVYIPTT
IRNRKQHCFR
RFFPYIALFQ

在朱莉娅中,

replace(seq, r"(.{10})" , "\n")

不起作用,因为我不知道如何获取捕获组 (.{10}) 并将其替换为自身 + "\n"

julia> replace(seq, r"(.{10})" , "\n")
"\n\n\n\n\n\n"

为此,我需要 2 个步骤:

    julia> a=matchall(r"(.{1,10})" ,seq)
    6-element Array{SubString{UTF8String},1}:
     "MSKNKSPLLN"
     "ESEKMMSEML"
     "PMKVSQSKLN"
     "YEEKVYIPTT"
     "IRNRKQHCFR"
     "RFFPYIALFQ"

    julia> b=join(a, "\n")
    "MSKNKSPLLN\nESEKMMSEML\nPMKVSQSKLN\nYEEKVYIPTT\nIRNRKQHCFR\nRFFPYIALFQ"

    julia> println(b)
    MSKNKSPLLN
    ESEKMMSEML
    PMKVSQSKLN
    YEEKVYIPTT
    IRNRKQHCFR
    RFFPYIALFQ

# Caution :    
a=matchall(r"(.{10})" ,seq) # wrong if seq is not exactly a multiple of 10 !

julia> seq
"MSKNKSPLLNESEKMMSEMLPMKVSQSKLNYEEKVYIPTTIRNRKQHCFRRFFPYIAL"

julia> matchall(r"(.{10})" ,seq)
5-element Array{SubString{UTF8String},1}:
"MSKNKSPLLN"
"ESEKMMSEML"
"PMKVSQSKLN"
"YEEKVYIPTT"
"IRNRKQHCFR"

julia> matchall(r"(.{1,10})" ,seq)
6-element Array{SubString{UTF8String},1}:
"MSKNKSPLLN"
"ESEKMMSEML"
"PMKVSQSKLN"
"YEEKVYIPTT"
"IRNRKQHCFR"
"RFFPYIAL" 

是否有一步解决方案或更好(更快)的方法?

只是为了好玩,所有这些有趣的答案的基准! (使用 Julia 5.0 更新)

function loop(a)
 last = 0
 #create the interval, in your case 10
 salt = 10
 #iterate in string (starts in the 10th value, don't forget julia use 1 to first index)
 for i in salt:salt+1:length(a)
    # replace the string for a new one with '\n'
    a = string(a[1:i], '\n', a[i+1:length(a)])
    last = Int64(i)
 end
 # replace the rest
 a = string(a[1:length(a) - last % salt + 1], '\n', a[length(a) - last % salt + 2:length(a)])
 println(a)
end

function regex1(seq)
  a=matchall(r"(.{1,10})" ,seq)
  b=join(a, "\n")
  println(b)
end

function regex2(seq)
  a=join(split(replace(seq, r"(.{10})", s"\1 ")), "\n")
  println(a)
end

function regex3(seq)
  a=replace(seq, r"(.{10})", Base.SubstitutionString("\\1\n"))
  a= chomp(a) # because there is a new line at the end
  println(a)
end

function intrapad(seq::String)
  buf = IOBuffer((length(seq)*11)>>3) # big enough buffer
  for i=1:10:length(seq)
    write(buf,SubString(seq,i,i+9),'\n')
  end
  #return
  print(takebuf_string(buf))
end

function join_substring(seq)
  a=join((SubString(seq,i,i+9) for i=1:10:length(seq)),'\n')
  println(a)
end

seq="MSKNKSPLLNESEKMMSEMLPMKVSQSKLNYEEKVYIPTTIRNRKQHCFRRFFPYIALFQ"

for i = 1:5
  println("loop :")
  @time loop(seq)
  println("regex1 :")
  @time regex1(seq)
  println("regex2 :")
  @time regex2(seq)
  println("regex3 :")
  @time regex3(seq)
  println("intrapad :")
  @time intrapad(seq)
  println("join substring :")
  @time join_substring(seq)
end

我将基准更改为执行 5 次 @time,并在此处发布执行 5 次 @time 后的结果:

loop :
MSKNKSPLLN
ESEKMMSEML
PMKVSQSKLN
YEEKVYIPTT
IRNRKQHCFR
RFFPYIA
LFQ
  0.000013 seconds (53 allocations: 3.359 KB)
regex1 :
MSKNKSPLLN
ESEKMMSEML
PMKVSQSKLN
YEEKVYIPTT
IRNRKQHCFR
RFFPYIALFQ
  0.000013 seconds (49 allocations: 1.344 KB)
regex2 :
MSKNKSPLLN
ESEKMMSEML
PMKVSQSKLN
YEEKVYIPTT
IRNRKQHCFR
RFFPYIALFQ
  0.000017 seconds (47 allocations: 1.703 KB)
regex3 :
MSKNKSPLLN
ESEKMMSEML
PMKVSQSKLN
YEEKVYIPTT
IRNRKQHCFR
RFFPYIALFQ
  0.000013 seconds (31 allocations: 976 bytes)
intrapad :
MSKNKSPLLN
ESEKMMSEML
PMKVSQSKLN
YEEKVYIPTT
IRNRKQHCFR
RFFPYIALFQ
  0.000007 seconds (9 allocations: 608 bytes)
join substring :
MSKNKSPLLN
ESEKMMSEML
PMKVSQSKLN
YEEKVYIPTT
IRNRKQHCFR
RFFPYIALFQ
  0.000012 seconds (21 allocations: 800 bytes)

Intrapad 现在是第一个 ;)

【问题讨论】:

  • 不知道另一种解决方案,但可以将 2 个步骤更改为一个班轮,如下所示:seq="MSKNKSPLLNESEKMMSEMLPMKVSQSKLNYEEKVYIPTTIRNRKQHCFRRFFPYIALFQ";println(join(matchall(r"(.{10})" ,seq), "\n"));
  • 所以我再次检查了文档:println(replace("ABHISHEKBHASKERMSEMLPMKVSQSKLNYEEKVYIPTTIRNRKQHCFRRFFPYIALFQ", r".{10}", s"\g<0> sss")); 这里如果我将 sss 替换为 \n 这应该可以工作,但是根据文档“通过使用 \n 来引用第 n 个捕获组”这是这里的问题。
  • 是的,@AbhiNickz replace(seq, r"(.{10})" , s"\g\n") 产生错误,但插入一个很好的解决方案blanc 例如:replace(seq, r"(.{10})" , s"\g ") 就可以了
  • @Dan Done :) 谢谢你的例子!
  • 为什么 (length(seq)*11)>>3 in intrapad:输出字符串每 10 个字符有 11 个字符,因此它需要大约 orig_size*11/10 个字符。 >>3 是除以 8(右移)和 8

标签: regex replace julia


【解决方案1】:

就像@daycaster 建议的那样,您可以使用s"\1" 作为替换字符串来支持捕获组。问题是特殊的s"" 字符串语法不支持像\n 这样的特殊字符。您可以通过手动构造一个SubstitutionString 对象来解决此问题,但是您需要转义\1 中的\

julia> replace(seq, r"(.{10})", Base.SubstitutionString("\\1\n"))
"MSKNKSPLLN\nESEKMMSEML\nPMKVSQSKLN\nYEEKVYIPTT\nIRNRKQHCFR\nRFFPYIALFQ\n"

【讨论】:

    【解决方案2】:

    如果速度是个问题,最好避免使用较重的工具,例如正则表达式,并尝试只在底层完成工作,如下所示:

    function intrapad(seq::String)
      buf = IOBuffer((length(seq)*11)>>3) # big enough buffer
      for i=1:10:length(seq)
        write(buf,SubString(seq,i,i+9),'\n')
      end
      return takebuf_string(buf)
    end
    

    速度来自使用 IOBuffer 和 SubStrings 最小化分配。使用 BenchmarkTools 包,我们有:

    julia> @benchmark intrapad(seq)
    BenchmarkTools.Trial: 
      memory estimate:  624.00 bytes
      allocs estimate:  10
      minimum time:     729.00 ns (0.00% GC)
      median time:      767.00 ns (0.00% GC)
      mean time:        862.99 ns (7.84% GC)
      maximum time:     26.86 μs (96.21% GC)
    
    julia> @benchmark replace(seq, r"(.{10})", Base.SubstitutionString("\\1\n"))
    BenchmarkTools.Trial: 
      memory estimate:  720.00 bytes
      allocs estimate:  26
      minimum time:     2.18 μs (0.00% GC)
      median time:      2.29 μs (0.00% GC)
      mean time:        2.43 μs (3.85% GC)
      maximum time:     531.31 μs (98.95% GC)
    

    只有 2.5 倍的加速。 replace函数实现的非常好!

    另一种不用正则表达式的方法是

    join((SubString(seq,i,i+9) for i=1:10:length(seq)),'\n')
    

    这不是那么快(慢 10 倍,在我的机器上没有内存分配损失),但非常易读。

    【讨论】:

    • 这些函数适用于纯 ASCII 字符串,因为它们依赖于 byte-per-char 索引。但在基因组序列等方面,应该没问题(或者在 Julia 的未来版本中,对字符串进行了大修)
    • 最后一个连接示例给出以下错误:错误:LoadError:语法:元组中缺少分隔符
    • 这是一个版本的东西。此示例适用于 0.5。你是 0.4 的吗?
    【解决方案3】:

    类似:

    julia> split(replace(seq, r"(.{10})", s"\1 "))
    6-element Array{SubString{String},1}:
     "MSKNKSPLLN"
     "ESEKMMSEML"
     "PMKVSQSKLN"
     "YEEKVYIPTT"
     "IRNRKQHCFR"
     "RFFPYIALFQ"
    

    如果你想要它作为一个字符串,使用join():

    julia> join(split(replace(seq, r"(.{10})", s"\1 ")), "\n")
    "MSKNKSPLLN\nESEKMMSEML\nPMKVSQSKLN\nYEEKVYIPTT\nIRNRKQHCFR\nRFFPYIALFQ"
    
    julia> println(ans)
    MSKNKSPLLN
    ESEKMMSEML
    PMKVSQSKLN
    YEEKVYIPTT
    IRNRKQHCFR
    RFFPYIALFQ
    

    【讨论】:

    • 结果是一个数组,就像:matchall(r"(.{10})" ,seq)
    • 包含空格的字符串会失败。
    • @MattB。我的蛋白质序列包含空格?所以这就是为什么我总是很饿......!
    • 当然可以,但我想其他人以后发现这篇文章的字符串中会包含更多通用数据。
    【解决方案4】:

    我不知道你如何使用 REGEX,但我认为它可以解决你的问题:

    a = "oiaoueaoeuaoeuaoeuaoeuaoteuhasonetuhaonetuahounsaothunsaotuaosu"
    last = 0
    #create the interval, in your case 10
    salt = 10
    #iterate in string (starts in the 10th value, don't forget julia use 1 to first index)
    for i in salt:salt+1:length(a)
        # replace the string for a new one with '\n'
        a = string(a[1:i], '\n', a[i+1:length(a)])
        last = Int64(i)
    end
    # replace the rest
    a = string(a[1:length(a) - last % salt + 1], '\n', a[length(a) - last % salt + 2:length(a)])
    println(a)
    

    【讨论】:

    猜你喜欢
    • 2011-02-09
    • 2019-04-09
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-13
    • 1970-01-01
    相关资源
    最近更新 更多