※注記
以前はまったくバカみたいなことをやっているので、書き直しました。クラスメソッド File.check_fname_overlapping(fname) です。fname = "hoge"(ファイル名でもディレクトリ名でもいいです)が存在していなければそれをそのまま返します。既に存在していれば、その最後に(1)
を付加した String path/to/hoge(1)
を返します。拡張子があれば path/to/hoge(1).rb
のように拡張子の前に付きます。既にそのディレクトリに hoge(1).rb と hoge(3).rb があれば、path/to/hoge(2).rb
が返ります。(2017/9/14)
class File def self.check_fname_overlapping(fname) #String{dir, base, result, suffix, r}, m, m1 return fname unless File.exist?(fname) dir = File.dirname(fname) base = File.basename(fname) m = /(.+)(\..+)$/.match(base) result, suffix = m ? [m[1], m[2]] : [base, ""] begin result = if (m1 = /(.+)\((\d+)\)$/.match(result)) m1[1] + "(#{m1[2].to_i + 1})" else result + "(1)" end end while File.exist?(r = File.join(dir, result + suffix)) r end end if __FILE__ == $0 p File.check_fname_overlapping("nyao.jpg") p File.check_fname_overlapping("./fuga/hoge.rb") end
自家製の Gem 'kaki-utils' に入れてあります。
kaki-utils | RubyGems.org | your community gem host
ダウンロードの時などに同じファイル名のファイルがあると、ファイルの末尾に「(1)」などを付けて上書きしないようになっていますが、それを Ruby で実装してみました。えらく大袈裟になってしまいましたが…。
fname_check!
メソッドで、レシーバーはファイル名の入った String クラスです。カレント・ディレクトリに同一ファイル名がなければ何もせず、あれば「(1)」などを自動的に付加します(破壊的メソッドです)。img.jpg
img(1).jpg
img(3).jpg
があるところにに "img.jpg".fname_check!
とすれば、"img(2).jpg"
に変わります。
def getdir #カレント・ディレクトリのファイル名・フォルダ名を配列に入れて返す dir = Dir.open('.') a = [] while name = dir.read next if name == "." or name ==".." a << name end dir.close a end def num_exist(fname) #存在する"(数字)"の数字部分を、配列に入れて返す num = [0] a = fname.split(/\./) return false if a.length != 2 getdir.each do |fname1| next unless /\./ =~ fname1 a1 = fname1.split(/\./) next if a1.length != 2 next unless /(.+)\((\d+)\)$/ =~ a1[0] if a1[1] == a[1] and $1 == a[0] num << $2.to_i end end return false if num == [] num.sort end def num_exist1(fname) num = [0] getdir.each do |fname1| next unless /(.+)\((\d+)\)$/ =~ fname1 if $1 == fname num << $2.to_i end end return false if num == [] num.sort end def filename_exist(fname) #与えられたファイル名と一致するファイル名が、カレント・ディレクトリにあるか getdir.each do |fname1| if fname1 == fname return true end end false end def num_max(num) #初めて現れる空白番号を返す l = num.length return l if num[-1] + 1 == l num.eachnum do |i| return i if num[i] != i end end class Array def eachnum #配列による繰り返し for j in 0..(self.length - 1) yield(j) end end end class String def fname_check! return unless filename_exist(self) if /\./ =~ self num = num_exist(self) return unless num a = self.split(/\./) self.replace(a[0] + '(' + num_max(num).to_s + ').' + a[1]) else num = num_exist1(self) return unless num self.replace(self + '(' + num_max(num).to_s + ')') end end end Dir.chdir("D:") i = "text.txt" i.fname_check! fo = open(i, "w+"); fo.puts "a"; fo.close
改良版
やっていることは殆ど変っていないです。(2015/6/21)
def num_exist(fname) #存在する"(数字)"の数字部分を、配列に入れて返す num = [0] a = fname.split(/\./) return false unless a.length == 2 Dir.glob("*").each do |fname1| next unless /\./.match(fname1) a1 = fname1.split(/\./) next unless a1.length == 2 next unless m = /(.+)\((\d+)\)$/.match(a1[0]) num << m[2].to_i if a1[1] == a[1] and m[1] == a[0] end num.uniq.sort end def num_exist1(fname) num = [0] Dir.glob("*").each do |fname1| next unless m = /(.+)\((\d+)\)$/.match(fname1) num << m[2].to_i if m[1] == fname end num.uniq.sort end def filename_exist(fname) #与えられたファイル名と一致するファイル名が、カレント・ディレクトリにあるか Dir.glob("*").each {|fname1| return true if fname1 == fname} false end class Array def num_max #初めて現れる空白番号を(文字列で)返す num = self ln = num.length return ln.to_s if num[-1] + 1 == ln num.each_with_index {|n, i| return i.to_s unless n == i} end end class String def fname_check! s = self return unless filename_exist(s) if /\./.match(s) a = s.split(/\./) self.replace(a[0] + '(' + num_exist(s).num_max + ').' + a[1]) else self.replace(s + '(' + num_exist1(s).num_max + ')') end end end Dir.chdir("D:") i = "text.txt" i.fname_check! fo = open(i, "w+"); fo.puts "a"; fo.close