在Ruby中将字符串切割成给定长度的块的最佳方法是什么?

时间:2009-04-16 01:06:50

标签: ruby string chunking

我一直在寻找一种优雅而有效的方法,将字符串块化为Ruby中给定长度的子字符串。

到目前为止,我能想到的最好的是:

def chunk(string, size)
  (0..(string.length-1)/size).map{|i|string[i*size,size]}
end

>> chunk("abcdef",3)
=> ["abc", "def"]
>> chunk("abcde",3)
=> ["abc", "de"]
>> chunk("abc",3)
=> ["abc"]
>> chunk("ab",3)
=> ["ab"]
>> chunk("",3)
=> []

您可能希望chunk("", n)返回[""]而不是[]。如果是这样,只需将其添加为方法的第一行:

return [""] if string.empty?

您会推荐更好的解决方案吗?

修改

感谢Jeremy Ruten这个优雅而有效的解决方案:

def chunk(string, size)
    string.scan(/.{1,#{size}}/)
end

9 个答案:

答案 0 :(得分:148)

使用String#scan

>> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{4}/)
=> ["abcd", "efgh", "ijkl", "mnop", "qrst", "uvwx"]
>> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{1,4}/)
=> ["abcd", "efgh", "ijkl", "mnop", "qrst", "uvwx", "yz"]
>> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{1,3}/)
=> ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"]

答案 1 :(得分:17)

这是另一种方法:

"abcdefghijklmnopqrstuvwxyz".chars.to_a.each_slice(3).to_a.map {|s| s.to_s }

=> [“abc”,“def”,“ghi”,“jkl”,“mno”,“pqr”,“stu”,“vwx”,“yz”]

答案 2 :(得分:5)

我认为如果你知道你的字符串是块大小的倍数

,这是最有效的解决方案
def chunk(string, size)
    (string.length / size).times.collect { |i| string[i * size, size] }
end

和部分

def parts(string, count)
    size = string.length / count
    count.times.collect { |i| string[i * size, size] }
end

答案 3 :(得分:2)

我做了一点测试,将约593MB的数据切成18991个32KB的数据。 在按ctrl + C之前,您的slice + map版本使用100%CPU运行了至少15分钟。使用String#unpack的该版本在3.6秒内完成:

def chunk(string, size)
  string.unpack("a#{size}" * (string.size/size.to_f).ceil)
end

答案 4 :(得分:1)

test.split(/(...)/).reject {|v| v.empty?}

拒绝是必要的,因为否则包括集合之间的空白区域。我的正则表达式并没有完全看到如何解决这个问题。

答案 5 :(得分:1)

这是另一种解决情况稍有不同的解决方案,当处理大型字符串时,无需一次存储所有块。这样,它可以一次存储单个块,并且比切片字符串快得多:

io = StringIO.new(string)
until io.eof?
  chunk = io.read(chunk_size)
  do_something(chunk)
end

答案 6 :(得分:1)

一种更好的解决方案,考虑了字符串的最后一部分,该部分可能小于块大小:

def chunk(inStr, sz)  
  return [inStr] if inStr.length < sz  
  m = inStr.length % sz # this is the last part of the string
  partial = (inStr.length / sz).times.collect { |i| inStr[i * sz, sz] }
  partial << inStr[-m..-1] if (m % sz != 0) # add the last part 
  partial
end

答案 7 :(得分:0)

只需text.scan(/.{1,4}/m)就可以解决问题

答案 8 :(得分:-1)

您还有其他一些限制因素吗?否则我会非常想做一些简单的事情,比如

[0..10].each {
   str[(i*w),w]
}