【Ruby】正規表現でマッチした文字列の一部分だけを置換するには?


問題

以下のような文字列があった場合、.png で終わるURLのhttpだけをhttpsに変えることはできるでしょうか?

str = 'kkk<a href="http://abcdege/hoge222/bar/t22est.md">aaab.png 
bbb<a href="http://abcdege/ho22aage/bddfear/ted2st.png">aaak
ubotabbbccxcb <a href="http://abcdege/22hoge/b22ar/tfeest.md">aa23a
bbkubotasbbb <a href="http://abcdege/hoffee11ge/bar/test.html">appkub
otapoooabbb<a href="http://abcdege/ho22aage/bddfear/ted2swwt.png">ab
ddbbb.png'

考察

結構悩みました。
rubyのgsub(正規表現, 文字列)メソッドを使って置換しようとしたのですが、

  • 正規表現でマッチするものがURL全体
  • そのマッチしたURLをある固定の文字列で置換してしまう

そこで、マッチしたもの(URL)の一部分(今回はhttpの部分)だけを置換する方法はないか考えました。

考えた結果

上記の考え方では思いつかなかったです。そもそも、gsub(正規表現, 文字列)メソッドのメソッドだけを使おうとする考えがよくなかったかもしれないです。
正規表現のブロックみたいなのを使った方法もあるかもしれないですが。
ただ、結局、それと同等の結果を導く手順は作成できました。

2段階subで置換します。

考えたアルゴリズムの大枠

  • .png で始まるURLをすべて配列に格納
  • その配列の要素を1つずつ読んで
".png で始まるURL".gsub(http, "https")

で置換するとい流れです。

考えたアルゴリズムの詳細(完成品)

  • .png で始まるURLをすべて配列に格納

まず文字列からURLを抜き出します。
ここで、String#scanを使おうと思ったのですが、「文字列からURLを抽出する」というメソッドがすでにあるようです。。(すごすぎる)
参考:文字列からURLを抽出する - Ruby Tips!

URI.extract(str)
#=> ["http://abcdege/hoge222/bar/t22est.md", "http://abcdege/ho22aage/bddfear/ted2st.png", "http://abcdege/22hoge/b22ar/tfeest.md", "http://abcdege/hoffee11ge/bar/test.html", "http://abcdege/ho22aage/bddfear/ted2swwt.png"]

次に、これらを使ってそれぞれを判断して置換していきます。

(完成品)
URI.extract(str).each do |uri|
  if /.*(\.png)$/.match(uri)
    new_uri = uri.sub("http","https")
    str = str.sub(uri, new_uri)
  end
end
str
#=> "kkk<a href=\"http://abcdege/hoge222/bar/t22est.md\">aaab.png \nbbb<a href=\"https://abcdege/ho22aage/bddfear/ted2st.png\">aaak\nubotabbbccxcb <a href=\"http://abcdege/22hoge/b22ar/tfeest.md\">aa23a\nbbkubotasbbb <a href=\"http://abcdege/hoffee11ge/bar/test.html\">appkub\notapoooabbb<a href=\"https://abcdege/ho22aage/bddfear/ted2swwt.png\">ab\nddbbb.png"

2段階subで置換するのがポイントな気がします。