views:

47

answers:

2

Is there a way to open a file case-insensitively in Ruby under Linux? For example, given the string foo.txt, can I open the file FOO.txt?

One possible way would be reading all the filenames in the directory and manually search the list for the required file, but I'm looking for a more direct method.

+1  A: 

You can use Dir.glob with the FNM_CASEFOLD flag to get a list of all filenames that match the given name except for case. You can then just use first on the resulting array to get any result back or use min_by to get the one that matches the case of the orignial most closely.

def find_file(f)
  Dir.glob(f, File::FNM_CASEFOLD).min_by do |f2|
    f.chars.zip(f2.chars).count {|c1,c2| c1 != c2}
  end
end

system "touch foo.bar"
system "touch Foo.Bar"
Dir.glob("FOO.BAR", File::FNM_CASEFOLD) #=> ["foo.bar", "Foo.Bar"]
find_file("FOO.BAR") #=> ["Foo.Bar"]
sepp2k
+3  A: 

Whilst you can't make open case insensitive you can write the directory search you suggested quite concisely. e.g.

filename = Dir.glob('foo.txt', File::FNM_CASEFOLD).first
if filename
  # use filename here
else
  # no matching file
end

Note that whilst the documentation suggests that FNM_CASEFOLD can't be used with glob this appears to be incorrect or out of date.

Alternatives

If you're concerned about using FNM_CASEFOLD then a couple of alternatives are:

filename = Dir.glob('*').find { |f| f.downcase == 'foo.txt' }

or write a little method to build a case insensitive glob for a given filename:

def ci_glob(filename)
  glob = ''
  filename.each_char do |c|
    glob += c.downcase != c.upcase ? "[#{c.downcase}#{c.upcase}]" : c
  end
  glob
end

irb(main):024:0> ci_glob('foo.txt')
=> "[fF][oO][oO].[tT][xX][tT]"

and then you can do:

filename = Dir.glob(ci_glob('foo.txt')).first
mikej
For most of the `IO` and `File` methods, Ruby simply passes everything through to libc. So, I guess this might basically randomly work or not, depending on how your operating system's libc works.
Jörg W Mittag
@Jörg Thanks. I have updated the answer with a couple of other ideas that avoid the need to rely on `FNM_CASEFOLD`.
mikej