BOOST 之filesystem, path

目錄[-]ios

C++ 語言(其實是 C++ 標準)的最多見問題之一是,缺少定義良好的庫來幫助處理文件系統查詢和操做。因爲這個緣由,程序員不得不使用本機操做系統提供的應用程序編程接口(Application Program Interfaces,API),而這使得代碼不能在平臺之間移植。如下面的簡單狀況爲例:您須要肯定某個文件是不是 Directory 類型。在 Microsoft® Windows® 平臺中,能夠經過調用 GetAttributes 庫函數(在 windows.h 頭文件中定義)進行此操做:程序員

DWORD GetFileAttributes (LPCTSTR lpFileName);

對於目錄,所獲得的結果應該爲 FILE_ATTRIBUTE_DIRECTORY,而您的代碼必須檢查是否爲此結果。在 UNIX® 和 Linux® 平臺上,能夠經過使用 stat 或 fstat 函數及 sys/stat.h 中定義的 S_ISDIR 宏來實現相同的功能。您還必須理解 stat 結構。下面是對應的代碼:編程

#include <sys/stat.h>
#include <stdio.h>
int main()
  {
  struct stat s1;
  int status = stat(<const char* denoting pathname>, &s1);
  printf(「Path is a directory : %d\n」, S_ISDIR(s1.st_mode));
  return 0;
  }

對於 I/O 操做較多的程序,這樣的不一致就意味着須要進行大量的工程工做才能在平臺間移植代碼。正是由於這個緣由,咱們才引入了 Boost Filesystem Library。這個普遍使用的庫提供了安全、可移植且易用的 C++ 接口,用於執行文件系統操做。能夠從 Boost 站點免費下載此庫。windows

使用 boost::filesystem 的第一個程序

在深刻研究 Boost Filesystem Library 的更多細節以前,請看一下清單 1 中所示的代碼;此代碼使用 Boost API 肯定某個文件的類型是否爲 Directory。安全

清單 1. 用於肯定某個文件的類型是否爲 Directory 的代碼
#include <stdio.h>
#include 「boost/filesystem.hpp」
int main()
  {
  boost::filesystem::path path("/usr/local/include"); // random pathname
  bool result = boost::filesystem::is_directory(path);  
  printf(「Path is a directory : %d\n」, result);
  return 0;
  }

此代碼很是明瞭易懂,您並不須要瞭解任何系統特定的例程。此代碼通過驗證,能在不用修改的狀況下在 gcc-3.4.4 和 cl-13.10.3077 上成功編譯。dom

回頁首函數

瞭解 Boost path 對象

瞭解 Boost Filesystem Library 的關鍵是 path 對象,由於 Filesystem Library 中定義的多個例程都要對相應的 path 對象操做。文件系統路徑一般依賴於操做系統。例如,衆所周知,UNIX 和 Linux 系統使用正斜槓 ( /) 字符做爲目錄分隔符,而 Windows 將反斜槓 (\) 字符用於相似的用途。boost::filesystem::path 旨在準確地抽象此特性。path 對象能夠經過多種方式進行初始化,最多見的方式是使用 char* 或std::string 進行初始化,如清單 2 中所示。工具

清單 2. 建立 Boost path 對象的方法
path(); // empty path 
path(const char* pathname); 
path(const std::string& pathname);
path(const char* pathname, boost::filesystem::path::name_check checker); 
path(const char* pathname, boost::filesystem::path::name_check checker);

在初始化 path 對象時,能夠採用本機格式或可移植操做系統接口(Portable Operating System Interface,POSIX)委員會定義的可移植格式提供 PATHNAME 變量。這兩種方法在實際中各有優缺點。考慮如下狀況:您但願操做軟件所建立的目錄,此目錄在 UNIX 和 Linux 系統上位於 /tmp/mywork,而在 Windows 上位於 C:\tmp\mywork。能夠採用多種方法處理問題。清單 3 顯示了面向本機格式的方法。測試

清單 3. 使用本機格式初始化 path
#ifdef UNIX
boost::filesystem::path path("/tmp/mywork");
#else
boost::filesystem::path path("C:\\tmp\\mywork ");
#endif

