给定目录的路径(作为字符串,可能相对于当前工作目录或绝对路径),我想打开一个特定文件。我知道文件的文件名,除了它的情况。 (可能是TASKDATA.XML
,TaskData.xml
甚至tAsKdAtA.xMl
。)
受到the accepted answer启发Open a file case-insensitively in Ruby under Linux的启发,我已经想出了这个小模块来生成一个用于匹配文件名称的glob:
module Utils
def self.case_insensitive_glob_string(string)
string.each_char.map do |c|
cased = c.upcase != c.downcase
cased ? "[#{c.upcase}#{c.downcase}]" : c
end.join
end
end
就我的具体情况而言,我打电话给
Utils.case_insensitive_glob_string('taskdata.xml')
并且会得到
'[Tt][Aa][Ss][Kk][Dd][Aa][Tt][Aa].[Xx][Mm][Ll]'
现在我必须展开glob,即将它与给定目录中的实际文件进行匹配。不幸的是,Dir.glob(...)
似乎没有一个参数来传递相对于应该扩展glob的目录(' s路径)。直观地说,创建一个Dir
对象并处理glob会对我有意义:
d = Dir.new(directory_path)
# => #<Dir:/the/directory>
filename = d.glob(Utils.case_insensitive_glob_string('taskdata.xml')).first() # I wish ...
# NoMethodError: undefined method `glob' for #<Dir:/the/directory>
...但glob
仅作为类方法存在,而不是作为实例方法存在。 (任何人都知道为什么这么多Dir
的方法能够完全理解相对到特定的目录? )
所以看起来我有两个选择:
将当前工作目录更改为给定目录
或
第一个选项很简单:使用Dir.chdir
。但是因为这是宝石,我不想弄乱我的宝石用户的环境,我回避它。 (与块概要一起使用时,可能会比我手动(或不)重置工作目录时好一些。)
第二个选项看起来很简单。只需做
taskdata_xml_name_glob = Utils.case_insensitive_glob_string('taskdata.xml')
taskdata_xml_path_glob = File.join(directory_path, taskdata_xml_name_glob)
filename = Dir.glob(taskdata_xml_path_glob).first()
,对吗?几乎。当directory_path
包含在globs中具有特殊含义的字符时,当我只想对文件名进行全局扩展时,它们将错误地展开。这不太可能,但由于Gem用户提供了路径,无论如何我都要考虑它。
我应该在使用文件名glob directory_path
之前逃离File.join
吗?如果是这样,是否有设施可以做到这一点,还是我必须自己编写转义函数?
或者我应该使用不同的方法(无论是chdir
还是其他不同的方法)?
答案 0 :(得分:2)
如果我实现了这种行为,我会过滤一个由Dir#entries
返回的数组:
Dir.entries("#{target}").select { |f| f =~ /\A#{filename}\z/i }
请注意,在unix平台上,.
和..
条目也会列出,但在第二步中不太可能匹配它们。此外,文件名可能应使用Regexp.escape
转义:
Dir.entries("#{target}").select { |f| f =~ /\A#{Regexp.escape(filename)}\z/i }