2010-12-13 185 views
43

的路徑提取的文件名和擴展名我都存儲在.log在這句法的文件列表:我怎樣才能在C++

c:\foto\foto2003\shadow.gif 
D:\etc\mom.jpg 

我想提取的名稱,並從該文件的擴展名。你能舉一個簡單的方法來做這個例子嗎?

回答

126

要提取的文件名不帶擴展名,使用boost ::文件系統::路徑:: ,而不是醜陋的std :: string :: find_last_of(」。「)

boost::filesystem::path p("c:/dir/dir/file.ext"); 
std::cout << "filename and extension : " << p.filename() << std::endl; // file.ext 
std::cout << "filename only   : " << p.stem() << std::endl;  // file 
+0

同意。最簡潔地回答問題。 – AndyUK 2014-05-28 12:43:18

+7

實際上,p.filename()是類型路徑,並且在隱式轉換時將被引號包圍,因此您將得到: 文件名和擴展名:「file.ext」 您可能想要p.filename()。 string()代替。 – 2016-02-18 19:54:27

+1

使用C++ 14/C++ 17,你可以使用'std :: experimental :: filesystem'和'std :: filesystem'。見下面的Yuchen Zhong的帖子。 – 2017-04-12 14:15:44

3

不是代碼,但這裏的理念是:

  1. 從輸入流(std::ifstream),每個實例讀會的完整路徑閱讀std::string
  2. 的字符串爲做一個find_last_of\
  3. 提取從這個位置到最後一個子,現在這會給你的文件名
  4. 做一個find_last_of.,和一個子兩側會給你名字+擴展名。
+0

+1僅用於提供幫助而不提供代碼。 – razlebe 2010-12-13 16:12:12

+0

而且-1不能攜帶:) – Kos 2010-12-13 16:12:44

+0

爲什麼要投票?如果我說的話有什麼問題,請告訴我,我會解決的! – Nim 2010-12-13 16:13:24

19

如果你想要一個安全的方式(即平臺之間的便攜式,而不是假設的道路),我建議使用boost::filesystem

它看起來在某種程度上是這樣的:

boost::filesystem::path my_path(filename); 

然後你可以從這個路徑提取各種數據。 Here's the documentation of path object.


BTW:還記得,爲了使用路徑喜歡

c:\foto\foto2003\shadow.gif 

你一定要逃逸一個字符串的\

const char* filename = "c:\\foto\\foto2003\\shadow.gif"; 

或者使用/代替:

const char* filename = "c:/foto/foto2003/shadow.gif"; 

這僅適用於在""引號中指定文字字符串,從文件加載路徑時,問題不存在。

+2

+1肯定要走的路。主站點上的示例提供了搜索目錄的方法:使用path.extension()方法搜索日誌(請參閱http://www.boost.org/doc/libs/1_36_0/libs/filesystem/doc/index .htm) – Tom 2010-12-13 16:18:43

+0

事實上,在大多數情況下,這是要走的路,但它涉及在某些情況下添加對外部庫的不良依賴。如果你只想使用C++標準提供的東西,我建議你看一下C++的正則表達式,在那裏你可以定義一個正則表達式來做你想做的事情(互聯網上有很多例子)。優點 - 由於一些額外的依賴關係,沒有開銷。然而,這也讓一個問題開放 - 是否需要多平臺?無論您使用的是Windows還是Linux,Boost都會照顧路徑風格。使用正則表達式你必須自己做。 – rbaleksandar 2014-07-11 20:13:00

13

您必須從std::string中的文件中讀取您的文件名。您可以使用std::ostream的字符串提取操作符。一旦你的文件名爲std::string,你可以使用std::string::find_last_of方法找到最後一個分隔符。

事情是這樣的:

​​
+0

不想成爲自作聰明,但它應該是path.substr而不是path.substring,對吧? – 2012-02-23 09:10:13

0

我也使用這個片段以確定適當的斜槓字符:

boost::filesystem::path slash("/"); 
    boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native(); 

,然後與用於OS優選斜線取代斜線有用的,如果一臺Linux之間不斷部署/ 。窗戶

6

對於C++ 17

#include <filesystem> 

std::filesystem::path p("c:/dir/dir/file.ext"); 
std::cout << "filename and extension: " << p.filename() << std::endl; // "file.ext" 
std::cout << "filename only: " << p.stem() << std::endl;    // "file" 

參考文件系統有關:http://en.cppreference.com/w/cpp/filesystem


如所建議的通過@RoiDanto,對於輸出格式,std::out可以圍繞與報價,例如輸出:

filename and extension: "file.ext" 

您可以通過p.filename().string()轉換std::filesystem::pathstd::string如果這就是你所需要的,例如:

filename and extension: file.ext 
+0

嗨@RoiDanton,感謝編輯!我再次檢查了引用鏈接中的示例代碼,似乎沒有必要將返回類型從'std :: filesystem :: path'轉換爲'std :: string'以便能夠使用'的std :: cout'。 http://en.cppreference.com/w/cpp/filesystem/path/filename但是,如果您認爲不然,請隨時評論或編輯帖子。 – 2017-04-12 14:39:27

+0

這是真的,'std :: cout'可以依靠隱式轉換。然而,由於'std :: cout'後面的註釋表示file.ext和file,無論是'.string()'都必須添加到註釋中,否則它們應該是「file.ext」和「file」。使用Visual C++確實沒有區別(即使沒有'string()'輸出也沒有引號),但對於gcc 6.1,如果忽略'.string()',輸出將帶有引號。見http://coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3 – 2017-04-12 14:56:44

+0

@RoiDanton,嘿,這是有趣的見解。我會再次更新這篇文章。感謝分享! – 2017-04-12 15:21:02

0

對於Linux或UNIX機器,操作系統有一個處理的路徑和文件名的兩種功能。使用man 3 basename來獲取有關這些功能的更多信息。 使用系統提供的功能的優點是您不必安裝boost或需要編寫自己的函數。從人頁

#include <libgen.h> 
     char *dirname(char *path); 
     char *basename(char *path); 

示例代碼:

char *dirc, *basec, *bname, *dname; 
      char *path = "/etc/passwd"; 

      dirc = strdup(path); 
      basec = strdup(path); 
      dname = dirname(dirc); 
      bname = basename(basec); 
      printf("dirname=%s, basename=%s\n", dname, bname); 

由於非const參數類型的基名()函數,它是一點點非直線前進使用該內部C++代碼。下面是我的代碼庫中的一個簡單示例:

string getFileStem(const string& filePath) const { 
    char* buff = new char[filePath.size()+1]; 
    strcpy(buff, filePath.c_str()); 
    string tmp = string(basename(buff)); 
    string::size_type i = tmp.rfind('.'); 
    if (i != string::npos) { 
     tmp = tmp.substr(0,i); 
    } 
    delete[] buff; 
    return tmp; 
} 

使用new/delete不是很好的樣式。如果兩次調用之間發生任何事情,我可以將它放入try/catch 區塊。