原帖:https://tokyo.zxproxy.com/browse.php?u=uG7kXsFlW1ZmaxKEvCzu8HrCJ0bXIAddA1s5dtIUZ%2FYzM1u9JI7jjKLTXvXJlIqeavUo1Ak%3D&b=6

如果要在 C++ 裡對特定的檔案做存取,其實透過 STL 的 fstream(參考)來做,一般是不會有什麼問題的;相對的,問題比較大的部分,可能會是在於對於資料夾(folder、directory)的處理,以及對於路徑的操作上。像是以路徑來說,Windows 用的「\」、而且有磁碟代號(X:),而 Linux 則是用「/」、也沒有磁碟代號的概念;如果再加上一些其他檔案系統設計上的不同,所以其實要寫一個跨平台的檔案操作程式,其實有滿多繁瑣的事情要做的。而在路徑的部分,就 Heresy 的認知,STL 裡面好像還沒有類似 dir 或 ls 功能的函式、可以用來列出目錄下的檔案?

Boost C++ Libraries 裡的 FileSystem 這個函式庫(官方網頁,以下簡稱 Filesystem),就是為了讓程式開發者可以快速、簡單地對系統的檔案、資料夾、路徑進行操作,而發展出來的函式庫;他不但和 C++ 的標準函式庫可以非常好地相融在一起,更可以讓程式開發者寫的程式能在不同的作業系統下運作。而同時,FileSystem 也已經被 C++ Standards Committee 接受決定當作 TR2 的一部分了~

雖然目前 Boost 的 1.44 還是以第二版的 FileSystem 為預設使用的版本,但是實際上 FileSystem 已經有第三版(網頁)了~而在下一版的 Boost(1.45),也會改以 FileSystem v3 來當作預設的版本,所以  Heresy 這邊的介紹也就以 FileSystem v3 為主了。

使用 FileSystem v3

由於 FileSystem 有牽涉到系統的操作,所以他也是 Boost C++ Libraries 裡,少數需要先編譯、建置的函式庫之一,而建置的方法,就請參考之前的《Boost C++ Libraries 簡介》,這邊就不多提了。

而前面也有提到,目前的 Boost 1.44 預設還是使用 FileSystem v2,所以如果要使用 FileSystem v3 的話,除了要 include FileSystem 的主要 header 檔:boost/filesystem.hpp 外,還要再 include 他之前,加上一行:

#define BOOST_FILESYSTEM_VERSION 3

來指定要使用的 FileSystem 版本。理論上等到 Boost 1.45 後,應該就不必加這個了。

而由於 FileSystem 這個函式庫是需要是先編譯的,所以在建置有使用到 FileSystem 的程式時,也需要 link FileSystem 的 library 檔。在 VC 的環境下的話,理論上 Boost 會自動去 link,使用者只要設定好路徑就可以了;gcc 的話,則是要加上「-l boost_filesystem」來做 library 的 linking。

在操作上,FileSystem 主要是定義了一個 path 的類別,用來處理路徑的操作;像是透過 operator / 可以輕鬆地來加上子目路的路徑、透過 pareant_path() 則可以簡單地取得上一層路徑。同時,Path 也可以快速地和 std::string 做轉換,所以在使用上相當地方便~

下面就是一些簡單的 Path 操作範例:

#define BOOST_FILESYSTEM_VERSION 3

#include <stdlib.h>
#include <string>
#include <iostream> #include <boost/filesystem.hpp>
namespace BFS = boost::filesystem; int main( )
{
std::cout << "Windows style:\n";
BFS::path p1( "c:\\windows" );
BFS::path p2 = p1 / "system32";
std::cout << "p1=" << p1 << ", p2=" << p2 << "\n" << std::endl; std::cout << "Linux style:\n";
p1 = "/usr/local";
p2 = p1.parent_path();
std::cout << "p1=" << p1 << ", p2=" << p2 << "\n" << std::endl;
}

執行的結果應該會向下面這樣:

Windows style:
p1="c:\windows", p2="c:\windows\system32" Linux style:
p1="/usr/local", p2="/usr"

在這個範例裡,可以發現 FileSystem 的 Path 在操作上相當簡單,除了可以直接透過 std::string 來建立 Path 的物件外,Boost 也提供了轉換的函式,在大部分情況下都可以把 std::string 自動轉換成 Path 的型別。而如果要進到子資料夾,只要透過 operator / 就可以了(上方黃底的部分)~在操作上相當地直覺。

