Я искал элегантный и эффективный способ разбить строку на подстроки заданной длины в 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?
Вы бы порекомендовали лучшее решение?
редактировать
Спасибо Джереми Рутену за это элегантное и эффективное решение: [редактировать: НЕ эффективно!]
def chunk(string, size)
string.scan(/.{1,#{size}}/)
end
редактировать
Решению string.scan требуется около 60 секунд, чтобы разбить 512k на 1k фрагментов 10000 раз, по сравнению с исходным решением на основе срезов, которое занимает всего 2,4 секунды.
Ответы:
Использование
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"]
источник
/.
часть означает, что оно будет включать все символы, ЗА ИСКЛЮЧЕНИЕМ новой строки\n
. Если вы хотите включить символы новой строки, используйтеstring.scan(/.{4}/m)
Вот еще один способ сделать это:
"abcdefghijklmnopqrstuvwxyz".chars.to_a.each_slice(3).to_a.map {|s| s.to_s }
=> [«abc», «def», «ghi», «jkl», «mno», «pqr», «stu», «vwx», «yz»]
источник
"abcdefghijklmnopqrstuvwxyz".chars.each_slice(3).map(&:join)
Я думаю, что это наиболее эффективное решение, если вы знаете, что ваша строка кратна размеру блока
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
источник
string.length / size
на(string.length + size - 1) / size
- этот шаблон распространен в коде C, который имеет дело с целочисленным усечением.Вот еще одно решение для немного другого случая, когда обрабатываются большие строки и нет необходимости хранить все фрагменты за раз. Таким образом, он хранит по одному фрагменту за раз и работает намного быстрее, чем нарезка строк:
io = StringIO.new(string) until io.eof? chunk = io.read(chunk_size) do_something(chunk) end
источник
Errno::EINVAL
ошибок типаInvalid argument @ io_fread
иInvalid argument @ io_write
.Я провел небольшой тест, который разбил около 593 МБ данных на 18991 фрагмент по 32 КБ. Ваша версия slice + map работала не менее 15 минут с использованием 100% ЦП, прежде чем я нажал ctrl + C. Эта версия с использованием String # распаковала за 3,6 секунды:
def chunk(string, size) string.unpack("a#{size}" * (string.size/size.to_f).ceil) end
источник
test.split(/(...)/).reject {|v| v.empty?}
Отклонение необходимо, поскольку в противном случае между наборами остается пробел. Мое регулярное выражение не совсем готово к тому, чтобы сразу же понять, как это исправить.
источник
Лучшее решение, которое учитывает последнюю часть строки, которая может быть меньше размера блока:
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
источник
Вы имеете в виду еще какие-то ограничения? Иначе у меня возникло бы ужасное искушение сделать что-нибудь простое, например
[0..10].each { str[(i*w),w] }
источник
Просто
text.scan(/.{1,4}/m)
решает проблемуисточник