須要單個 #ifdef 來按操做系統初始化 path 對象。不過,若是您喜歡使用可移植格式,請參見清單 4ui

清單 4. 使用可移植格式初始化 path
boost::filesystem::path path("/tmp/mywork");

請注意,path::name_check 指的是一個名稱檢查函數原型。若是其參數輸入 PATHNAME 對於特定的操做系統或文件系統有效,名稱檢查函數將返回「True」。Boost Filesystem Library 提供了多個名稱檢查函數,並且也歡迎您提供本身的變體。經常使用的名稱檢查函數是 Boost 提供的portable_posix_name 和 windows_name

回頁首

path 成員函數概述

path 對象提供了多個成員方法。這些成員例程並不會修改文件系統,但會根據 path 名稱提供有用的信息。此部分提供了其中幾個例程的概述:

  • const std::string& string( )此例程會返回用於初始化 path 的字符串的副本,其格式符合 path 語法規則。

  • std::string root_directory( )在提供了路徑的狀況下,此 API 將返回根目錄,不然將返回空字符串。例如,若是路徑包含/tmp/var1,則此例程將返回 /,即 UNIX 文件系統的根。不過,若是路徑是相對路徑,如 ../mywork/bin,此例程將返回空字符串。

  • std::string root_name( )在給定從文件系統根目錄開始的路徑的狀況下,此例程將返回包含 PATHNAME 的第一個字符的字符串。

  • std::string leaf( )在給定絕對路徑名稱(例如,/home/user1/file2)的狀況下,此例程將提供與文件名稱對應的字符串(即 file2)。

  • std::string branch_path( )這是與 leaf 互補的例程。在給定路徑的狀況下,將會返回其構造所用的全部元素(除了最後一個元素)。例如,對於使用 /a/b/c 初始化的 path,path.branch_path( ) 將返回 /a/b。對於包含單個元素的路徑,如 c,此例程將返回空字符串。

  • bool empty( )若是 path 對象包含空字符串(例如 path path1("")),則此例程將返回 True。

  • boost::filesystem::path::iterator此例程用於遍歷 path 的各個元素。請看清單 5 所示的代碼。

    清單 5. 使用 path::iterator(begin 和 end 接口)
    #include <iostream>
    #include 「boost/filesystem.hpp」
    int main()
      {
      boost::filesystem::path path1("/usr/local/include"); // random pathname
      boost::filesystem::path::iterator pathI = path1.begin();
      while (pathI != path1.end())
        {
        std::cout << *pathI << std::endl;
        ++pathI;
        }
      return 0;
      }
     
    // result: 1

    上述程序的輸出依次是 /usrlocalinclude,表明了該目錄的層次結構。

  • path operator / (char* lhs, const path& rhs)此例程是 path 的非成員函數。它將返回使用 lhs 和 rhs 造成的路徑的串聯值。它將自動插入 / 做爲路徑分隔符,如清單 6 中所示。

    清單 6. 路徑字符串的串聯
    #include <iostream>
    #include 「boost/filesystem.hpp」
    int main()
      {
      boost::filesystem::path path1("/usr/local/include"); // random pathname
      boost::filesystem::path::iterator pathI = path1.begin();
      while (pathI != path1.end())
        {
        std::cout << *pathI << std::endl;
        ++pathI;
        }
      return 0;
      }
     
    // result: 1

回頁首

錯誤處理

文件系統操做常常遇到意外的問題,Boost Filesystem Library 將使用 C++ 異常報告運行時錯誤。boost::filesystem_error 類派生自std::runtime_error 類。庫中的函數使用 filesystem_error 異常報告操做錯誤。與不一樣的可能錯誤類型對應,Boost 頭文件定義了相應的錯誤代碼。用戶代碼一般駐留在 try...catch 塊內,使用 filesystem_error 異常來報告相關錯誤消息。清單 7 提供了重命名文件的小示例,在 from 路徑中的文件不存在時引起異常。

清單 7. Boost 中的錯誤處理
#include <iostream>
#include 「boost/filesystem.hpp」
int main()
  {
  try {
  boost::filesystem::path path("C:\\src\\hdbase\\j1"); 
  boost::filesystem::path path2("C:\\src\\hdbase\\j2"); 
  boost::filesystem::rename(path, path2);
  }
  catch(boost::filesystem::filesystem_error e) { 
  // do the needful 
  }    
  return 0;
  }

