C++ 檔案、資料夾、路徑處理函式庫:boost::filesystem

原帖: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 );
  }
}

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

时间: 2024-10-02 02:44:15

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

如何利用資料庫儲存圖檔

在 SQL Server 當中有一款資料類型號作 Image , 除了可以儲存圖檔外它還可以儲存大型的二進位資料檔, 對這一個欄位大部分的人是聽過但是不知影按怎來用, 今日的文章就要來討論如何將圖檔存入去資料庫 準備工作 為了降低這篇文章的篇幅及複雜度, 咱決定借用 Upload 元件來替我們完成檔案上傳的工作, 所要使用的是 Dundas 所提供免錢的上傳元件, 請到下底的網址下載 Dundas Upload 元件並安裝 http://www.dundas.com/創造資料表 在這個例咱要用到

Linux檔案權限

因為 Linux 是多人多工的 OS, 所以, 檔案系統必須要有一套嚴密的保護措施, 以免, 因為有意或無意的人為破壞, 造成混亂及損失. . Linux 將檔案分成三種身份.四種權限: 三種身份是: u: 自己(user) g: 和自己同一組的人(group) o: 其它人 (other) 而 a 則是代表所有的人. 每種身份皆有四種可能的權限: r : 讀取權 (read) w : 寫入權 (write) x :執行權 (execute) 及: (上面這三種權限用得最多, 以下則較少用, 也

如何抓取「鼠標或用鍵盤選取中」的檔案清單?

问题描述 想做一个功能按下热键会判断目前所选的档案为哪些之后我要再做一些处理但是查不到如何抓取选取中的档案我觉得理论上应该是可以原因就是否则微软的Ctrl+C跟Ctrl+X,当按下时怎么取的到档案清单但是google查好久,都找不到相关文章如何抓抓取"选取中"的档案清单请问有相关范例,或是我应该找什么关键字吗?现在想到一个笨法方式,按下热键→触发Ctrl+X我在去读取剪贴簿就好了,但是这样会覆盖掉原先的剪贴簿,所以暂时不打算这样做 解决方案 解决方案二:要看控件的,比如TextBox可

將 ASP .NET WebForm 的 DataGrid 中的資料 匯出至 Microsoft Excel

datagrid|excel|web 將 ASP .NET WebForm 的 DataGrid 中的資料 匯出至 Microsoft Excel本文將逐步帶領您產生 ASP.NET WebForm 上的 DataGrid Web 伺服器控制項,然後將 DataGrid 內容匯出至 Microsoft Excel. 技術本文將說明兩種匯出 DataGrid 資料的技術: 使用 Excel MIME 類型 (或內容類型) 您可以使用伺服器端的程式碼將 DataGrid 連結至資料,然後在用戶端電腦

讀取excel資料的問題

问题描述 在vc6.0的專案中用odbc的方法讀取excel檔的資料時仍有下列問題未解決:1.我是利用m_pRa.GetFieldValue(_T("身分證號"),Sa);將excel檔中身分證號欄的資料讀進Sa文字變數中.2.假如excel的身分證號欄全是文字欄(第一位是英文字母或中文字)便毫無問題.若是數字欄則讀進來的會是有一位小數的數字,例如原來是123的數字資料,結果Sa="123.0".這還沒多大問題.可是若有部份是數字,另一部份是文字,那麼數字的資料卻完

SQL Server7.0 Web資料搜尋技巧

server|web|技巧 「資料庫資料」搜尋:使用SQL指令的LIKE語法.或Microsoft SQL Server 7.0的中文全文檢索(Full Text Search)功能. Microsoft Index Server 於Windows NT Option Pack當中所包括的Microsoft Index Server,提供中文全文檢索的搜尋功能,可以搜尋網站中Microsoft Word 或Microsoft Excel檔案.text.html.ASP等檔案格式內的資料. SQL

Oracle9i 資料庫管理實務講座(一)

oracle Oracle 9i 資料庫管理實務講座(一)如何安裝Oracle 9i Enterprise Edition for Linux 原文出處 : 2001年11月Linuxer雜誌作者: 何致億下載 PDF 檔 前言 Oracle 9i資料庫系統可算是近幾年來Oracle公司一項殺手級的產品,其功能與應用面之廣,連研究Oracle多年的我在第一次接觸到產品規格時也不禁嚇了一跳!突然間一大堆的技術文件與白皮書排山倒海而來,一時之間還不知道該從何著手.所以我花了幾個月的時間在各種平台上進

PHP如何透過ODBC來存取資料庫

odbc 使用的環境 先建立一個測試用的資料庫 接著建立一個ODBC連結 再建個測試用的PHP Script 咱們來測試吧 作者 感謝 使用的環境 本文件主要是在Win32的環境下作說明, 您需要的是台跑Windows 9x/NT/2000的電腦, 並裝有任何一種web server和PHP3或PHP4, 且可正確執行PHP Script. 並有一種以上的SQL資料庫軟體, 例如:Access... 本文件以MS-Access資料庫來作說明, 其它種的資料庫端, 皆可以類似的方法來作ODBC連結

visual studio-c#開發 edi (電子資料交換) ANIS X12

问题描述 c#開發 edi (電子資料交換) ANIS X12 最近公司的客戶 系統更換為EDI的方式 但是 下單的時候 是給我們EDI ANSI X12 850的標準格式 850好像是採購訂單 然後我們還需要再轉成 edi格式給客戶 我有在網路上查到 EDI Framwork 可是測試版檔案只有2K的限制 完全無法使用 想請問有哪位高手用c#寫過嗎? 解决方案 你们公司是做货运的吗? 实际上大部分EDI的标准格式都只是用来做参考,各个公司在使用EDI文档的时候都会有或多或少的改变, 所以网上直