23
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ファイル名から拡張子とそうでない部分を分ける

Posted at

ファイル名を拡張子とそうでない部分(名前ないのかな?)とに分離したいとき、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"]になってしまいます(上のbasenameextnameの例ではそうなりません)。

次の括弧で.とそれに続く.を含まない文字列をキャプチャします。[^.]としたのは、file_name = "test.1.txt"のようなときにtestと.1.txtに分かれてしまわないようにです。
また、(\.[^.])?でキャプチャしなかったのは、拡張子なしだった場合にextがnilではなく空文字列になるようにして、File.extnameと同じ挙動にしたかったからです。

Regexp#matchにブロックを渡すと、マッチが成功した時だけMatchDataオブジェクトを引数にブロックを実行してくれます。
なので、引数に&:capturesを渡すとキャプチャした部分が配列で返ってきて変数に代入できるというわけです。
ちなみにregexp.match(file_name).capturesでも良さそうにも見えますが、マッチに失敗した時にnil.capturesになってNoMethodErrorになります。

23
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?