Lấy tên tệp từ một đường dẫn


82

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.


1
Tìm kiếm từ phía sau cho đến khi bạn nhấn phím xóa lùi?
Kerrek SB

2
@KerrekSB, ý bạn là dấu gạch chéo ngược ;)
Nim

tôi có một chuỗi std :: chứa đường dẫn của tệp "c: \\ MyDirectory \\ Myfile.pdf". Tôi cần đổi tên tệp này thành myfile_md.pdf nên tôi cần lấy tên tệp từ đường dẫn.
nidhal

1
Nếu bạn cần thực hiện nhiều thao tác với đường dẫn tệp, hãy cân nhắc sử dụng Boost FileSystem boost.org/doc/libs/release/libs/filesystem/v3/doc/index.htm
edA-qa mort-ora-y

2
@Nim: Vâng! Tôi phải có được khoảng cách ra ...
Kerrek SB

Câu trả lời:


29

_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ể _splitpathxử 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 _splitpathgọ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;

2
Không có _splitpathtrong bất kỳ bao gồm trên máy của tôi.
James Kanze

9
Tôi có Visual Studio, g ++, và Sun CC. Tại sao tôi phải sử dụng một cái gì đó không chuẩn khi có những giải pháp di động hoàn toàn tốt.
James Kanze

2
@James, trang được liên kết đến cho biết nó đang ở trong <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”?
Synetech

2
@Synetech Trang được liên kết để mô tả một tiện ích mở rộng của Microsoft, không phải <stdlib.h>. Và một giải pháp di động rõ ràng là boost::filesystem.
James Kanze

3
@James, bạn không có _splitpathtrong stdlib.hbả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.
Synetech

62

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

đơn giản nhất luôn là tốt nhất!
Jean-François Fabre

60

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_stringtrườ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ể:

A) Chỉ sử dụng std::stringthay vì tạo mẫu mã

std::string base_name(std::string const & path)
{
  return path.substr(path.find_last_of("/\\") + 1);
}

B) Cung cấp chức năng bao gói bằng cách sử dụng 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);
}

C) Chỉ định tham số mẫu khi gọi với const char *.

std::string base = base_name<std::string>("some/path/file.ext");

Kết quả

std::string filepath = "C:\\MyDirectory\\MyFile.bat";
std::cout << remove_extension(base_name(filepath)) << std::endl;

Bản in

MyFile

Trong trường hợp sử dụng này mọi thứ đều OK (và câu hỏi ban đầu được trả lời), nhưng phần mở rộng của bạn loại bỏ là không hoàn hảo - nó sẽ thất bại nếu chúng ta vượt qua có cái gì đó như "/home/user/my.dir/myfile"
avtomaton

@avtomaton Chức năng xóa phần mở rộng nên được sử dụng trên tên tệp không phải là đường dẫn. (Chỉ cần đăng ký base_nametrước.)
Pixelchemist

Tôi hiểu nó (đó là lý do tại sao tôi đã viết rằng câu hỏi ban đầu được trả lời và trong trường hợp sử dụng này, mọi thứ đều ổn). Tôi chỉ muốn chỉ ra vấn đề này cho một số người sẽ cố gắng sử dụng những đoạn mã này.
avtomaton

Giải thích rất hay. Nó nâng cao hiểu biết về cấu trúc của vấn đề. Cảm ơn
hell_ical_vortex

38

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 MatchPathSeparatorviệ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.)}


9
Làm thế nào về việc sử dụng string::find_last_ofthay vì thao tác các trình lặp ngược?
Luc Touraille

@LucTouraille Tại sao phải học hai cách thực hiện khi một người sẽ làm? Bạn sẽ cần các trình lặp đảo ngược cho bất kỳ vùng chứa nào ngoại trừ 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.
James Kanze

Lưu ý: Tiêu đề <filesystem> đi kèm với Visual Studio 2015 trở lên, vì vậy bạn không phải thêm phụ thuộc vào boost để sử dụng nó.
IInspectable

15

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.


Giải pháp ưa thích của tôi để lấy tên tệp từ một đường dẫn.
Andreas

15

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


14

Cách đơn giản nhất trong C ++ 17 là:

sử dụng #include <filesystem>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


nó không còn ở dạng "thử nghiệm" nữa
Vit

13

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

8

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

Đừng quên (và để xử lý) find_last_oftrả về string::nposnếu không tìm thấy gì.
congusbongus

@congusbongus Đúng, nhưng không có cảm giác tách các đường dẫn tập tin khi nó chỉ là một tên tập tin (không đường) :)
jave.web

