Generate unique filename (Ruby)

Hey guys!

Happy New Year! I am glad to be back this year with some interesting news. Thank you for blasting my mailbox with your questions and suggestions! I will do my best to answer all of them as soon as I can!

One of the readers asked me a  question that I  think is very interesting. Moreover, I have faced the same issue recently, and today I want to share it with you.

So topic of today is:

How to ensure filename is always unique ? [Ruby] 

Why is this a problem?

Imagine you are copying a file ‘a.jpg’  in the directory where this file already exist. Some of the systems I know just give you a validation error and ask to rename a file. But it is a good user experience ? I don’t think so.

So what are our options to tackle this issue ?

Option 1. Append some random string to the filename e.g ‘a-asdsds.jpg’

But is it a solution? How random are those strings ? Does this look appealing ?
I think it is a solution, but a pretty ugly one.

Option 2. Append a number to the filename that is incremented with every creation

This solutions is definitely not a new one, it is used by some OS, and I think it might be the easiest and most elegant one. E.g ‘picture.png’ becomes ‘picture-1.png’ and then ‘picture-2.png’.

Any other options you can think of ? Please comment and let me know 😉

Meanwhile we will go with Option 2.

Next question is:

Based on what should we increment this number ?

Some of the solutions I have seen before is based on counting number of files, and then incrementing count by one. This makes sense, but unfortunately this won’t work. 😉

Why?

Lets say I have files ‘a-1.jpg’, ‘a-2.jpg’,a-3.jpg’. Count is 3 here. Now Imagine I have  deleted file ‘a-2.jpg’, and I want to add a new file. So current Count is 2 (a-1 and a-3), so my next filename based on logic should be a-3 (we increment count by 1 -> 2+1), but it is already taken.

Here is the strategy that I think might work:

Lets say we again have 3 files –  ‘a-1.jpg’, ‘a-2.jpg’,a-3.jpg’. We will take the numbers of those files (1,2,3) find the largest and increment it by one. So if we apply to it to previous use-case – we will delete ‘a-2’, but largest number is still 3, so new filename will be ‘a-4’.

What do you think ?

I know that most of you are code-maniacs and want to dig into code right away, so I will shut up now and show you some code I have built to do exactly this :)

Short Explanation:

1. I take the file lets say ‘a.jpg’, and divide it into ‘a’ as a name and ‘.jpg’ as an extension.
2. I have a @file_list array that is pre-populated with filenames in the directory lets say @file_list is  [‘a-1.jpg’,’a-3.jpg’, ‘a.jpg’,’a-1.png’] .
3. I check if filename in @file_list consist of ‘a’ and with extension ‘.jpg’, thus I get 3 files [‘a-1.jpg’,’a.jpg’,’a-3.jpg’]
4. I have another array ‘related_file_indexes’, that splits every element of an array by last ‘-‘ and returns the number which gives me [‘1′,’3’]
5. I built filename with name, max of 1 and 3 which is 3+1=4 and append extension so my output will be ‘a-4.jpg

Full version of this code  with unit tests can be found here .

Once again, all suggestions and recommendation are more than welcome in the comments to this post 😉

Have a wonderful day!

Anatoly

Anatoly Spektor

IT Consultant with 6 years experience in Software Development and IT Leadership. Participated in such projects as Eclipse IDE, Big Blue Button, Toronto 2015 Panam Games.

Leave a Reply to Paynito Cancel Reply

Your email address will not be published. Required fields are marked *

Discussion

  1. Nop

    Tempfile.new or SecureRandom.hex
    If you really want to keep the name-I convention, then keep a simple text file with a counter and RENAME a temporary file to name-I. rename is an atomic operation.

  2. Anatoly Spektor

    Hey Nop,

    Both Tempfile.new and SecureRandom.hex – definitely work if you want to create brand new random file.

    The use case I was showing here was when you are having file ‘test’ and you are copying it to the directory where it already exists, I don’t think you would expect to have file name ’12sdsdsds’ (or other hex string) after copying file ‘test’ :)

    Having a simple text file with a counter – would work as well – very good suggestion!

    As well as having a settings db table that keeps that kind of stuff.

    I personally think that Reading a file every time you want to rename it is too expensive.

    Thanks a lot for your suggestions!

    Anatoly

  3. Paynito

    Hi I want to rename so that if Text1999.pdf already exists then the next file created (Via rename) is saved as Text1999b.pdf instead of overwriting Text1999.pdf.
    #!/usr/bin/ruby
    require ‘fileutils’

    list = Dir.glob(‘*.pdf’)
    list.each do |src|
    b = /^(?[a-z-]+).*_(?[\d()]+)/i.match(src)
    c = b[1]+b[2]+”.pdf”

    def myRename (c)
    counter = 0
    if
    File.file?(c)
    [suffix = (“a”..”z”).to_a
    d = b[1]+b[2]+suffix[counter]+”.pdf”
    counter = counter +1 #increment
    File.rename(src,d)
    ]
    else[

    File.rename(src, c)
    ]
    end
    end
    end

    1. Anatoly Spektor

      Very good use case Paynito!

arrow