Как лучше всего нарезать строку на куски заданной длины в Ruby? - PullRequest
82 голосов
/ 16 апреля 2009

Я искал элегантный и эффективный способ разбить строку на подстроки заданной длины в 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?

Вы бы порекомендовали какое-нибудь лучшее решение?

Редактировать

Спасибо Джереми Рутену за это элегантное и эффективное решение: [edit: НЕ эффективно!]

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

Редактировать

Решение string.scan занимает около 60 секунд, чтобы нарезать 512 КБ на 1 000 кусков 10000 раз, по сравнению с исходным решением на основе слайсов, которое занимает всего 2,4 секунды.

Ответы [ 7 ]

149 голосов
/ 16 апреля 2009

Использование 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"]
17 голосов
/ 04 февраля 2011

Вот еще один способ сделать это:

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

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

5 голосов
/ 26 июля 2015

Я думаю, что это наиболее эффективное решение, если вы знаете, что ваша строка кратна размеру куска

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
1 голос
/ 06 февраля 2019

Лучшее решение, которое учитывает последнюю часть строки, которая может быть меньше размера чанка:

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
1 голос
/ 20 сентября 2018

Вот еще одно решение для немного другого случая, когда обрабатываются большие строки, и нет необходимости хранить все порции за раз. Таким образом, он сохраняет один блок за раз и работает намного быстрее, чем нарезка строк:

io = StringIO.new(string)
until io.eof?
  chunk = io.read(chunk_size)
  do_something(chunk)
end
1 голос
/ 16 апреля 2009
test.split(/(...)/).reject {|v| v.empty?}

Отклонение необходимо, поскольку в противном случае оно содержит пустое пространство между наборами. Мой regex-fu не совсем понимает, как исправить это прямо у меня на голове.

0 голосов
/ 16 апреля 2009

Есть ли у вас другие ограничения? В противном случае я бы ужасно соблазнился сделать что-то простое, как

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