回頁首

Boost Filesystem Library 中的函數類別

boost::filesystem 提供了不一樣類別的函數:有些函數(如 is_directory)用於查詢文件系統,而其餘函數(如 create_directory)則主動對文件系統進行修改。根據各自功能的不一樣,這些函數能夠大略納入如下類別:

  • 屬性函數:提供雜項信息,如文件大小、磁盤使用量等。

  • 文件系統操做函數:用於建立常規文件、目錄和符號連接;複製和重命名文件;提供刪除功能。

  • 實用工具:測試文件的擴展名等。

  • 雜項常規函數:以編程方式更改文件擴展名等。

屬性函數

Boost Filesystem Library 包括如下屬性函數:

  • uintmax_t file_size(const path&)返回常規文件的大小(以字節爲單位)

  • boost::filesystem::space_info space(const path&)接受路徑做爲輸入,並返回定義以下的 space_info 結構:

    struct space_info { 
      uintmax_t capacity;
      uintmax_t free;
      uintmax_t available;
    };

    根據文件系統所屬的磁盤分區,此流程將對該分區的全部目錄返回相同的磁盤使用量統計數據(以字節爲單位)。例如,對於 C:\src\dir1 和 C:\src\dir2,都會返回相同的磁盤使用數據。

  • std::time_t last_write_time(const path&)返回文件的最後修改時間。

  • void last_write_time(const path&, std::time_t new_time)修改文件的最後修改時間。

  • const path& current_path( )返回程序的當前工做目錄的完整路徑(注意,此路徑與最初運行程序的路徑可能不一樣,由於可能採用編程方式更改目錄)。

文件系統操做函數

這組函數負責進行新文件和目錄建立、文件刪除等操做:

  • bool create_directory(const path&)此函數使用給定的路徑名稱建立目錄。(請注意,若是 PATHNAME 自己包含無效字符,則結果常常是由平臺定義的。例如,在 UNIX 和 Windows 系統中,星號 (*)、問號 (?) 及其餘此類字符視爲無效,不能出如今目錄名稱中。)

  • bool create_directories(const path&)與建立單個目錄相對,您可使用此 API 建立目錄樹。例如,以目錄樹 /a/b/c 爲例,必須在 /tmp 文件夾內建立此目錄樹。可調用此 API 完成任務,但使用相同的參數調用 create_directory 時將引起異常。

  • bool create_hard_link (const path& frompath, const path& topath)此函數在 frompath 和 topath 間建立硬連接。

  • bool create_symlink(const path& frompath, const path& topath)此函數在 frompath 和 topath 間建立符號(軟)連接。

  • void copy_file(const path& frompath, const path& topath)將 frompath 引用的文件的內容和屬性複製到 topath 引用的文件中。例程expects a destination file to be absent;若是存在目標文件,則會引起異常。所以,此函數與 UNIX 中系統指定的 cp 命令並不等效。另外,此函數還預期 frompath 變量將引用正確的常規文件。請看如下示例:frompath 引用符號連接 /tmp/file1,然後者反過來引用文件 /tmp/file2;而 topath 能夠爲 /tmp/file3。在這種狀況下,copy_file 將失敗。這是此 API 與 cp 命令相比的另外一個差異。

  • void rename(const path& frompath, const path& topath)此函數是用於重命名文件的 API。能夠經過在 topath 參數中指定完整路徑名來同時重命名和更改文件的位置,如清單 8 中所示。

    清單 8. Boost 中的重命名功能
    #include <stdio.h>
    #include 「boost/filesystem.hpp」
    int main()
      {
      boost::filesystem::path path("/home/user1/abc"); 
      boost::filesystem::rename(path, "/tmp/def");  
      return 0;
      }
     
    // abc is renamed def and moved to /tmp folder
  • bool remove(const path& p)此例程將嘗試刪除路徑 p 所引用的文件或目錄。對於目錄的狀況,若是目錄的內容不爲空,則此例程將引起異常。警告:此例程並不考慮所刪除的內容,即便其餘程序在訪問同一文件也如此!

  • unsigned long remove_all(const path& p)此 API 嘗試刪除路徑 p 所引用的文件或目錄。與 remove 不一樣,此函數並不會特殊考慮不爲空的目錄。此函數是 UNIX rm –rf 命令的 Boost 對等項。

