Как сделать строку Ruby безопасной для файловой системы? - PullRequest
41 голосов
/ 21 декабря 2009

У меня есть пользовательские записи в качестве имен файлов. Конечно, это плохая идея, поэтому я хочу отбросить все, кроме [a-z], [A-Z], [0-9], _ и -.

Например:

my§document$is°°   very&interesting___thisIs%nice445.doc.pdf

должно стать

my_document_is_____very_interesting___thisIs_nice445_doc.pdf

а затем в идеале

my_document_is_very_interesting_thisIs_nice445_doc.pdf

Есть ли хороший и элегантный способ сделать это?

Ответы [ 6 ]

58 голосов
/ 30 мая 2012

Я хотел бы предложить решение, которое отличается от старого. Обратите внимание, что старый использует устаревший returning. Кстати, это в любом случае характерно для Rails , и вы не упомянули Rails в своем вопросе (только как тег). Кроме того, существующее решение не может закодировать .doc.pdf в _doc.pdf, как вы и просили. И, конечно же, он не сводит подчеркивания в одно целое.

Вот мое решение:

def sanitize_filename(filename)
  # Split the name when finding a period which is preceded by some
  # character, and is followed by some character other than a period,
  # if there is no following period that is followed by something
  # other than a period (yeah, confusing, I know)
  fn = filename.split /(?<=.)\.(?=[^.])(?!.*\.[^.])/m

  # We now have one or two parts (depending on whether we could find
  # a suitable period). For each of these parts, replace any unwanted
  # sequence of characters with an underscore
  fn.map! { |s| s.gsub /[^a-z0-9\-]+/i, '_' }

  # Finally, join the parts with a period and return the result
  return fn.join '.'
end

Вы не указали все подробности о конверсии. Таким образом, я делаю следующие предположения:

  • Должно быть не более одного расширения имени файла, что означает, что в имени файла должно быть не более одного периода
  • Конечные периоды не отмечают начало расширения
  • Ведущие периоды не отмечают начало продления
  • Любая последовательность символов за пределами A - Z, a - z, 0 - 9 и - должна быть свернута в один _ (т.е. подчеркивание само по себе считается как запрещенный символ, и строка '$%__°#' станет '_' - вместо '___' из частей '$%', '__' и '°#')

Сложная часть этого - разделение имени файла на основную часть и расширение. С помощью регулярного выражения я ищу последний период, за которым следует нечто иное, чем период, чтобы в строке не было следующих периодов, соответствующих одинаковым критериям. Однако ему должен предшествовать какой-то символ, чтобы быть уверенным, что это не первый символ в строке.

Мои результаты тестирования функции:

1.9.3p125 :006 > sanitize_filename 'my§document$is°°   very&interesting___thisIs%nice445.doc.pdf'
 => "my_document_is_very_interesting_thisIs_nice445_doc.pdf"

что я думаю, это то, что вы просили. Я надеюсь, что это красиво и элегантно.

27 голосов
/ 21 декабря 2009

С http://devblog.muziboo.com/2008/06/17/attachment-fu-sanitize-filename-regex-and-unicode-gotcha/:

def sanitize_filename(filename)
  returning filename.strip do |name|
   # NOTE: File.basename doesn't work right with Windows paths on Unix
   # get only the filename, not the whole path
   name.gsub!(/^.*(\\|\/)/, '')

   # Strip out the non-ascii character
   name.gsub!(/[^0-9A-Za-z.\-]/, '_')
  end
end
17 голосов
/ 21 ноября 2012

Если вы используете Rails, вы также можете использовать String # параметризация. Это не особо предназначено для этого, но вы получите удовлетворительный результат.

"my§document$is°°   very&interesting___thisIs%nice445.doc.pdf".parameterize
2 голосов
/ 30 января 2019

В Rails вы также можете использовать sanitize из ActiveStorage :: Filename :

ActiveStorage::Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg"
ActiveStorage::Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg"
0 голосов
/ 20 февраля 2018

Существует библиотека, которая может быть полезна, особенно если вы заинтересованы в замене странных символов Юникода на ASCII: unidecode .

irb(main):001:0> require 'unidecoder'
=> true
irb(main):004:0> "Grzegżółka".to_ascii
=> "Grzegzolka"
0 голосов
/ 15 ноября 2017

Для Rails я обнаружил, что хочу сохранить любые расширения файлов, но использую parameterize для оставшихся символов:

filename = "my§doc$is°° very&itng___thsIs%nie445.doc.pdf"
cleaned = filename.split(".").map(&:parameterize).join(".")

Детали реализации и идеи см. В источнике: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/transliterate.rb

def parameterize(string, separator: "-", preserve_case: false)
  # Turn unwanted chars into the separator.
  parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
  #... some more stuff
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...