web-dev-qa-db-fra.com

Quel est le meilleur moyen de couper une chaîne en morceaux de longueur donnée en Ruby?

Je recherchais un moyen élégant et efficace de découper une chaîne en sous-chaînes d’une longueur donnée en Ruby.

Jusqu'ici, le mieux que j'ai pu trouver est la suivante:

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)
=> []

Vous voudrez peut-être que chunk("", n) renvoie [""] au lieu de []. Si c'est le cas, ajoutez simplement ceci comme première ligne de la méthode:

return [""] if string.empty?

Recommanderiez-vous une meilleure solution?

Modifier

Merci à Jeremy Ruten pour cette solution élégante et efficace:

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

Utilisez 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"]
146
Jeremy Ruten

Voici une autre façon de le faire:

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

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

17
Jason

Je pense que c'est la solution la plus efficace si vous savez que votre chaîne est un multiple de la taille d'un morceau

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

et pour les pièces

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

Voici une autre solution pour des cas légèrement différents, lors du traitement de chaînes de grande taille, sans qu'il soit nécessaire de stocker tous les morceaux à la fois. De cette manière, il stocke un seul morceau à la fois et exécute beaucoup plus rapidement que le découpage en chaînes:

io = StringIO.new(string)
until io.eof?
  chunk = io.read(chunk_size)
  do_something(chunk)
end
1
prcu
test.split(/(...)/).reject {|v| v.empty?}

Le rejet est nécessaire car il inclut sinon l'espace vide entre les ensembles. Mon regex-fu n'est pas tout à fait à la recherche de solutions à cela.

1
Chuck

Une meilleure solution qui prend en compte la dernière partie de la chaîne qui pourrait être inférieure à la taille du bloc:

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          # add the last part 
end
0
kirkytullins

Y a-t-il d'autres contraintes que vous avez en tête? Sinon, je serais terriblement tenté de faire quelque chose de simple, comme

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