2009-12-21 172 views
37

我有用户条目作为文件名。当然这不是一个好主意,所以我想放弃除了[a-z],[A-Z],[0-9],_-之外的所有东西。如何使一个Ruby字符串安全的文件系统?

例如:

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 

是否有这样做的一个很好的和优雅的方式?

+1

这是一个很好的问题。我希望它有一个stdlib回答 –

回答

24

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 
+0

感谢您的链接!顺便说一句,在你链接的文章中,海报说这个功能有问题。 – marcgg

+1

thx,correct .. – miku

+3

'name.gsub!(/ [^ 0-9A-Za-z。\ - ] /,'_')'是我5年后唯一使用的部分:D – Aleks

53

我想建议,从旧的不同的解决方案。请注意,旧版本使用已弃用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任何序列的开始 - Za - z0 - 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" 

我认为这是你的要求是什么。我希望这是很好,很优雅。

+0

谢谢!这有所帮助。 :) – Surya

+0

当我尝试使用代码时,获取“未定义(?...)序列...”。任何Ruby版本的限制? –

+0

@JP。对不起,迟到的回复,你现在可能已经明白了。没有经过测试,但我相信在Ruby 1.9中出现了后视图(这是问号所示)。所以是的,有限制。例如见http://stackoverflow.com/q/7605615/1117365 –

15

如果你使用Rails,你也可以使用String#parameterize。这不是特意为此而设,但您会获得满意的结果。

"my§document$is°° very&interesting___thisIs%nice445.doc.pdf".parameterize 
+1

This isn'技术上准确,因为它也将删除十进制字符,这在保留扩展中是非常重要的。幸运的是,参数化背后的代码[相对简单](http://apidock.com/rails/ActiveSupport/Inflector/parameterize),只需几个'gsub'调用即可实现。 –

0

对于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 
0

有一个图书馆,可能会有所帮助,特别是如果你有兴趣更换怪异的联合国带ASCII码的icode字符:unidecode

irb(main):001:0> require 'unidecoder' 
=> true 
irb(main):004:0> "Grzegżółka".to_ascii 
=> "Grzegzolka" 
相关问题