任意の文字列に対して、ある文字数毎に区切り文字列を入れたものを取得したい


文字数のカウントを見たいために、対象の文字列を、ある文字数ごとに、区切りの文字列で置き替えた文字列を作成したいとします。

このとき、文字数毎というのは、たとえば5文字毎に区切り文字を入れたいとする場合、元の文字列を`□□□□□□□□□□□□□□□□□□□□`、区切り文字を`■`とした時、

[code]

    □□□□■□□□□■□□□□■□□□□■
    ~~~~|~~~~|~~~~|~~~~
    4+1 |4+1 |4+1 |4+1
    
[/code]

といった文字列になるようなものを意図としています。

上記の実装例として、`String`クラスをオープンにして、下のようなメソッドを新しく作成したとします:

[code]

     class String
       def step_replace(steps, replace_str)
         split_str = self.split('').each_slice(steps)
         range_max = steps - replace_str.length - 1
         split_str.map { |x| x.slice(0..range_max) + [replace_str] }.join
       end
     end
    
[/code]

このとき、この`step_replace`は下のような形で利用することが可能です。

[code]

     puts ("あいうえお" * 3).step_replace(5, "んん")
     # output: あいうんんあいうんんあいうんん
    
[/code]

しかし、この`step_replace`は「文字列をいちいち`split`で配列にしたりしたものを加工している」という点で、冗長であるように感じます。もうすこし簡潔な書き方があるような気がしましたが、如何でしょうか。それともこれ以上簡潔にはならないのでしょうか。




正規表現を使ってこんなのはどうでしょう。

(最後のasは端数でも大丈夫という確認用です）

[code]

    irb(main):001:0> ("あいうえお"*3+"as").scan(/(.{1,3}).{,2}/).join("んん")
    => "あいうんんあいうんんあいうんんas"
    
[/code]

これはjoinの前でこのようなリストが得られてそこからのjoinになります。

[code]

    irb(main):002:0> ("あいうえお"*3+"as").scan(/(.{1,3}).{,2}/)
    => [["あいう"], ["あいう"], ["あいう"], ["as"]]
    
[/code]

変数を適用するなら以下の様にできます

[code]

    irb(main):001:0> steps=5
    => 5
    irb(main):002:0> replace_str="んん"
    => "んん"
    irb(main):002:0> str_len=replace_str.length
    => 2
    irb(main):008:0> ("あいうえお"*3+"as").scan(/(.{1,#{steps-str_len}}).{,#{str_len}}/).join(replace_str)
    => "あいうんんあいうんんあいうんんas"
    
[/code]

おまけですが、挿入の場合は以下でできると思います。

[code]

    irb(main):003:0> ("あいうえお"*3).scan(/.{1,5}/).join("んん")
    => "あいうえおんんあいうえおんんあいうえお"
    
[/code]

