Ruby Regex将匹配Unix和Windows文件路径

Ste*_*ing 4 ruby regex filepath

以下实例方法采用文件路径并返回文件的前缀(分隔符之前的部分):

@separator = "@"

def table_name path
  regex = Regexp.new("\/[^\/]+#{@separator}")
  path.match(regex)[0].gsub(/^.|.$/,'').downcase.to_sym
end

table_name "bla/bla/bla/Prefix@invoice.csv"
# => :prefix
Run Code Online (Sandbox Code Playgroud)

到目前为止,此方法仅适用于Unix.为了使它在Windows上运行,我还需要捕获反斜杠(\).不幸的是,那是我被困的时候:

@separator = "@"

def table_name path
  regex = Regexp.new("(\/|\\)[^\/\\]+#{@separator}")
  path.match(regex)[0].gsub(/^.|.$/,'').downcase.to_sym
end

table_name("bla/bla/bla/Prefix@invoice.csv")
# RegexpError: premature end of char-class: /(\/|\)[^\/\]+@/

# Target result:
table_name("bla/bla/bla/Prefix@invoice.csv")
# => :prefix
table_name("bla\bla\bla\Prefix@invoice.csv")
# => :prefix
Run Code Online (Sandbox Code Playgroud)

我怀疑Ruby的字符串插值和转义是让我困惑的地方.

我如何更改正则表达式以使其在Unix和Windows上都能正常工作?

sar*_*old 6

我实际上并不知道所指的bla/bla/bla/Prefix@invoice.csv是什么; 是bla/bla/bla/bla所有目录,还有文件名Prefix@invoice.csv

假设我已正确理解您的文件名,我建议使用File.split():

irb> (path, name) = File.split("bla/bla/bla/Prefix@invoice.csv")
=> ["bla/bla/bla", "Prefix@invoice.csv"]
irb> (prefix, postfix) = name.split("@")
=> ["Prefix", "invoice.csv"]
Run Code Online (Sandbox Code Playgroud)

它不仅与平台无关,而且更易读.

更新

你引起了我的好奇心:

>> wpath="blah\\blah\\blah\\Prefix@invoice.csv"
=> "blah\\blah\\blah\\Prefix@invoice.csv"
>> upath="bla/bla/bla/Prefix@invoice.csv"
=> "bla/bla/bla/Prefix@invoice.csv"
>> r=Regexp.new(".+[\\\\/]([^@]+)@(.+)")
=> /.+[\\\/]([^@]+)@(.+)/
>> wpath.match(r)
=> #<MatchData "blah\\blah\\blah\\Prefix@invoice.csv" 1:"Prefix" 2:"invoice.csv">
>> upath.match(r)
=> #<MatchData "bla/bla/bla/Prefix@invoice.csv" 1:"Prefix" 2:"invoice.csv">
Run Code Online (Sandbox Code Playgroud)

你是对的,\必须双重转义才能使它在正则表达式中工作:一次通过解释器,再次通过正则表达式引擎.(绝对感觉很尴尬.)正则表达式是:

.+[\\/]([^@]+)@(.+)
Run Code Online (Sandbox Code Playgroud)

字符串是:

".+[\\\\/]([^@]+)@(.+)"
Run Code Online (Sandbox Code Playgroud)

正则表达式,对于实际使用可能太脆弱(如何处理没有/\路径分隔符的路径@或没有或有太多的路径名@?),查找任意数量的字符,单个路径分隔符,任何数量的非@,@,然后任意数量的任何字符.我假设第一个.+将贪婪地消耗尽可能多的字符,以使匹配尽可能向右:

>> evil_path="/foo/bar@baz/blorp/Prefix@invoice.csv"
=> "/foo/bar@baz/blorp/Prefix@invoice.csv"
>> evil_path.match(r)
=> #<MatchData "/foo/bar@baz/blorp/Prefix@invoice.csv" 1:"Prefix" 2:"invoice.csv">
Run Code Online (Sandbox Code Playgroud)

但是,根据输入数据格式错误,它可能会做错了.