ファイル名を拡張子とそうでない部分(名前ないのかな?)とに分離したいとき、Rubyなら
file_name = "test.txt"
name = File.basename(file_name, ".*") #=> "test"
ext = File.extname(file_name) #=> ".txt"
とできるわけですが、なんとなくDRYじゃない感じがして、一行で書いてみたので残しておきます。
file_name = "test.txt"
name, ext = /\A(.+?)((?:\.[^.]+)?)\z/.match(file_name, &:captures) #=> ["test", ".txt"]
正規表現の最初の括弧でnameの部分をキャプチャします。
普通に(.+)だとgreedyにマッチするので、拡張子部分までマッチしてしまって上手くいきません。
また(.*?)だと、filename = ".bashrc"のとき、[name, ext] == ["", ".bashrc"]になってしまいます(上のbasenameとextnameの例ではそうなりません)。
次の括弧で.とそれに続く.を含まない文字列をキャプチャします。[^.]としたのは、file_name = "test.1.txt"のようなときにtestと.1.txtに分かれてしまわないようにです。
また、(\.[^.])?でキャプチャしなかったのは、拡張子なしだった場合にextがnilではなく空文字列になるようにして、File.extnameと同じ挙動にしたかったからです。
Regexp#matchにブロックを渡すと、マッチが成功した時だけMatchDataオブジェクトを引数にブロックを実行してくれます。
なので、引数に&:capturesを渡すとキャプチャした部分が配列で返ってきて変数に代入できるというわけです。
ちなみにregexp.match(file_name).capturesでも良さそうにも見えますが、マッチに失敗した時にnil.capturesになってNoMethodErrorになります。