而如果想把 Path 轉換為 std::string 的話,也只要呼叫他提供的 Path::string() 這個函式(或者也有提供 wstring() 的版本)就可以了;下面就是一個簡單的例子:

std::string str = p2.string();

Path 相關函式細節

當然,FileSystem 提供的 Path 這個類別,還有許多成員函式可以使用;在 FileSystem 官方的 tutorial 文件裡,就有列出一些他的函式,reference 裡則有完整的列表。

第一部分是路徑分解的函示(path decomposition、參考),主要目的就是用來分解路徑的各項目。下面 Windows 是以「C:\Windows\System32\xcopy.exe」、Linux 則是以「/Build/heresy/a.out」來當作範例的資料:

函式
說明
Windows 範例
Linux 範例
root_name()
根目錄名稱 C:  
root_directory()
根目錄資料夾 \ /
root_path()
根目錄路徑 C:\ /
relative_path()
相對路徑 Windows\System32\xcopy.exe Build/heresy/a.out
parent_path()
上一層目錄路徑 C:\Windows\System32 /Build/heresy
filename()
檔案名稱 xcopy.exe a.out
stem()
不包含副檔名的檔名 xcopy a
extension()
副檔名 .exe .out

基本上,對於要解析一個路徑來說,這邊提供的函式應該算是相當充分了~

而除了路徑的分解外,他也還有提供不少判斷用的函式(官方稱為 path query,參考),例如:empty()、has_filename()、has_extension()、is_absolution() 等等;這些都是簡單地回傳一 true 或 false,可以幫助快速地進行路徑的條件判斷,在這邊就不一一解釋了。

而除了 path 本身的函式之外,在 boost::filesystem 這個 namespace 下,也還提供了相當多的函式,可以用來取得檔案的相關資料、或是對檔案、目錄進行操作;Boost 把這些函式稱為「Operational functions」,下面 Heresy 列了一些自己覺得比較重要的:

確認檔案性質、相關資料的函式:
函式
說明
bool exists( const path& )
判斷所指定的路徑是否存在
bool is_directory( const path& )
判斷指定的路徑是否是目錄
bool is_regular_file( const path& )
判斷指定的路徑是否是一般檔案
bool is_symlink( const path& )
判斷指定的路徑是否是 symbolic link
bool is_other( const path& )
判斷指定的路徑是否不是路徑、一般檔案或 symbolic link
bool is_empty( const path& )
判斷指定路徑是否是空目錄、或是大小為零的檔案
uintmax_t file_size( const path& )
取得指定路徑檔案的大小,只對 regular file 有用
註:這些函式實際上都是用 file_status 來進行操作的,傳入 path 當參數的只是 inline 函式
檔案、目錄操作:
函式
說明
void copy_file( const path& from, const path& to)
複製檔案。 也可以再加上額外的 copy_option,來指定檔案已存在時的處理方法(fail_if_exists、overwrite_if_exists)。
void rename( const path& old, const path& new )
修改檔案、目錄的名稱。
bool create_directories( const path& )

bool create_directory( const path& )

建立新目錄。 create_directory() 在上層目錄不存在的情況下,會建立失敗;而 create_directories() 則會一層一層地建立下來。
bool remove( const path& )

uintmax_t remove_all(const path& p)

刪除檔案或目錄。 remove() 只會刪除單一檔案和目錄,如果目錄內還有東西,會刪除失敗;remove_all() 則是會把目錄內的東西也一起刪除掉。
其他:
函式
說明
path current_path()

void current_path( const path& )

取得、設定目前的工作路徑
bool equivalent(const path& , const path& )
確認兩個路徑是否相同
space_info space( const path& )
取得路徑的容量、可用空間資訊
path unique_path();
產生一個獨一無二的路徑名稱。形式可以指定,預設的形式會式「%%%%-%%%%-%%%%-%%%%」,每一個「%」都會被填入隨機產生的值,適合用在產生暫存性的路徑名稱。

基本上,有了這些函式,應該就足以應付絕大部份檔案、路徑操作上的需求了!當然,Heresy 這邊沒有全列,而實際上 boost 的 FileSystem 對於某些檔案系統的特殊功能,也沒有完全支援;而如果要用到 FileSystem 沒有支援的功能,可能就得用系統提供的功能了。

目錄的處理

這部分的「目錄的處理」,指的主要是列出一個目錄下所有的檔案、目錄的部分;FileSystem 在這部分,主要是透過「directory_iterator」這個類別(參考)來做的。透過 path 和 directory_iterator,程式開發者可以輕鬆地透過和 STL iterator 相同的操作方法,來對一個目錄下的項目作處理,算是相當方便的~

