Cách đơn giản nhất để lấy tên tệp từ đường dẫn là gì?
string filename = "C:\\MyDirectory\\MyFile.bat"
Trong ví dụ này, tôi sẽ nhận được "MyFile". không có phần mở rộng.
Cách đơn giản nhất để lấy tên tệp từ đường dẫn là gì?
string filename = "C:\\MyDirectory\\MyFile.bat"
Trong ví dụ này, tôi sẽ nhận được "MyFile". không có phần mở rộng.
Câu trả lời:
_splitpath sẽ làm những gì bạn cần. Tất nhiên bạn có thể làm điều đó theo cách thủ công nhưng cũng có thể _splitpath
xử lý tất cả các trường hợp đặc biệt.
BIÊN TẬP:
Như BillHoag đề cập đến nó được khuyến khích để sử dụng phiên bản an toàn hơn _splitpath
gọi _splitpath_s when available.
Hoặc nếu bạn muốn một cái gì đó di động, bạn có thể làm một cái gì đó như thế này
std::vector<std::string> splitpath(
const std::string& str
, const std::set<char> delimiters)
{
std::vector<std::string> result;
char const* pch = str.c_str();
char const* start = pch;
for(; *pch; ++pch)
{
if (delimiters.find(*pch) != delimiters.end())
{
if (start != pch)
{
std::string str(start, pch);
result.push_back(str);
}
else
{
result.push_back("");
}
start = pch + 1;
}
}
result.push_back(start);
return result;
}
...
std::set<char> delims{'\\'};
std::vector<std::string> path = splitpath("C:\\MyDirectory\\MyFile.bat", delims);
cout << path.back() << endl;
_splitpath
trong bất kỳ bao gồm trên máy của tôi.
<stdlib.h>
. Đối với tính di động, có lẽ bạn có thể liệt kê một số ví dụ về “các giải pháp di động hoàn toàn tốt”?
<stdlib.h>
. Và một giải pháp di động rõ ràng là boost::filesystem
.
_splitpath
trong stdlib.h
bản sao VS của bạn? Sau đó, bạn có thể muốn thực hiện cài đặt sửa chữa của VS.
Một giải pháp khả thi:
string filename = "C:\\MyDirectory\\MyFile.bat";
// Remove directory if present.
// Do this before extension removal incase directory has a period character.
const size_t last_slash_idx = filename.find_last_of("\\/");
if (std::string::npos != last_slash_idx)
{
filename.erase(0, last_slash_idx + 1);
}
// Remove extension if present.
const size_t period_idx = filename.rfind('.');
if (std::string::npos != period_idx)
{
filename.erase(period_idx);
}
Nhiệm vụ này khá đơn giản vì tên tệp cơ sở chỉ là một phần của chuỗi bắt đầu từ dấu hiệu cuối cùng cho các thư mục:
std::string base_filename = path.substr(path.find_last_of("/\\") + 1)
Nếu tiện ích mở rộng cũng bị xóa, điều duy nhất cần làm là tìm tiện ích mở rộng cuối cùng .
và tính substr
đến thời điểm này
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
Có lẽ nên kiểm tra để đối phó với các tệp chỉ bao gồm các phần mở rộng (tức là .bashrc
...)
Nếu bạn chia điều này thành các chức năng riêng biệt, bạn có thể linh hoạt sử dụng lại các tác vụ đơn lẻ:
template<class T>
T base_name(T const & path, T const & delims = "/\\")
{
return path.substr(path.find_last_of(delims) + 1);
}
template<class T>
T remove_extension(T const & filename)
{
typename T::size_type const p(filename.find_last_of('.'));
return p > 0 && p != T::npos ? filename.substr(0, p) : filename;
}
Mã được tạo mẫu để có thể sử dụng nó với các std::basic_string
trường hợp khác nhau (tức là std::string
& std::wstring
...)
Nhược điểm của quá trình tạo mẫu là yêu cầu chỉ định tham số mẫu nếu a const char *
được truyền cho các hàm.
Vì vậy, bạn có thể:
std::string
thay vì tạo mẫu mãstd::string base_name(std::string const & path)
{
return path.substr(path.find_last_of("/\\") + 1);
}
std::string
(làm chất trung gian có thể sẽ được nội tuyến / tối ưu hóa đi)inline std::string string_base_name(std::string const & path)
{
return base_name(path);
}
const char *
.std::string base = base_name<std::string>("some/path/file.ext");
std::string filepath = "C:\\MyDirectory\\MyFile.bat";
std::cout << remove_extension(base_name(filepath)) << std::endl;
Bản in
MyFile
base_name
trước.)
Giải pháp đơn giản nhất là sử dụng một cái gì đó như boost::filesystem
. Nếu vì lý do nào đó, đây không phải là một lựa chọn ...
Thực hiện điều này một cách chính xác sẽ yêu cầu một số mã phụ thuộc hệ thống: trong Windows, hoặc '\\'
hoặc '/'
có thể là dấu phân cách đường dẫn; trong Unix, chỉ '/'
hoạt động, và trong các hệ thống khác, ai biết được. Giải pháp rõ ràng sẽ là một cái gì đó như:
std::string
basename( std::string const& pathname )
{
return std::string(
std::find_if( pathname.rbegin(), pathname.rend(),
MatchPathSeparator() ).base(),
pathname.end() );
}
, với MatchPathSeparator
việc được định nghĩa trong tiêu đề phụ thuộc hệ thống như sau:
struct MatchPathSeparator
{
bool operator()( char ch ) const
{
return ch == '/';
}
};
cho Unix, hoặc:
struct MatchPathSeparator
{
bool operator()( char ch ) const
{
return ch == '\\' || ch == '/';
}
};
cho Windows (hoặc cái gì đó vẫn khác đối với một số hệ thống không xác định khác).
EDIT: Tôi đã bỏ lỡ thực tế là anh ấy cũng muốn ngăn chặn việc mở rộng. Đối với điều đó, hơn thế nữa:
std::string
removeExtension( std::string const& filename )
{
std::string::const_reverse_iterator
pivot
= std::find( filename.rbegin(), filename.rend(), '.' );
return pivot == filename.rend()
? filename
: std::string( filename.begin(), pivot.base() - 1 );
}
Mã phức tạp hơn một chút, bởi vì trong trường hợp này, cơ sở của trình lặp ngược nằm ở phía sai của nơi chúng ta muốn cắt. (Hãy nhớ rằng cơ sở của một trình lặp ngược lại nằm sau ký tự mà trình lặp trỏ tới.) Và thậm chí điều này hơi đáng ngờ: Tôi không thích thực tế là nó có thể trả về một chuỗi trống chẳng hạn. (Nếu '.'
ký tự duy nhất là ký tự đầu tiên của tên tệp, tôi tranh luận rằng bạn nên trả lại tên tệp đầy đủ. Điều này sẽ yêu cầu một chút mã bổ sung để bắt được trường hợp đặc biệt.)}
string::find_last_of
thay vì thao tác các trình lặp ngược?
string
, vì vậy bạn vẫn phải học chúng. Và sau khi học chúng, không có lý do gì để bận tâm tìm hiểu tất cả các giao diện cồng kềnh std::string
.
Bạn cũng có thể sử dụng các API Path của shell PathFindFileName, PathRemoveExtension. Có thể tệ hơn _splitpath cho vấn đề cụ thể này, nhưng những API đó rất hữu ích cho tất cả các loại công việc phân tích đường dẫn và chúng có tính đến các đường dẫn UNC, dấu gạch chéo và những thứ kỳ lạ khác.
wstring filename = L"C:\\MyDirectory\\MyFile.bat";
wchar_t* filepart = PathFindFileName(filename.c_str());
PathRemoveExtension(filepart);
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773589(v=vs.85).aspx
Hạn chế là bạn phải liên kết đến shlwapi.lib, nhưng tôi không thực sự chắc chắn tại sao đó là một nhược điểm.
Nếu bạn có thể sử dụng boost,
#include <boost/filesystem.hpp>
path p("C:\\MyDirectory\\MyFile.bat");
string basename = p.filename().string();
//or
//string basename = path("C:\\MyDirectory\\MyFile.bat").filename().string();
Đây là tất cả.
Tôi khuyên bạn nên sử dụng thư viện tăng cường. Boost mang đến cho bạn rất nhiều tiện ích khi làm việc với C ++. Nó hỗ trợ hầu hết các nền tảng. Nếu bạn sử dụng Ubuntu, bạn có thể cài đặt thư viện boost chỉ bằng một dòng sudo apt-get install libboost-all-dev
(tham khảo. Làm thế nào để cài đặt boost trên Ubuntu? )
Cách đơn giản nhất trong C ++ 17 là:
sử dụng #include <filesystem>
và filename()
cho tên tệp có phần mở rộng và stem()
không có phần mở rộng.
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
string filename = "C:\\MyDirectory\\MyFile.bat";
std::cout << fs::path(filename).filename() << '\n'
<< fs::path(filename).stem() << '\n'
<< fs::path("/foo/bar.txt").filename() << '\n'
<< fs::path("/foo/bar.txt").stem() << '\n'
<< fs::path("/foo/.bar").filename() << '\n'
<< fs::path("/foo/bar/").filename() << '\n'
<< fs::path("/foo/.").filename() << '\n'
<< fs::path("/foo/..").filename() << '\n'
<< fs::path(".").filename() << '\n'
<< fs::path("..").filename() << '\n'
<< fs::path("/").filename() << '\n';
}
đầu ra:
MyFile.bat
MyFile
"bar.txt"
".bar"
"."
"."
".."
"."
".."
"/"
Tham khảo: cppreference
Chức năng:
#include <string>
std::string
basename(const std::string &filename)
{
if (filename.empty()) {
return {};
}
auto len = filename.length();
auto index = filename.find_last_of("/\\");
if (index == std::string::npos) {
return filename;
}
if (index + 1 >= len) {
len--;
index = filename.substr(0, len).find_last_of("/\\");
if (len == 0) {
return filename;
}
if (index == 0) {
return filename.substr(1, len - 1);
}
if (index == std::string::npos) {
return filename.substr(0, len);
}
return filename.substr(index + 1, len - index - 1);
}
return filename.substr(index + 1, len - index);
}
Kiểm tra:
#define CATCH_CONFIG_MAIN
#include <catch/catch.hpp>
TEST_CASE("basename")
{
CHECK(basename("") == "");
CHECK(basename("no_path") == "no_path");
CHECK(basename("with.ext") == "with.ext");
CHECK(basename("/no_filename/") == "no_filename");
CHECK(basename("no_filename/") == "no_filename");
CHECK(basename("/no/filename/") == "filename");
CHECK(basename("/absolute/file.ext") == "file.ext");
CHECK(basename("../relative/file.ext") == "file.ext");
CHECK(basename("/") == "/");
CHECK(basename("c:\\windows\\path.ext") == "path.ext");
CHECK(basename("c:\\windows\\no_filename\\") == "no_filename");
}
Từ C ++ Docs - string :: find_last_of
#include <iostream> // std::cout
#include <string> // std::string
void SplitFilename (const std::string& str) {
std::cout << "Splitting: " << str << '\n';
unsigned found = str.find_last_of("/\\");
std::cout << " path: " << str.substr(0,found) << '\n';
std::cout << " file: " << str.substr(found+1) << '\n';
}
int main () {
std::string str1 ("/usr/bin/man");
std::string str2 ("c:\\windows\\winhelp.exe");
SplitFilename (str1);
SplitFilename (str2);
return 0;
}
Kết quả đầu ra:
Splitting: /usr/bin/man
path: /usr/bin
file: man
Splitting: c:\windows\winhelp.exe
path: c:\windows
file: winhelp.exe
find_last_of
trả về string::npos
nếu không tìm thấy gì.
string::npos
không cần phải thực hiện vì cách string::substr
thực hiện và điều này . a) string::npos
được chuyển là "length" => substr
đã ghi lại hành vi đọc tất cả cho đến khi kết thúc. b) substr
được cho trước " string::npos + 1
" và không có độ dài: string::npos
được ghi lại là có giá trị là -1
, do đó đánh giá 0
=> bắt đầu của chuỗi và giá trị mặc định của độ dài substr
là npos
=> hoạt động trên "just filename" quá cplusplus.com/reference / chuỗi / chuỗi / substr cplusplus.com/reference/string/string/npos
Biến thể C ++ 11 (lấy cảm hứng từ phiên bản của James Kanze) với khởi tạo đồng nhất và lambda nội tuyến ẩn danh.
std::string basename(const std::string& pathname)
{
return {std::find_if(pathname.rbegin(), pathname.rend(),
[](char c) { return c == '/'; }).base(),
pathname.end()};
}
Tuy nhiên, nó không xóa phần mở rộng tệp.
return c == '/' || c == '\\';
để làm cho nó hoạt động trên cửa sổ
if (pathname.size() == 0) return "."; auto iter = pathname.rbegin(); auto rend = pathname.rend(); while (iter != rend && *iter == '/') ++iter; if (iter == rend) /* pathname has only path separators */ return "/"; pathname = std::string(pathname.begin(), iter.base());
Các boost
filesystem
thư viện cũng có sẵn như là các experimental/filesystem
thư viện và đã được sáp nhập vào ISO C ++ cho C ++ 17. Bạn có thể sử dụng nó như thế này:
#include <iostream>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
int main () {
std::cout << fs::path("/foo/bar.txt").filename() << '\n'
}
Đầu ra:
"bar.txt"
Nó cũng hoạt động cho std::string
các đối tượng.
đây là điều duy nhất cuối cùng thực sự làm việc cho tôi:
#include "Shlwapi.h"
CString some_string = "c:\\path\\hello.txt";
LPCSTR file_path = some_string.GetString();
LPCSTR filepart_c = PathFindFileName(file_path);
LPSTR filepart = LPSTR(filepart_c);
PathRemoveExtension(filepart);
khá nhiều thứ mà Skrymsli đề xuất nhưng không hoạt động với wchar_t *, VS Enterprise 2015
_splitpath cũng hoạt động, nhưng tôi không muốn phải đoán xem tôi sẽ cần bao nhiêu ký tự char [?]; một số người có thể cần kiểm soát này, tôi đoán.
CString c_model_name = "c:\\path\\hello.txt";
char drive[200];
char dir[200];
char name[200];
char ext[200];
_splitpath(c_model_name, drive, dir, name, ext);
Tôi không tin rằng bất kỳ bao gồm nào là cần thiết cho _splitpath. Không cần thư viện bên ngoài (như boost) cho một trong hai giải pháp này.
Tôi sẽ làm điều đó bằng ...
Tìm kiếm ngược từ cuối chuỗi cho đến khi bạn tìm thấy dấu gạch chéo ngược / dấu gạch chéo ngược đầu tiên.
Sau đó tìm kiếm ngược lại từ cuối chuỗi cho đến khi bạn tìm thấy dấu chấm đầu tiên (.)
Sau đó, bạn có phần đầu và phần cuối của tên tệp.
Mẫu ...
'\\'
làm dấu phân cách đường dẫn cũng sử dụng '/'
, vì vậy bạn cần phải phù hợp với cả hai.) Và tôi không chắc bạn đang mong đợi điều gì.
my.source.cpp
được biên dịch thành my.source.obj
(với phần mở rộng được .cpp
thay thế bằng .obj
).
m_szFilePath.MakeLower();
CFileFind finder;
DWORD buffSize = MAX_PATH;
char longPath[MAX_PATH];
DWORD result = GetLongPathName(m_szFilePath, longPath, MAX_PATH );
if( result == 0)
{
m_bExists = FALSE;
return;
}
m_szFilePath = CString(longPath);
m_szFilePath.Replace("/","\\");
m_szFilePath.Trim();
//check if it does not ends in \ => remove it
int length = m_szFilePath.GetLength();
if( length > 0 && m_szFilePath[length - 1] == '\\' )
{
m_szFilePath.Truncate( length - 1 );
}
BOOL bWorking = finder.FindFile(this->m_szFilePath);
if(bWorking){
bWorking = finder.FindNextFile();
finder.GetCreationTime(this->m_CreationTime);
m_szFilePath = finder.GetFilePath();
m_szFileName = finder.GetFileName();
this->m_szFileExtension = this->GetExtension( m_szFileName );
m_szFileTitle = finder.GetFileTitle();
m_szFileURL = finder.GetFileURL();
finder.GetLastAccessTime(this->m_LastAccesTime);
finder.GetLastWriteTime(this->m_LastWriteTime);
m_ulFileSize = static_cast<unsigned long>(finder.GetLength());
m_szRootDirectory = finder.GetRoot();
m_bIsArchive = finder.IsArchived();
m_bIsCompressed = finder.IsCompressed();
m_bIsDirectory = finder.IsDirectory();
m_bIsHidden = finder.IsHidden();
m_bIsNormal = finder.IsNormal();
m_bIsReadOnly = finder.IsReadOnly();
m_bIsSystem = finder.IsSystem();
m_bIsTemporary = finder.IsTemporary();
m_bExists = TRUE;
finder.Close();
}else{
m_bExists = FALSE;
}
Biến m_szFileName chứa fileName.
boost::filesystem::path( path ).filename()
.
Không sử dụng _splitpath()
và_wsplitpath()
. Chúng không an toàn, và chúng đã lỗi thời!
Thay vào đó, hãy sử dụng các phiên bản an toàn của chúng, cụ thể là _splitpath_s()
và_wsplitpath_s()
Điều này cũng sẽ hoạt động:
// strPath = "C:\\Dir\\File.bat" for example
std::string getFileName(const std::string& strPath)
{
size_t iLastSeparator = 0;
return strPath.substr((iLastSeparator = strPath.find_last_of("\\")) != std::string::npos ? iLastSeparator + 1 : 0, strPath.size() - strPath.find_last_of("."));
}
Nếu bạn có thể sử dụng nó, Qt cung cấp QString (với split, trim, v.v.), QFile, QPath, QFileInfo, v.v. để thao tác với tệp, tên tệp và thư mục. Và tất nhiên nó cũng là hình thái chéo.
getFilename
hoặc một cái gì đó tương tự).
Trong một thời gian dài, tôi đã tìm kiếm một chức năng có thể phân hủy đúng đường dẫn tệp. Đối với tôi, mã này hoạt động hoàn hảo cho cả Linux và Windows.
void decomposePath(const char *filePath, char *fileDir, char *fileName, char *fileExt)
{
#if defined _WIN32
const char *lastSeparator = strrchr(filePath, '\\');
#else
const char *lastSeparator = strrchr(filePath, '/');
#endif
const char *lastDot = strrchr(filePath, '.');
const char *endOfPath = filePath + strlen(filePath);
const char *startOfName = lastSeparator ? lastSeparator + 1 : filePath;
const char *startOfExt = lastDot > startOfName ? lastDot : endOfPath;
if(fileDir)
_snprintf(fileDir, MAX_PATH, "%.*s", startOfName - filePath, filePath);
if(fileName)
_snprintf(fileName, MAX_PATH, "%.*s", startOfExt - startOfName, startOfName);
if(fileExt)
_snprintf(fileExt, MAX_PATH, "%s", startOfExt);
}
Kết quả ví dụ là:
[]
fileDir: ''
fileName: ''
fileExt: ''
[.htaccess]
fileDir: ''
fileName: '.htaccess'
fileExt: ''
[a.exe]
fileDir: ''
fileName: 'a'
fileExt: '.exe'
[a\b.c]
fileDir: 'a\'
fileName: 'b'
fileExt: '.c'
[git-archive]
fileDir: ''
fileName: 'git-archive'
fileExt: ''
[git-archive.exe]
fileDir: ''
fileName: 'git-archive'
fileExt: '.exe'
[D:\Git\mingw64\libexec\git-core\.htaccess]
fileDir: 'D:\Git\mingw64\libexec\git-core\'
fileName: '.htaccess'
fileExt: ''
[D:\Git\mingw64\libexec\git-core\a.exe]
fileDir: 'D:\Git\mingw64\libexec\git-core\'
fileName: 'a'
fileExt: '.exe'
[D:\Git\mingw64\libexec\git-core\git-archive.exe]
fileDir: 'D:\Git\mingw64\libexec\git-core\'
fileName: 'git-archive'
fileExt: '.exe'
[D:\Git\mingw64\libexec\git.core\git-archive.exe]
fileDir: 'D:\Git\mingw64\libexec\git.core\'
fileName: 'git-archive'
fileExt: '.exe'
[D:\Git\mingw64\libexec\git-core\git-archiveexe]
fileDir: 'D:\Git\mingw64\libexec\git-core\'
fileName: 'git-archiveexe'
fileExt: ''
[D:\Git\mingw64\libexec\git.core\git-archiveexe]
fileDir: 'D:\Git\mingw64\libexec\git.core\'
fileName: 'git-archiveexe'
fileExt: ''
Tôi hy vọng điều này cũng giúp bạn :)
shlwapi.lib/dll
sử dụng HKCU
đăng ký trong nội bộ.
Tốt nhất là không liên kết với shlwapi.lib
nếu bạn đang tạo thư viện hoặc sản phẩm không có giao diện người dùng. Nếu bạn đang viết lib thì mã của bạn có thể được sử dụng trong bất kỳ dự án nào kể cả những dự án không có giao diện người dùng.
Nếu bạn đang viết mã chạy khi người dùng chưa đăng nhập (ví dụ: dịch vụ [hoặc khác] được đặt để bắt đầu khi khởi động hoặc khởi động) thì không có HKCU
. Cuối cùng, shlwapi là các chức năng giải quyết; và kết quả là nằm trong danh sách không dùng nữa trong các phiên bản Windows mới hơn.