2009-12-21 170 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" 
相關問題