Lấy tên thư mục từ tên tệp


85

Tôi có tên tệp (C: \ folder \ foo.txt) và tôi cần truy xuất tên thư mục (C: \ folder) trong C ++ không được quản lý. Trong C #, tôi sẽ làm một cái gì đó như thế này:

string folder = new FileInfo("C:\folder\foo.txt").DirectoryName;

Có một hàm nào có thể được sử dụng trong C ++ không được quản lý để trích xuất đường dẫn từ tên tệp không?

Câu trả lời:


20

Có một chức năng Windows tiêu chuẩn cho việc này, PathRemoveFileSpec . Nếu bạn chỉ hỗ trợ Windows 8 trở lên, bạn nên sử dụng PathCchRemoveFileSpec để thay thế. Trong số các cải tiến khác, nó không còn giới hạn ở MAX_PATH(260) ký tự.


2
Lưu ý rằng chức năng này hiện không được dùng nữa. Đề xuất từ ​​Microsoft là sử dụng PathCchRemoveFileSpec để thay thế.
Mặc định

1
@Default: PathCchRemoveFileSpec chỉ khả dụng bắt đầu từ Windows 8. Vì Windows Vista và 7 vẫn được hỗ trợ, nên PathRemoveFileSpec cũng vậy .
IInspectable

153

Sử dụng Boost.Filesystem:

boost::filesystem::path p("C:\\folder\\foo.txt");
boost::filesystem::path dir = p.parent_path();


Nếu bạn cũng có thể đối phó với các thư mục, hãy biết thực tế rằng parent_path()từ "C:\\folder"sẽ dẫn đến "C:".
Semjon Mössinger

rất nhiều boost được nâng cấp lên std vì vậy bạn cũng hãy thử cái này .... include <filesystem> .... std :: Experiment :: filesystem :: path p ("C: \\ folder \\ foo.txt");
S Meaden

72

Ví dụ từ http://www.cplusplus.com/reference/string/string/find_last_of/

// string::find_last_of
#include <iostream>
#include <string>
using namespace std;

void SplitFilename (const string& str)
{
  size_t found;
  cout << "Splitting: " << str << endl;
  found=str.find_last_of("/\\");
  cout << " folder: " << str.substr(0,found) << endl;
  cout << " file: " << str.substr(found+1) << endl;
}

int main ()
{
  string str1 ("/usr/bin/man");
  string str2 ("c:\\windows\\winhelp.exe");

  SplitFilename (str1);
  SplitFilename (str2);

  return 0;
}

1
Đây là giải pháp tối thiểu tốt nhất ở đây.
plasmacel.

39

Trong C ++ 17 tồn tại một lớp std::filesystem::pathsử dụng phương thức parent_path.

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
    for(fs::path p : {"/var/tmp/example.txt", "/", "/var/tmp/."})
        std::cout << "The parent path of " << p
                  << " is " << p.parent_path() << '\n';
}

Đầu ra có thể:

The parent path of "/var/tmp/example.txt" is "/var/tmp"
The parent path of "/" is ""
The parent path of "/var/tmp/." is "/var/tmp"

2
Cũng có một .remove_filename()phương pháp.
Qqwy

1
Cảm ơn, @Qqwy, nó cũng cho phép sử dụng đường dẫn thư mục với phương pháp đó để nhận được kết quả chính xác và mong đợi không giống như cách tiếp cận từ câu trả lời
Herrgott

13

Tại sao nó phải phức tạp như vậy?

#include <windows.h>

int main(int argc, char** argv)         // argv[0] = C:\dev\test.exe
{
    char *p = strrchr(argv[0], '\\');
    if(p) p[0] = 0;

    printf(argv[0]);                    // argv[0] = C:\dev
}

10
Đây không phải là di động. Dấu phân cách đường dẫn trong linux là '/'. đường dẫn std :: filesystem :: là tiêu chuẩn và di động.
Rémi

7
 auto p = boost::filesystem::path("test/folder/file.txt");
 std::cout << p.parent_path() << '\n';             // test/folder
 std::cout << p.parent_path().filename() << '\n';  // folder
 std::cout << p.filename() << '\n';                // file.txt

Bạn có thể cần p.parent_path().filename()lấy tên của thư mục mẹ.


5

Sử dụng hệ thống tập tin boost ::. Dù sao thì nó cũng sẽ được tích hợp vào tiêu chuẩn tiếp theo để bạn cũng có thể quen với nó.


1
Bạn đang nói về tiêu chuẩn nào? Tôi biết rằng rất nhiều thứ từ boost đã được thêm vào C ++ std lib, hệ thống tệp cũng sẽ được thêm vào?
McLeary

7
"Nó sẽ được đưa vào anyway tiêu chuẩn tiếp theo" Và nó không
Anton K

@AntonK có thể là C ++ 2017?
Alessandro Jacopson

6
@AlessandroJacopson Tuyệt vời, nó có vẻ được bao gồm trong C ++ 17 - en.cppreference.com/w/cpp/filesystem
Anton K


1

Tôi rất ngạc nhiên là không ai đề cập đến cách tiêu chuẩn trong Posix

Vui lòng sử dụng basename / dirnamecấu trúc.

tên cơ sở người đàn ông


Các chức năng POSIX không phải là không có nhược điểm của chúng. Đặc biệt, chúng có thể sửa đổi bộ đệm mà bạn truyền vào, (chúng thực sự có nghĩa là chữ ký có basname(char * path)chứ không phải basename(const char * path)) và các triển khai không làm được điều đó dường như phải sử dụng bộ đệm tĩnh khiến chúng không an toàn theo luồng (về nguyên tắc bạn cũng có thể trả về các kết quả được phân bổ động, nhưng điều đó khiến bạn phụ thuộc vào các allochàm gia đình, điều khó hiểu trong C ++).
dmckee --- cựu điều hành kitten

-1

C ++ chuẩn sẽ không giúp bạn nhiều về vấn đề này, vì tên đường dẫn là dành riêng cho nền tảng. Bạn có thể phân tích cú pháp chuỗi theo cách thủ công (như trong câu trả lời của glowcoder), sử dụng các tiện ích của hệ điều hành (ví dụ: http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx ) hoặc có thể là cách tiếp cận tốt nhất, bạn có thể sử dụng thư viện hệ thống tệp của bên thứ ba như boost :: filesystem.


C ++ 1z của tiêu chuẩn hiện đang cố gắng áp dụng thư viện hệ thống tệp tăng cường kể từ bây giờ làm cho tính thân thiện với nền tảng ít gặp vấn đề hơn nhiều. Nó vẫn còn trong tiêu đề thử nghiệm cho MSVC, ít nhất.
kayleeFrye_onDeck

-6

Chỉ cần sử dụng cái này: ExtractFilePath (your_path_file_name)


5
Tôi tin rằng đây là một phương pháp Delphi, không phải một cái gì đó trong C ++.
Ian Hunter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.