読者です 読者をやめる 読者になる 読者になる

同一ファイル名を上書きせず、(1)などをファイル名に付加する(Ruby)

ダウンロードの時などに同じファイル名のファイルがあると、ファイルの末尾に「(1)」などを付けて上書きしないようになっていますが、それを Ruby で実装してみました。えらく大袈裟になってしまいましたが…。fname_check! メソッドで、レシーバーはファイル名の入った String クラスです。カレント・ディレクトリに同一ファイル名がなければ何もせず、あれば「(1)」などを自動的に付加します(破壊的メソッドです)。img.jpgimg(1).jpgimg(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