【问题标题】:What is the best way to chop a string into chunks of a given length in Ruby?在Ruby中将字符串切成给定长度的块的最佳方法是什么?
【发布时间】:2010-10-19 18:11:24
【问题描述】:

我一直在寻找一种优雅而有效的方法,在 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

编辑

string.scan 解决方案将 512k 切成 1k 块 10000 次大约需要 60 秒,而原始基于切片的解决方案仅需要 2.4 秒。

【问题讨论】:

  • 您的原始解决方案尽可能高效和优雅:无需检查字符串的每个字符即可知道将其切入何处,也无需将整个内容转换为数组然后又回来了。

标签: ruby string chunking


【解决方案1】:

使用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"]

【讨论】:

  • 好的,现在这太棒了!我知道必须有更好的方法。非常感谢 Jeremy Ruten。
  • def 块(字符串,大小); string.scan(/.{1,#{size}}/);结束
  • 哇,我现在觉得自己很蠢。我什至都懒得去检查扫描是如何工作的。
  • 小心这个解决方案;这是一个正则表达式,其中的/. 位意味着它将包括除换行符\n 之外的所有字符。如果要包含换行符,请使用 string.scan(/.{4}/m)
  • 多么聪明的解决方案!我喜欢正则表达式,但我不会为此使用量词。谢谢杰里米·鲁滕
【解决方案2】:

这是另一种方法:

"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)
  • 我喜欢这个,因为它适用于包含换行符的字符串。
  • 这应该是公认的解决方案。如果长度与 pattern. 不匹配,使用扫描可能会丢弃最后一个令牌
  • Finbarr 的替代方案为我返回了此答案中的输出(一个包含 9 个字符串对象的数组,最大长度为 3)。答案本身的代码返回 8 个数组,每个数组包含 3 个字母,最后一个包含两个:["y", "z"]。我正在使用 Ruby 3.0.1,fwiw。
【解决方案3】:

如果你知道你的字符串是块大小的倍数,我认为这是最有效的解决方案

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 代码中很常见。跨度>
【解决方案4】:

我做了一个小测试,将大约 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

【讨论】:

    【解决方案5】:

    这里是另一种解决方案略有不同的情况,当处理大字符串时,不需要一次存储所有块。通过这种方式,它一次存储单个块并且执行速度比切片字符串快得多:

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

    【讨论】:

    • 对于非常大的字符串,这是到目前为止最好的方法。这将避免将整个字符串读入内存并避免出现Errno::EINVAL 错误,例如Invalid argument @ io_freadInvalid argument @ io_write
    【解决方案6】:
    test.split(/(...)/).reject {|v| v.empty?}
    

    reject 是必要的,因为它包含集合之间的空白空间。我的 regex-fu 还没有完全明白如何解决这个问题。

    【讨论】:

    • 扫描方法会忘记不匹配的字符,即:如果您尝试在 3 个部分上使用 10 长度的字符串切片,您将有 3 个部分和 1 个元素将被删除,您的方法不会这样做,这样最好。
    【解决方案7】:

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

    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
    

    【讨论】:

      【解决方案8】:

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

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

      【讨论】:

      • 我真的没有任何约束,除了简单、优雅和高效。我喜欢你的想法,但你介意把它翻译成一种方法吗? [0..10] 可能会变得稍微复杂一些。
      • 我修复了我的示例以使用 str[iw,w] 而不是 str[iw...(i+1)*w]。发送
      • 这应该是 (1..10).collect 而不是 [0..10].each。 [1..10] 是一个由一个元素组成的数组——一个范围。 (1..10) 是范围本身。 +each+ 返回调用它的原始集合(在本例中为 [1..10]),而不是块返回的值。我们想要 +map+ 在这里。
      【解决方案9】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-21
        • 2021-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-29
        • 2012-05-31
        相关资源
        最近更新 更多