下面是一個簡單的例子,他會列出 C:\windows 下所有的檔案與資料夾:

namespace BFS = boost::filesystem;
BFS::path p1( "c:\\windows" );
std::cout << "Files in " << p1 << std::endl;
for( BFS::directory_iterator it = BFS::directory_iterator(p1);
it != BFS::directory_iterator(); ++ it )
{
BFS::path px = it->path();
std::cout << px.filename() << "\n";
}

在這個例子裡,基本上就是透過一個 for 迴圈、以及透過 p1 建立出來的 directory_iterator it,來掃整個 p1 目錄下的項目。其中,directory_iterator( p1 ) 就是建立一個指向 p1 目錄下第一個項目的 iterator、directory_iterator() 則是建立一個通用的、代表結尾的 end iterator,用來做中斷條件的判斷參考。而在使用上,由於 it 的型別實際上是 directory_iterator,所以還是需要轉型成 path,才方便做一般性的操作;或者,也可以透過 status() 這個函式,來取得檔案狀態進行操作。

而前面也提過,由於他是採用 iterator 的概念來做的,所以可以和 STL 做很好的整合;這邊就用官方 tut4.cpp 的例子(網頁),稍微修改一下來做示範了~

#define BOOST_FILESYSTEM_VERSION 3

#include <stdlib.h>
#include <vector>
#include <iostream> #include <boost/filesystem.hpp> using namespace std;
using namespace boost::filesystem; int main( int argc, char* argv[] )
{
path p( argv[1] );
if( exists( p ) && is_directory( p ) )
{
cout << p << " is a directory containing:\n";
vector<path> v;
copy( directory_iterator( p ), directory_iterator(), back_inserter( v ) );
sort( v.begin(), v.end() );
for( vector<path>::const_iterator it( v.begin() ); it != v.end(); ++it )
cout << "\t" << *it << '\n';
}
return 0;
}

在這個範例裡,主要就是多了先將目錄的 path 都先複製到 vector<path> v 裡面,進行 sort 的動作;如此一來,就可以確保輸出的結果是排序過的了~而目前這樣寫的排序順序,就是直接用 path 預設的大小比較方法來做,如果要自己控制排序順序的話,STL 的 sort() 也可以簡單地透過自訂 comparsion 來做到;比如說如果搭配C++0x 的 lambda expression 的話,可以簡單寫成:

sort( v.begin(), v.end(),
[](const path& p1, const path& p2 ){return p1.extension()<p2.extension();} );

如此就可以根據副檔名來做排序了~不過這個寫法非常簡略,所以其實還是有不少改善的空間的。 ^^"

範例

最後,則是來貼一個 Heresy 自己寫的程式當作範例。這個程式會去遞迴地去掃描所給的資料夾下的所有的資料夾以及檔案,並且根據檔案大小來做排序(非 regular file 會放到最後);而輸出的部分,如果是檔案的話,他除了印出檔名外,也會印出檔案的大小。

#define BOOST_FILESYSTEM_VERSION 3

#include <stdlib.h>
#include <vector>
#include <string>
#include <iostream> #include <boost/filesystem.hpp> using namespace std;
using namespace boost::filesystem; bool CompareBySize( const path& rP1, const path& rP2 )
{
if( !is_regular_file( rP1 ) && !is_regular_file( rP2 ) )
return false;
else if( !is_regular_file( rP1 ) )
return false;
else if( !is_regular_file( rP2 ) )
return true; return file_size( rP1 ) < file_size( rP2 );
} void outputFileInfo( const path& rPath )
{
cout << "   File: " << rPath.filename();
if( is_regular_file( rPath ) )
cout << " (size:" <<  file_size( rPath ) / 1024 << "kb)";
cout << endl;
} void ScanDirectory( const path& rPath )
{
cout << " Directory: " << rPath << endl;
vector<path> vList;
copy( directory_iterator(rPath), directory_iterator(), back_inserter( vList ) );
sort( vList.begin(), vList.end(), CompareBySize );
for( vector<path>::const_iterator it = vList.begin(); it != vList.end(); ++ it )
{
if( is_directory( *it ) )
ScanDirectory( *it );
else
outputFileInfo( *it );
}
} int main( int argc, char* argv[] )
{
path rootPath( argv[1] );
if( exists( rootPath ) )
{
if( is_directory( rootPath ) )
ScanDirectory( rootPath );
else
outputFileInfo( rootPath );
}
}