@ jave.web Nó có ý nghĩa và PHẢI xử lý trả về 'string :: npos'. Việc triển khai một chức năng cho điều này sẽ có thể xử lý các đầu vào khác nhau bao gồm "chỉ tên tệp". Nếu không, nó sẽ vô dụng nếu lỗi của nó trong quá trình triển khai thực tế.
winux 22/09/2016

@winux này đã xem xét hợp lệ PATHS ... Nếu bạn không tin tưởng vào đầu vào, bạn nên, tất nhiên, xác nhận con đường đầu tiên.
jave.web

@winux Dù sao thì việc kiểm tra string::nposkhông cần phải thực hiện vì cách string::substrthự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 substrnpos=> hoạt động trên "just filename" quá cplusplus.com/reference / chuỗi / chuỗi / substr cplusplus.com/reference/string/string/npos
jave.web

5

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.


Ngắn gọn và ngọt ngào, mặc dù nó chỉ hoạt động với các đường dẫn không phải Windows.
Volomike

bạn luôn có thể thay đổi lambda quay trở lại return c == '/' || c == '\\';để làm cho nó hoạt động trên cửa sổ
ziomq1991

Để xử lý những con đường như "", "///", và "dir1 / dir2 /", thêm đoạn mã sau trước khi tuyên bố trở lại trên (cf. POSIX basename ()): 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());
Gidfiddle

5

Các boost filesystemthư viện cũng có sẵn như là các experimental/filesystemthư 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::stringcác đối tượng.


4

đâ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.


4
std::string getfilename(std::string path)
{
    path = path.substr(path.find_last_of("/\\") + 1);
    size_t dot_i = path.find_last_of('.');
    return path.substr(0, dot_i);
}

3

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 ...


Mà không hoạt động cho bất kỳ hệ thống nào tôi biết. (Một hệ thống chấp nhận '\\'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ì.
James Kanze

Được rồi, vì vậy hãy sửa đổi nó cho phù hợp, không có vấn đề gì. Và mong chờ dấu chấm đầu tiên (.)
TomP89 15/12/11

Bạn vẫn phải tìm dấu chấm cuối cùng, không phải dấu chấm đầu tiên. (Trình lặp ngược là bạn của bạn!)
James Kanze

À vâng, điểm tốt. Vì vậy, đối với một file.ext.ext thì bạn có muốn giải nén file.ext không. :)
TomP89

Có lẽ. Đó là quy ước thông thường, trong mọi trường hợp: chẳng hạn my.source.cppđược biên dịch thành my.source.obj(với phần mở rộng được .cppthay thế bằng .obj).
James Kanze

2
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.


3
wow - đó là rất nhiều mã cho "lấy tên tệp" từ đường dẫn ... :)
Nim

4
@Nim Ấn tượng của tôi nữa. Trong mã của riêng tôi, tôi sử dụng một lớp lót: boost::filesystem::path( path ).filename().
James Kanze

Tôi có một lớp CFileInfo có mã đó. Tôi chỉ bán mã ở đây vì nó đang được thử nghiệm và tôi không muốn mạo hiểm bất cứ điều gì ... Bạn chỉ có thể sử dụng khoảng 5 dòng mã từ ví dụ này.
Lucian

2

Không sử dụng _splitpath()_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()_wsplitpath_s()


2

Đ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.


4
Vì lợi ích của những người đọc mã trong tương lai của bạn, vui lòng sử dụng các biến tạm thời có tên có ý nghĩa thay vì nhồi nhét mọi thứ vào một dòng mã duy nhất (và trong khi bạn đang đọc, vui lòng gói gọn tất cả điều này vào một hàm getFilenamehoặc một cái gì đó tương tự).
Luc Touraille

đã chỉnh sửa. Nhưng vấn đề là làm cho nó ngắn gọn, vì một số câu trả lời hữu ích đã được đưa ra.
typedef

1
Tôi nghĩ điều đó là SAI. Bạn không nên thay thế phần cuối cùng: "strPath.size () - strPath.find_last_of (". ")" Bằng "strPath.find_last_of (". ") - iLastSeparator"
taktak004

@ taktak004 bạn nói đúng, nó phải là `return strPath.substr ((iLastSeparator = strPath.find_last_of (" / "))! = std :: string :: npos? iLastSeparator + 1: 0, strPath.find_last_of (". " ) - iLastSeparator); '
phenmod

2

Bạn có thể sử dụng hệ thống tệp std :: để làm điều đó khá hay:

#include <filesystem>
namespace fs = std::experimental::filesystem;

fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();

0

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


0

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.


0

Một giải pháp regex chậm nhưng thẳng về phía trước:

    std::string file = std::regex_replace(path, std::regex("(.*\\/)|(\\..*)"), "");
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.