2017-07-25 180 views
2

我想從給定的String路徑中提取文件的擴展名。從Rust中給定路徑中提取文件擴展名慣用

下面這段代碼工作,但我不知道是否有一個更清潔,更地道的防鏽方法來實現這一目標:

use std::path::Path; 

fn main() { 

    fn get_extension_from_filename(filename: String) -> String { 

     //Change it to a canonical file path. 
     let path = Path::new(&filename).canonicalize().expect(
      "Expecting an existing filename", 
     ); 

     let filepath = path.to_str(); 
     let name = filepath.unwrap().split('/'); 
     let names: Vec<&str> = name.collect(); 
     let extension = names.last().expect("File extension can not be read."); 
     let extens: Vec<&str> = extension.split(".").collect(); 

     extens[1..(extens.len())].join(".").to_string() 
    } 

    assert_eq!(get_extension_from_filename("abc.tar.gz".to_string()) ,"tar.gz"); 
    assert_eq!(get_extension_from_filename("abc..gz".to_string()) ,".gz"); 
    assert_eq!(get_extension_from_filename("abc.gz".to_string()) , "gz"); 

} 
+0

所以你想要得到最左邊的點後的一切?這會給錯誤的結果「版本1.2.txt」 – interjay

+0

@interjay,是的,因此我維護允許擴展的散列表,因此'2.txt'會驚慌。我的意圖是以通用的方式提取可能的擴展,並與允許的擴展hashmap進行比較。 – Sokio

+2

'.tar.gz'不是一個獨立的擴展名,它是一個'.gz'文件,當解壓縮時會得到一個'.tar'文件。你應該遵循相同的過程。提取擴展部分和非擴展部分,並遞歸處理非擴展部分拉伸擴展。 – loganfsmyth

回答

4

在慣用的鏽可以失敗應該是一個OptionResult函數的返回類型。一般來說,函數還應該接受切片而不是String,並且只在必要時創建新的String。這減少了過多的複製和堆分配。

您可以使用所提供的extension()方法,然後轉換所產生的OsStr&str

use std::path::Path; 
use std::ffi::OsStr; 

fn get_extension_from_filename(filename: &str) -> Option<&str> { 
    Path::new(filename) 
     .extension() 
     .and_then(OsStr::to_str) 
} 

assert_eq!(get_extension_from_filename("abc.gz"), Some("gz")); 

使用and_then是方便在這裏,因爲這意味着你沒有解開的extension()和處理返回的Option<&OsStr>在致電to_str之前可能有None。我也可以使用lambda |s| s.to_str()而不是OsStr::to_str - 這可能是一個偏好或意見問題,哪個更加習慣。

請注意,參數&str和返回值都是對爲斷言創建的原始字符串片段的引用。返回的片不能超過它所引用的原始片,因此如果需要更長的時間,您可能需要從此結果創建擁有的String

1

什麼比用更地道鏽病的builtin method呢?

Path::new(&filename).extension() 
+0

感謝您的快速響應,但我認爲這個例子會失敗: 'assert_eq!(get_extension_from_filename(「abc.tar.gz」.to_string()),「tar.gz」);' – Sokio

+0

@Sokio哦,我錯過了那。 – Alexander

相關問題