基本上,這也就只是個簡單的範例程式了~實用價值不高,而且應該也還有許多改善的空間。 ^^"

C++ 檔案、資料夾、路徑處理函式庫:boost::filesystem的更多相关文章

  1. du 查看 資料夾 佔用空間

    查看 目前目錄使用的空間大小 du -h --max-depth=0 -h, --human-readable 查看 目前及下一屠的目錄 使用的空間大小 du -h --max-depth=1

  2. [ Docker ] 映射資料夾

    - docker run -v <host path>:<container path> - 例如:docker run -v /home/adrian/data:/data ...

  3. 路徑 z

    最近因為寫到使用FileDialog開檔讀檔的關係,所以在打開時,會常常需要移動到資料夾所在路徑,因此就在想要如何才能指定開啟FileDialog 能夠就指定在想要的資料夾上,並且移動整個專案時,不會 ...

  4. 在 Server 端存取 Excel 檔案的利器:NPOI Library

    转处 http://msdn.microsoft.com/zh-tw/ee818993.aspx Codeplex 軟體套件(Package)資訊 套件名稱 NPOI 作者 tonyqus, huse ...

  5. C# 選擇本機檔案並上傳

    參考自:http://www.dotblogs.com.tw/puma/archive/2008/11/07/5910.aspxhttp://www.codeproject.com/Articles/ ...

  6. .net批量上傳Csv檔資料應用程序開發總結

    應用環境:visual studio 2010開發工具,Database為Sql2008以上版本 最近在生產環境中需要開發一款應用程式,上傳電子檔(.csv)資料至Database 最初方案: 以tx ...

  7. [心得] 如何利用liquibase進行資料庫版本控制 - 實際練習

    透過上一篇的基本觀念介紹,希望大家應該有一點點感覺了! 這篇我們就來做個簡單的版本演練,加深印象吧! 我使用的環境如下 System : Windows 7 Database : SQL Server ...

  8. Delphi APP 開發入門(八)SQLite資料庫

    Delphi APP 開發入門(八)SQLite資料庫 分享: Share on facebookShare on twitterShare on google_plusone_share   閲讀次 ...

  9. 如何在 Visual Studio 2012 控制 TFS 版控時要忽略哪些檔案

    幾乎在任何一種版本控管的機制裡,都會遇到那些「不應該簽入到版本庫」的潛規則,以往我們在用 SVN 的時候,我就寫過幾篇文章要大家注意這點.最近都改用 TFS 做版控,因為大多使用 Visual Stu ...

随机推荐

  1. Oracle存储过程Procedure语法及案例

    create or replace procedure replace(desstr in varchar2, replacestr in varchar2, tablename in varchar ...

  2. ubuntu中彻底删除nginx

    1.先执行一下命令: 1.1 删除nginx,–purge包括配置文件 sudo apt-get --purge remove nginx 1.2 自动移除全部不使用的软件包 sudo apt-get ...

  3. 4.7 《硬啃设计模式》 第24章 麻烦的多角关系 - 中介者模式(Mediator Pattern)简介

    在Windows程序中,有时候界面控件之间的交互会很麻烦,如:A控件显示什么的时候,B控件要显示什么,另外C控件要不可用,同样其它控件也会有类似的复杂要求.控件与控件之间很容易形成复杂的多角关系了.现 ...

  4. js中设置setInterval的注意点

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  5. Web开发在线工具

    JSON: JSON格式化工具 JSON检验并格式化工具 专为Web开发者准备的 63个免费在线工具

  6. awk参数详解

    wk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息 awk处理过程: 依次对每一行进行处理,然后输出 awk命令形式: awk [-F| ...

  7. Python学习之路——模块

    一.模块: 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需 ...

  8. Python之路:Python 基础(三)-文件操作

    操作文件时,一般需要经历如下步骤: 打开文件 操作文件 一.打开文件 文件句柄 = file('文件路径', '模式') # 还有一种方法open 例1.创建文件  f = file('myfile. ...

  9. 面向对象程序设计-C++ Finial exam review NOTES【第十六次上课笔记】

    写在前面: 我记得也不全,如果有记录的更全的同学可以留言,我会添加哒 :) 常量 内敛函数 为什么需要内敛函数 内敛函数适用于什么场合 内敛函数本身,最大优点是,避免了真正函数调用的开销 因为普通函数 ...

  10. Android外部存储 - 官方文档解读

    预备知识:External Storage Technical Information 摘要: "The WRITE_EXTERNAL_STORAGE permission must onl ...