如何根据Ruby之间的字符数在Ruby中拆分字符串?

Rub*_*uby 0 ruby string substring

我现在正在写一个Ruby宝石,但是我试图想到一个有效的方法在下面做到以下几点:

P<GBRLAST<<FIRST<MIDDLE<<<<<<<<<<<<<<<<<<<<<
Run Code Online (Sandbox Code Playgroud)

从那我想得到:GBR,最后,第一中间作为输出

我知道我可以使用类似的东西:

 string[2...5]
Run Code Online (Sandbox Code Playgroud)

输出"GBR",但我如何将"最后"和"第一中间"作为其他输出?

总是<<在LAST和FIRST <之间,以及FIRST和MIDDLE之间,但LAST,FIRST和MIDDLE可以是任意长度(它们是示例名称),并且可能不仅仅是FIRST和MIDDLE与<分隔符.例如:

 P<GBRLAST<<FIRST<MIDDLE<LION<<<<<<<<<<<<<<<<
Run Code Online (Sandbox Code Playgroud)

我能看到这样做的唯一方法是通过渐进式if循环,但有没有一种更有效和本地的方式将其拆分,我没有看到?

A F*_*kly 5

a = "P<GBRLAST<<FIRST<MIDDLE<LION<<<<<<<<<<<<<<<<"

parts = a.gsub(/<+/, '<').split('<')
# => ["P", "GBRLAST", "FIRST", "MIDDLE", "LION"] 
Run Code Online (Sandbox Code Playgroud)

这会将所有'<<<<'字符串折叠为单个'<'字符,然后使用它作为分隔符将字符串拆分.

first = parts[1][0..2]
# => "GBR" 

second = parts[1][3..-1]
# => "LAST" 

the_rest = parts[2..-1]
# => ["FIRST", "MIDDLE", "LION"] 
Run Code Online (Sandbox Code Playgroud)

做你想做的事.

这假定'first'总是3个字符长,但除非你有更多规则,否则我看不到任何其他分裂方式.

编辑:

评论者提出了一些很好的优化建议.

@ 7stud建议:

parts = a.gsub(/<+/, '<').split('<')
Run Code Online (Sandbox Code Playgroud)

可以改写为:

parts = a.split(/<+/)
Run Code Online (Sandbox Code Playgroud)

就处理器周期而言,这也更有效.

Benchmark.measure { 10000.times { a.split(/<+/) }}
# => #<Benchmark::Tms:0x007fc0320b84a8 @label="", @real=0.053515, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.04999999999999999, @total=0.04999999999999999> 
Benchmark.measure { 10000.times { a.gsub(/<+/, '<').split('<') }}
# => #<Benchmark::Tms:0x007fc0328fe3d8 @label="", @real=0.081377, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.07999999999999996, @total=0.07999999999999996> 
Run Code Online (Sandbox Code Playgroud)

@Shadwell指出:

我们可以拆分单个'<'并删除空白条目以避免使用正则表达式.

a.split("<").select { |s| !s.empty? }
Run Code Online (Sandbox Code Playgroud)

避免正则表达式是一个很好的目标 - 正则表达式通常效率低下,是一种应用于字符串操作的通用语言,而不是优化的,有针对性的操作.它们也是不透明的,容易出错,容易出现边缘情况并且难以维护.

然而,在这种情况下,使用select比在正则表达式上拆分效率稍差.此外,正则表达式很简单,不用担心太多.

Benchmark.measure { 10000.times { a.split(/<+/) }}
# => #<Benchmark::Tms:0x007fc0320b84a8 @label="", @real=0.053515, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.04999999999999999, @total=0.04999999999999999>  
Benchmark.measure { 10000.times { a.split("<").select { |s| !s.empty? } }}
# => #<Benchmark::Tms:0x007fc032039ea0 @label="", @real=0.061219, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.06, @total=0.06> 
Run Code Online (Sandbox Code Playgroud)

这里应该注意的是,除非我们处理真正大量的数据,否则不会真正感受到这些速度差异,因此应首先考虑代码可读性.

@careyswoveland评论我最喜欢的:

[a[2..4]].concat(a[5..-1].split(/<+/))
Run Code Online (Sandbox Code Playgroud)

它返回一个很好的所有值数组,并处理任意数量的额外字符串.需要进行一些精神上的解包才能理解正在发生的事情,但这是Ruby的强大和简洁的一个真正美丽的例子.

  • 你也可以拆分`"<"`并删除所有空白条目:`a.split("<").select {| s | !s.empty?}`避免做`gsub` (3认同)