實用工具

Boost Filesystem Library 包含如下實用工具:

  • bool exists(const path&)此函數檢查文件的擴展名。文件能夠爲任何類型:常規文件、目錄、符號連接等等。

  • bool is_directory(const path&)此函數檢查路徑是否與目錄對應。

  • bool is_regular(const path&)此函數檢查普通文件(即此文件不是目錄、符號連接、套接字或設備文件)。

  • bool is_other(const path&)一般,此函數檢查設備文件(如 /dev/tty0)或套接字文件。

  • bool is_empty(const path&)若是路徑與文件夾對應,此函數將檢查文件夾是否爲空,並據此返回「True」或「False」。若是路徑與文件對應,此函數將檢查文件的大小是否等於 0。對於文件的硬連接或符號連接的狀況,此 API 將檢查原始文件是否爲空。

  • bool equivalent(const path1& p1, const path2& p2)此 API 很是實用,可用於比較相對路徑和絕對路徑名。請看清單 9

    清單 9. 測試兩個路徑是否等效
    #include <stdio.h>
    #include 「boost/filesystem.hpp」
    int main()
      {
      boost::filesystem::path path1("/usr/local/include"); // random pathname
      boost::filesystem::path path2("/tmp/../usr/local/include");
      bool result = boost::filesystem::is_equivalent(path1, path2);  
      printf(「Paths are equivalent : %d\n」, result);
      return 0;
      }
     
    // result: 1
  • path system_complete(const path&)此函數是與 bool equivalent(const path1& p1, const path2& p2) 同一系列的另外一個 API。在給定當前工做目錄中任意文件路徑的狀況下,此 API 將返回該文件的絕對路徑。例如,若是用戶位於目錄 /home/user1 並查詢文件 ../user2/file2,此函數將返回 /home/user2/file2,即文件 file2 的完整路徑名。

雜項函數

Boost Filesystem Library 包括如下雜項函數:

  • std::string extension(const path&)此函數之前面帶句點 (.) 的形式返回給定文件名的擴展名。例如,對於文件名爲 test.cpp 的文件,extension 將返回 .cpp。對於文件沒有擴展名的狀況,此函數將返回空字符串。對於隱藏文件(即 UNIX 系統中文件名以 . 開始的文件),此函數將相應地計算擴展名類型或返回空字符串(所以,對於 .test.profile,此例程將返回 .profile)。

  • std::string basename(const path&)這是與 extension 互補的例程。它將返回文件名中 . 以前的字符串。請注意,即便提供了絕對文件名,此 API 仍然僅會返回屬於文件名的直接部分,如清單 10 中所示。

    清單 10. 使用 boost::basename
    #include <stdio.h>
    #include <cstring>
    #include 「boost/filesystem.hpp」
    use namespace std;
    int main()
      {
      boost::filesystem::path path1("/tmp/dir1/test1.c "); 
      boost::filesystem::path path2("/tmp/dir1/.test1.profile");
      string result1 = boost::filesystem::basename (path1);  
      string result2 = boost::filesystem::basename (path2);
      printf(「Basename 1: %s  Basename2 : %s\n」, result1.c_str(), result2.c_str());
      return 0;
      }
     // result: Basename1: test1 Basename2: .test1
  • std::string change_extension(const path& oldpath, const std::string new_extension)此 API 將返回反映更改後的名稱的新字符串。請注意,與 oldpath 對應的文件保持不變。這只是一個常規函數。另請注意,您必須顯式地在擴展名中指定。例如,change_extension("test.c", "so") 會獲得 testso,而不是 test.so

回頁首

結束語

本文提供了 Boost Filesystem Library 的簡單概述。不該將本文視爲 Boost 中的整個文件系統接口的綜合文檔。並未討論此 API 集的內部狀況,也沒有討論這些 API 在非 UNIX 或 Windows 平臺(如 VMS)中的細節。有關文件系統的更多信息,請參見參考資料

相關文章
相關標籤/搜索