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.
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
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"]