Ruby Regex для сопоставления путей файлов Unix и Windows - PullRequest
4 голосов
/ 03 июля 2011

Следующий метод экземпляра берет путь к файлу и возвращает префикс файла (часть перед разделителем):

@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

Пока что этот метод работает только в 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

Я подозреваю, что интерполяция и экранирование строк в Ruby меня смущают.

Как я могу изменить Regex, чтобы он работал как в Unix, так и в Windows?

1 Ответ

6 голосов
/ 03 июля 2011

Я на самом деле не знаю, что означает 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"]

Не толькоЯвляется ли он платформо-независимым, он более разборчивый.

Обновление

Вы пробудили мое любопытство:

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

Вы правы,\ должен быть дважды экранирован, чтобы он работал в регулярном выражении: один раз, чтобы пройти через интерпретатор, еще раз, чтобы пройти через механизм регулярных выражений.(Определенно чувствует себя неловко.) Регулярное выражение:

.+[\\/]([^@]+)@(.+)

Строка:

".+[\\\\/]([^@]+)@(.+)"

Регулярное выражение, которое может быть слишком хрупким для реального использования (как оно будет обрабатывать путьбез / или \ разделителей пути или пути без @ или со слишком большим количеством @?), ищет любое количество символов, один разделитель пути, любое количество не-, @, затемлюбое количество любых символов.Я предполагаю, что первый .+ будет жадно потреблять как можно больше символов, чтобы максимально приблизить совпадение к вправо :

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

Но в зависимости от искаженного вводаданные, это может сделать очень неправильно.

...