Инкрементная часть строки в Ruby - PullRequest
1 голос
/ 27 декабря 2010

У меня есть метод в скрипте Ruby, который пытается переименовать файлы перед их сохранением.Выглядит это так:

def increment (path)
    if path[-3,2] == "_#"
        print "    Incremented file with that name already exists, renaming\n"
        count = path[-1].chr.to_i + 1
        return path.chop! << count.to_s
    else
        print "    A file with that name already exists, renaming\n"
        return path << "_#1"
    end
end

Допустим, у вас есть 3 файла с одинаковым именем, которые сохраняются в каталоге, и мы скажем, что файл называется example.mp3.Идея состоит в том, что первое будет сохранено как example.mp3 (поскольку оно не будет перехвачено if File.exists?("#{file_path}.mp3") в другом месте скрипта), второе будет сохранено как example_#1.mp3 (поскольку оно перехвачено elseчасть описанного выше метода), а третья как example_#2.mp3 (поскольку она перехватывается частью if вышеупомянутого метода).

У меня двойственная проблема.

1) if path[-3,2] == "_#" не будет работать для файлов с целым числом более одной цифры (например, example_#11.mp3), поскольку размещение символов будет неправильным (вам нужно, чтобы оно было path[-4,2]но тогда это не справляется с 3-значными числами и т. д.).

2) Я никогда не сталкиваюсь с проблемой 1), поскольку метод не может надежно отлавливать имена файлов.В настоящий момент он будет переименовывать первый в example_#1.mp3, но второй переименовывается в то же самое (что приводит к перезаписи ранее сохраненного файла).

Возможно, это слишком расплывчато для переполнения стека, но я не могу найти ничего, что решало бы проблему увеличения определенной части строки.

Заранее спасибо!

Редактирование / обновление:

Представленный ниже метод Уэйна, кажется, работает сам по себе, но не в том случае, если он включен как часть всего сценария - он может увеличить файл один раз (с example.mp3 до example_#1.mp3) но не справляется с принятием example_#1.mp3 и увеличением его до example_#2.mp3.Чтобы обеспечить немного больше контекста - в настоящее время, когда скрипт находит файл для сохранения, он передает имя методу Уэйна следующим образом:

file_name = increment(image_name)
File.open("images/#{file_name}.jpeg", 'w') do |output|
    open(image_url) do |input|
        output << input.read
    end
end    

Я немного отредактировал скрипт Уэйна, так что теперь он выглядит так:

def increment (name)
    name = name.gsub(/\s{2,}|(http:\/\/)|(www.)/i, '')
    if File.exists?("images/#{name}.jpeg")
        _, filename, count, extension = *name.match(/(\A.*?)(?:_#(\d+))?(\.[^.]*)?\Z/)
        count = (count || '0').to_i + 1
        "#{name}_##{count}#{extension}"
    else
        return name
    end
end

Куда я иду?Еще раз спасибо заранее.

Ответы [ 2 ]

7 голосов
/ 27 декабря 2010

Будет выполнено регулярное выражение:

#!/usr/bin/ruby1.8

def increment(path)
  _, filename, count, extension = *path.match(/(\A.*?)(?:_#(\d+))?(\.[^.]*)?\Z/)
  count = (count || '0').to_i + 1
  "#{filename}_##{count}#{extension}"
end

p increment('example')        # => "example_#1"
p increment('example.')       # => "example_#1."
p increment('example.mp3')    # => "example_#1.mp3"
p increment('example_#1.mp3') # => "example_#2.mp3"
p increment('example_#2.mp3') # => "example_#3.mp3"

Это, вероятно, не имеет значения для кода, который вы пишете, но если у вас когда-нибудь может быть несколько потоков или процессов, использующих этот алгоритм в одних и тех же файлах, существует условие гонки при проверке существования перед сохранением: найти то же имя файла не используется и написать в него. Если это имеет значение для вас, откройте файл в режиме, который не работает, если он существует, спасая исключение. Когда возникает исключение, выберите другое имя. Грубо:

loop do
  begin
    File.open(filename, File::CREAT | File::EXCL | File::WRONLY) do |file|
      file.puts "Your content goes here"
    end
    break
  rescue Errno::EEXIST
    filename = increment(filename)
    redo
  end
end
2 голосов
/ 27 декабря 2010

Вот вариант, который не принимает имя файла с существующим счетом:

def non_colliding_filename( filename )
  if File.exists?(filename)
    base,ext = /\A(.+?)(\.[^.]+)?\Z/.match( filename ).to_a[1..-1]
    i = 1
    i += 1 while File.exists?( filename="#{base}_##{i}#{ext}" )
  end
  filename
end

Доказательство:

%w[ foo bar.mp3 jim.bob.mp3 ].each do |desired|
  3.times{
    file = non_colliding_filename( desired )
    p file
    File.open( file, 'w' ){ |f| f << "tmp" }
  }
end
#=> "foo"
#=> "foo_#1"
#=> "foo_#2"
#=> "bar.mp3"
#=> "bar_#1.mp3"
#=> "bar_#2.mp3"
#=> "jim.bob.mp3"
#=> "jim.bob_#1.mp3"
#=> "jim.bob_#2.mp3"
...