2010-12-13 25 views

Trả lời

126

Để trích xuất một tên tập tin mà không cần mở rộng, sử dụng tăng :: hệ thống tập tin :: đường :: gốc thay vì xấu xí 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

Đồng ý. Trả lời câu hỏi gọn gàng nhất. – AndyUK

+7

Trên thực tế, p.filename() là đường dẫn kiểu và sẽ được bao quanh bởi dấu ngoặc kép khi được chuyển đổi hoàn toàn, vì vậy bạn sẽ nhận được: tên tệp và đuôi mở rộng: "file.ext" Bạn có thể muốn p.filename(). string() thay thế. –

+1

Với C++ 14/C++ 17, bạn có thể sử dụng 'std :: testing :: filesystem' resp' std :: filesystem'. Xem bài đăng từ Yuchen Zhong bên dưới. –

3

Không mã, nhưng đây là ý tưởng:

  1. Đọc một std::string từ input stream (std::ifstream), mỗi trường hợp đọc sẽ được đường dẫn đầy đủ
  2. Thực hiện find_last_of trên chuỗi cho \
  3. Trích xuất một chuỗi con từ vị trí này đến cùng, bây giờ điều này sẽ cung cấp cho bạn tên tập tin
  4. Thực hiện find_last_of cho ., và hai bên sẽ substring cung cấp cho bạn tên + tiện ích.
+0

+1 để cung cấp trợ giúp mà không cần cung cấp mã. – razlebe

+0

Và -1 vì không di động :) – Kos

+0

Tại sao bỏ phiếu xuống? Nếu có bất cứ điều gì sai trái với những gì tôi nói, hãy cho tôi biết và tôi sẽ sửa chữa! – Nim

19

Nếu bạn muốn một cách an toàn (ví dụ: di động giữa các nền tảng và không đưa giả định trên đường đi), tôi khuyên bạn nên sử dụng boost::filesystem.

Nó sẽ tìm bằng cách nào đó như thế này:

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

Sau đó, bạn có thể trích xuất dữ liệu khác nhau từ con đường này. Here's the documentation of path object.


BTW: Cũng nên nhớ rằng để sử dụng đường dẫn như

c:\foto\foto2003\shadow.gif 

bạn cần phải thoát khỏi \ trong một chuỗi chữ:

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

Hoặc sử dụng / thay vì:

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

Điều này chỉ áp dụng để chỉ định chuỗi ký tự trong các dấu ngoặc đơn "", sự cố không tồn tại khi bạn tải đường dẫn từ tệp.

+2

+1 Chắc chắn là con đường để đi.Ví dụ trên trang chính cung cấp cách tìm kiếm thư mục: Sử dụng phương thức path.extension() để tìm kiếm nhật ký (xem http://www.boost.org/doc/libs/1_36_0/libs/filesystem/doc/index .htm) – Tom

+0

Thực tế điều này là trong hầu hết các trường hợp, tuy nhiên, việc thêm vào trong một số trường hợp là sự phụ thuộc không mong muốn trên một thư viện bên ngoài. Nếu bạn muốn làm việc chỉ những gì các tiêu chuẩn C + + cung cấp tôi đề nghị nhìn vào regex C++, nơi bạn có thể xác định một biểu thức chính quy để làm những gì bạn muốn (nhiều ví dụ trên internet). Lợi thế - không có phí do một số phụ thuộc bổ sung. Tuy nhiên điều này cũng để lại một câu hỏi mở - là yêu cầu đa nền tảng? Boost sẽ chăm sóc kiểu đường dẫn cho dù bạn đang sử dụng Windows hay Linux. Sử dụng regex bạn phải làm điều đó một mình. – rbaleksandar

13

Bạn sẽ phải đọc tên tệp của mình từ tệp trong std::string. Bạn có thể sử dụng toán tử trích xuất chuỗi của std::ostream. Khi bạn có tên tệp của mình theo số std::string, bạn có thể sử dụng phương thức std::string::find_last_of để tìm dấu phân cách cuối cùng.

Something như thế này:

std::ifstream input("file.log"); 
while (input) 
{ 
    std::string path; 
    input >> path; 

    size_t sep = path.find_last_of("\\/"); 
    if (sep != std::string::npos) 
     path = path.substr(sep + 1, path.size() - sep - 1); 

    size_t dot = path.find_last_of("."); 
    if (dot != std::string::npos) 
    { 
     std::string name = path.substr(0, dot); 
     std::string ext = path.substr(dot, path.size() - dot); 
    } 
    else 
    { 
     std::string name = path; 
     std::string ext = ""; 
    } 
} 
+0

Không muốn là ass thông minh nhưng nó phải là path.substr và không phải path.substring, phải không? –

0

tôi cũng sử dụng đoạn mã này để xác định nhân vật dấu gạch chéo thích hợp:.

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

và sau đó thay thế các dấu gạch chéo với dấu gạch chéo ưa thích cho hệ điều hành hữu ích nếu ai không ngừng triển khai giữa Linux/. cửa sổ

6

Đối 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" 

tham khảo về hệ thống tập tin: http://en.cppreference.com/w/cpp/filesystem


Theo đề nghị của @RoiDanto, cho các định dạng đầu ra, std::out có thể bao quanh đầu ra với trích dẫn, ví dụ như:

filename and extension: "file.ext" 

Bạn có thể chuyển đổi std::filesystem::path-std::string bởi p.filename().string() nếu đó là những gì bạn cần, ví dụ .:

filename and extension: file.ext 
+0

Hey @RoiDanton, cảm ơn bạn đã chỉnh sửa! Tôi vừa kiểm tra mã ví dụ trong liên kết tham chiếu lần nữa, có vẻ như không cần chuyển đổi kiểu trả về từ 'std :: filesystem :: path' thành' std :: string' để có thể sử dụng 'std :: cout'. http://en.cppreference.com/w/cpp/filesystem/path/filename Nhưng nếu bạn nghĩ khác, hãy bình luận hoặc chỉnh sửa lại bài đăng. –

+0

Đúng, 'std :: cout' có thể dựa vào chuyển đổi ngầm định. Tuy nhiên kể từ khi các bình luận sau khi 'std :: cout' nói file.ext và file, hoặc' .string() 'phải được thêm vào các chú thích hoặc chúng phải là" file.ext "và" file ". Với Visual C++ thực sự không có sự khác biệt (thậm chí không có 'string()' đầu ra là không có dấu ngoặc kép), nhưng với gcc 6.1 đầu ra là với dấu ngoặc kép nếu '.string()' bị bỏ qua. Xem http://coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3 –

+0

@RoiDanton, này, đó là thông tin chi tiết thú vị. Tôi sẽ cập nhật lại bài đăng. Cảm ơn bạn đã chia sẻ điều này! –

0

Đối với linux hoặc unix máy móc, các os có hai chức năng đối phó với đường dẫn và tên tập tin. sử dụng man 3 basename để có thêm thông tin về các hàm này. Lợi thế của việc sử dụng chức năng được cung cấp bởi hệ thống là bạn không phải cài đặt tăng hoặc cần phải viết các chức năng của riêng bạn.

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

Ví dụ mã từ trang người đàn ông:

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); 

Bởi vì kiểu lập luận không const của basename() chức năng, đó là một chút phi thẳng về phía trước sử dụng bên trong này C++ . Dưới đây là một ví dụ đơn giản từ cơ sở mã của tôi:

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; 
} 

Việc sử dụng mới/xóa không được phong cách tốt. Tôi có thể đặt nó vào một khối try/catch trong trường hợp có sự cố xảy ra giữa hai cuộc gọi.

Các vấn đề liên quan