Szukałem eleganckiego i wydajnego sposobu na podzielenie sznurka na podciągi o określonej długości w Rubim.
Jak dotąd najlepsze, co mogłem wymyślić, to:
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)
=> []
Może chcesz chunk("", n)
, aby powrócić [""]
zamiast []
. Jeśli tak, po prostu dodaj to jako pierwszy wiersz metody:
return [""] if string.empty?
Czy poleciłbyś jakieś lepsze rozwiązanie?
Edytować
Podziękowania dla Jeremy'ego Rutena za to eleganckie i wydajne rozwiązanie: [edytuj: NIE wydajne!]
def chunk(string, size)
string.scan(/.{1,#{size}}/)
end
Edytować
Rozwiązanie string.scan zajmuje około 60 sekund, aby posiekać 512k na 1k kawałków 10000 razy, w porównaniu z oryginalnym rozwiązaniem opartym na plasterkach, które zajmuje tylko 2,4 sekundy.
Odpowiedzi:
Zastosowanie
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"]
źródło
/.
bit oznacza, że będzie zawierał wszystkie znaki Z WYJĄTKIEM nowych linii\n
. Jeśli chcesz dołączyć nowe linie, użyjstring.scan(/.{4}/m)
Oto inny sposób, aby to zrobić:
"abcdefghijklmnopqrstuvwxyz".chars.to_a.each_slice(3).to_a.map {|s| s.to_s }
=> ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"]
źródło
"abcdefghijklmnopqrstuvwxyz".chars.each_slice(3).map(&:join)
Myślę, że jest to najbardziej wydajne rozwiązanie, jeśli wiesz, że twój ciąg jest wielokrotnością rozmiaru fragmentu
def chunk(string, size) (string.length / size).times.collect { |i| string[i * size, size] } end
i na części
def parts(string, count) size = string.length / count count.times.collect { |i| string[i * size, size] } end
źródło
string.length / size
go(string.length + size - 1) / size
- ten wzorzec jest powszechny w kodzie C, który ma do czynienia z obcinaniem liczb całkowitych.Oto kolejne rozwiązanie dla nieco innego przypadku, podczas przetwarzania dużych ciągów i nie ma potrzeby przechowywania wszystkich fragmentów naraz. W ten sposób przechowuje pojedyncze fragmenty na raz i działa znacznie szybciej niż krojenie ciągów:
io = StringIO.new(string) until io.eof? chunk = io.read(chunk_size) do_something(chunk) end
źródło
Errno::EINVAL
błędów, takich jakInvalid argument @ io_fread
iInvalid argument @ io_write
.Zrobiłem mały test, który dzieli około 593 MB danych na 18991 32 KB części. Twoja wersja plasterka + mapy działała przez co najmniej 15 minut przy 100% CPU, zanim nacisnąłem ctrl + C. Ta wersja przy użyciu String # unpack zakończyła się w 3,6 sekundy:
def chunk(string, size) string.unpack("a#{size}" * (string.size/size.to_f).ceil) end
źródło
test.split(/(...)/).reject {|v| v.empty?}
Odrzucenie jest konieczne, ponieważ w przeciwnym razie obejmuje spację między zestawami. Moje regex-fu nie jest do końca gotowe, aby zobaczyć, jak to naprawić od razu na czubku mojej głowy.
źródło
Lepsze rozwiązanie uwzględniające ostatnią część ciągu, która może być mniejsza niż rozmiar fragmentu:
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
źródło
Czy masz na myśli jakieś inne ograniczenia? W przeciwnym razie bardzo bym się kusił, żeby zrobić coś prostego
[0..10].each { str[(i*w),w] }
źródło
Po prostu
text.scan(/.{1,4}/m)
rozwiązuje problemźródło