Uzyskaj nazwę pliku ze ścieżki

85

Jaki jest najprostszy sposób uzyskania nazwy pliku ze ścieżki?

string filename = "C:\\MyDirectory\\MyFile.bat"

W tym przykładzie powinienem otrzymać „MyFile”. bez przedłużenia.

nidhal
źródło
1
Szukaj od tyłu, aż trafisz w backspace?
Kerrek SB
2
@KerrekSB, masz na myśli ukośnik odwrotny ;)
Nim
mam std :: string, który zawiera ścieżkę do pliku „c: \\ MyDirectory \\ Myfile.pdf” Muszę zmienić nazwę tego pliku na myfile_md.pdf, więc muszę uzyskać nazwę pliku ze ścieżki.
nidhal
1
Jeśli potrzebujesz dużo pracy ze ścieżkami plików, rozważ użycie Boost FileSystem boost.org/doc/libs/release/libs/filesystem/v3/doc/index.htm
edA-qa mort-ora-y
2
@ Nim: Tak! Musiałem się oddalać ...
Kerrek SB,

Odpowiedzi:

29

_splitpath powinien zrobić to, czego potrzebujesz. Możesz oczywiście zrobić to ręcznie, ale _splitpathobsługuje również wszystkie specjalne przypadki.

EDYTOWAĆ:

Jak wspomniał BillHoag, zaleca się używanie bezpieczniejszej wersji o _splitpathnazwie _splitpath_s, jeśli jest dostępna.

Lub jeśli chcesz coś przenośnego, możesz po prostu zrobić coś takiego

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;
AndersK
źródło
2
Nie ma _splitpathżadnego z załączników na moim komputerze.
James Kanze,
9
Mam Visual Studio i g ++ oraz Sun CC. Po co używać czegoś niestandardowego, skoro istnieją doskonale dobre rozwiązania przenośne.
James Kanze,
2
@James, strona, do której prowadzi link, mówi, że jest <stdlib.h>. Jeśli chodzi o przenośność, może możesz wymienić kilka przykładów „doskonale dobrych przenośnych rozwiązań”?
Synetech
2
@Synetech Strona, do której prowadzi łącze, opisuje rozszerzenie firmy Microsoft, a nie <stdlib.h>. A oczywistym przenośnym rozwiązaniem jest boost::filesystem.
James Kanze
3
@James, nie masz _splitpathw stdlib.hswojej kopii VS? Następnie możesz wykonać instalację naprawczą VS.
Synetech
63

Możliwe rozwiązanie:

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);
}
hmjd
źródło
najprostsze jest zawsze najlepsze!
Jean-François Fabre
61

Zadanie jest dość proste, ponieważ podstawowa nazwa pliku to tylko część ciągu zaczynająca się od ostatniego separatora dla folderów:

std::string base_filename = path.substr(path.find_last_of("/\\") + 1)

Jeśli rozszerzenie ma również zostać usunięte, jedyną rzeczą do zrobienia jest znalezienie ostatniego .i przejście substrdo tego punktu

std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);

Być może powinno być sprawdzanie, aby poradzić sobie z plikami składającymi się wyłącznie z rozszerzeń (tj .bashrc...)

Jeśli podzielisz to na osobne funkcje, będziesz elastyczny, aby ponownie wykorzystać pojedyncze zadania:

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

Kod jest oparty na szablonie, aby móc go używać z różnymi std::basic_stringinstancjami (tj. std::string& std::wstring...)

Wadą szablonu jest wymóg określenia parametru szablonu, jeśli a const char *jest przekazywany do funkcji.

Możesz więc:

A) Używaj tylko std::stringzamiast tworzenia szablonu kodu

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

B) Zapewnij funkcję owijania przy użyciu std::string(jako półproduktów, które prawdopodobnie będą wstawiane / zoptymalizowane)

inline std::string string_base_name(std::string const & path)
{
  return base_name(path);
}

C) Określ parametr szablonu podczas wywoływania z const char *.

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

Wynik

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

Wydruki

MyFile
Pixelchemist
źródło
W tym przypadku wszystko jest w porządku (i odpowiedź na oryginalne pytanie), ale twój program do usuwania rozszerzeń nie jest doskonały - zawiedzie, jeśli przekażemy tam coś takiego jak "/home/user/my.dir/myfile"
avtomaton
@avtomaton Funkcji usuwania rozszerzenia należy używać w przypadku nazwy pliku, a nie ścieżki. (Po prostu base_name
złóż
Rozumiem (dlatego napisałem, że odpowiedź na oryginalne pytanie i w tym przypadku wszystko jest w porządku). Chciałem tylko zwrócić uwagę na ten problem komuś, kto spróbuje użyć tych fragmentów.
avtomaton
Bardzo ładne wyjaśnienie. Zwiększa strukturalne zrozumienie problemu. Dzięki
hell_ical_vortex
38

Najprostszym rozwiązaniem jest użycie czegoś w rodzaju boost::filesystem. Jeśli z jakiegoś powodu to nie jest opcja ...

Robi to poprawnie będzie wymagało trochę kodu zależnego systemu: pod Windows, albo '\\'czy '/'może być separator ścieżka; pod Uniksem '/'działa tylko , a pod innymi systemami, kto wie. Oczywistym rozwiązaniem byłoby coś takiego:

std::string
basename( std::string const& pathname )
{
    return std::string( 
        std::find_if( pathname.rbegin(), pathname.rend(),
                      MatchPathSeparator() ).base(),
        pathname.end() );
}

, ze MatchPathSeparatorzdefiniowaniem w nagłówku zależnym od systemu jako:

struct MatchPathSeparator
{
    bool operator()( char ch ) const
    {
        return ch == '/';
    }
};

dla Uniksa lub:

struct MatchPathSeparator
{
    bool operator()( char ch ) const
    {
        return ch == '\\' || ch == '/';
    }
};

dla Windows (lub coś jeszcze innego dla innego nieznanego systemu).

EDYCJA: Przegapiłem fakt, że on też chciał stłumić rozszerzenie. W tym celu więcej tego samego:

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

Kod jest nieco bardziej złożony, ponieważ w tym przypadku podstawa iteratora odwrotnego znajduje się po złej stronie miejsca, w którym chcemy wyciąć. (Pamiętaj, że podstawa iteratora odwrotnego znajduje się za znakiem, na który iterator wskazuje.) I nawet to jest trochę wątpliwe: nie podoba mi się fakt, że może on na przykład zwrócić pusty ciąg. (Jeśli jedyny '.'jest pierwszym znakiem nazwy pliku, uważałbym, że powinieneś zwrócić pełną nazwę pliku. Wymagałoby to trochę dodatkowego kodu, aby złapać specjalny przypadek.)}

James Kanze
źródło
9
A co powiesz na używanie string::find_last_ofzamiast manipulowania odwrotnymi iteratorami?
Luc Touraille,
@LucTouraille Po co uczyć się dwóch sposobów robienia rzeczy, skoro jeden to robi? Potrzebowałbyś odwrotnych iteratorów dla dowolnego kontenera z wyjątkiem string, więc i tak musisz się ich nauczyć. A gdy się ich nauczyłem, nie ma powodu, aby zawracać sobie głowę nauką całego nadętego interfejsu std::string.
James Kanze
Uwaga: nagłówek <filesystem> jest dostarczany z programem Visual Studio 2015 i nowszymi wersjami, więc nie musisz dodawać zależności od przyspieszenia, aby go używać.
Niespodziewane
16

Najprostszy sposób w C ++ 17 to:

użyj #include <filesystem>i filename()dla nazwy pliku z rozszerzeniem i stem()bez rozszerzenia.

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

wynik:

MyFile.bat
MyFile
"bar.txt"
".bar"
"."
"."
".."
"."
".."
"/"

Odniesienie: cppreference

eliasetm
źródło
nie jest już w "eksperymentalnym"
Vit
15

Możesz również użyć interfejsów API PathFindFileName powłoki, PathRemoveExtension. Prawdopodobnie gorsze niż _splitpath w przypadku tego konkretnego problemu, ale te interfejsy API są bardzo przydatne dla wszystkich rodzajów zadań parsowania ścieżek i uwzględniają ścieżki UNC, ukośniki i inne dziwne rzeczy.

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

Wadą jest to, że musisz linkować do shlwapi.lib, ale nie jestem pewien, dlaczego jest to wada.

Skrymsli
źródło
Moje preferowane rozwiązanie do pobierania nazwy pliku ze ścieżki.
Andreas
15

Jeśli możesz użyć wzmocnienia,

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

To wszystko.

Polecam skorzystać z biblioteki boost. Boost daje wiele udogodnień podczas pracy z C ++. Obsługuje prawie wszystkie platformy. Jeśli używasz Ubuntu, możesz zainstalować bibliotekę boost tylko o jedną linię sudo apt-get install libboost-all-dev(zob. Jak zainstalować boost na Ubuntu? )

plhn
źródło
12

Funkcjonować:

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

Testy:

#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");
}
Rian Quinn
źródło
8

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

Wyjścia:

Splitting: /usr/bin/man
 path: /usr/bin
 file: man
Splitting: c:\windows\winhelp.exe
 path: c:\windows
 file: winhelp.exe
jave.web
źródło
Nie zapomnij (i obsłuż), który find_last_ofzwraca, string::nposjeśli nic nie zostało znalezione.
congusbongus
@congusbongus Prawda, ale nie ma sensu dzielenia ścieżki pliku, gdy jest to tylko nazwa pliku (bez ścieżki) :)
jave.web
@ jave.web To ma sens i MUSI uchwyt zwracać „string :: npos”. Zaimplementowanie w tym celu funkcji powinno być w stanie obsłużyć różne dane wejściowe, w tym „samą nazwę pliku”. W przeciwnym razie będzie bezużyteczny, jeśli w rzeczywistej implementacji będzie zawierał błędy.
winux
@winux To bierze pod uwagę już prawidłowe ŚCIEŻKI ... Jeśli nie ufasz wejściu, powinieneś oczywiście najpierw sprawdzić poprawność ścieżki.
jave.web
@winux W każdym razie sprawdzanie string::nposnie musi być wykonywane ze względu na sposób, w jaki to i string::substrsą zaimplementowane. a) string::npos jest przekazywany jako "length" => substrma udokumentowane zachowanie czytania wszystkich do końca. b) substrma podane „ string::npos + 1” i nie ma długości: string::nposjest udokumentowane, że ma wartość -1, więc oblicza 0=> początek ciągu i domyślną wartością długości dla substrjest npos=> działa również na „tylko nazwa pliku” cplusplus.com/reference / string / ciąg / SUBSTR cplusplus.com/reference/string/string/npos
jave.web
5

Wariant C ++ 11 (zainspirowany wersją Jamesa Kanze) z jednolitą inicjalizacją i anonimową wbudowaną lambdą.

std::string basename(const std::string& pathname)
{
    return {std::find_if(pathname.rbegin(), pathname.rend(),
                         [](char c) { return c == '/'; }).base(),
            pathname.end()};
}

Nie usuwa jednak rozszerzenia pliku.

alveko
źródło
Krótkie i słodkie, chociaż działa tylko ze ścieżkami innymi niż Windows.
Volomike
zawsze możesz zmienić powrót lambda na, return c == '/' || c == '\\';aby
działał w systemie
Aby obsłużyć ścieżki takie jak „”, „///” i „dir1 / dir2 /”, dodaj następujący kod przed powyższą instrukcją return (por. 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

boost filesystemBiblioteka jest również dostępny jako experimental/filesystembiblioteki i został włączony do ISO C ++ C ++ 17. Możesz go używać w ten sposób:

#include <iostream>
#include <experimental/filesystem>

namespace fs = std::experimental::filesystem;

int main () {
    std::cout << fs::path("/foo/bar.txt").filename() << '\n'
}

Wynik:

"bar.txt"

Działa również w przypadku std::stringobiektów.

Adam Erickson
źródło
4

to jedyna rzecz, która w końcu zadziałała dla mnie:

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

prawie to, co sugerował Skrymsli, ale nie działa z wchar_t *, VS Enterprise 2015

_splitpath też działało, ale nie lubię zgadywać, ilu znaków [?] będę potrzebować; Myślę, że niektórzy ludzie prawdopodobnie potrzebują tej kontroli.

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

Nie sądzę, by były potrzebne jakieś dołączenia dla _splitpath. Żadne zewnętrzne biblioteki (takie jak boost) nie były potrzebne do żadnego z tych rozwiązań.

Fraktal
źródło
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);
}
Beyondo
źródło
3

Zrobiłbym to przez ...

Szukaj wstecz od końca ciągu, aż znajdziesz pierwszy ukośnik odwrotny / ukośnik w przód.

Następnie wyszukaj ponownie wstecz od końca ciągu, aż znajdziesz pierwszą kropkę (.)

Masz wtedy początek i koniec nazwy pliku.

Proste ...

TomP89
źródło
Co nie działa w żadnym systemie, który znam. (Jedyny system, który akceptuje '\\'jako separator ścieżek, również używa '/', więc musisz dopasować również.) I nie jestem pewien, czego oczekiwałbyś.
James Kanze,
Dobra, więc zmodyfikuj to, aby pasowało, nie ma znaczenia. I nie mogę się doczekać pierwszej kropki (.)
TomP89
Nadal musisz znaleźć ostatnią kropkę, a nie pierwszą. (Iteratory odwrotne są twoim przyjacielem!)
James Kanze,
Ach tak, słuszna uwaga. Tak więc w przypadku pliku file.ext.ext chciałbyś wyodrębnić plik.ext, prawda? :)
TomP89
Prawdopodobnie. W każdym razie taka jest zwykła konwencja: na przykład my.source.cppzostanie skompilowany do my.source.obj(z rozszerzeniem .cppzamienionym na .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;
}

Zmienna m_szFileName zawiera nazwę pliku.

Lucian
źródło
3
wow - to dużo kodu do "pobierz nazwę pliku" ze ścieżki ... :)
Nim
4
@Nim Moje wrażenie. W moim kodu używam jednego-liner: boost::filesystem::path( path ).filename().
James Kanze,
Mam klasę CFileInfo, która ma ten kod. Po prostu wrzuciłem tutaj kod, ponieważ jest przetestowany i nie chciałem nic ryzykować ... Możesz po prostu użyć około 5 linii kodu z tego przykładu.
Lucian
2

Nie używaj _splitpath()i _wsplitpath(). Nie są bezpieczne i są przestarzałe!

Zamiast tego użyj ich bezpiecznych wersji, a mianowicie _splitpath_s()i_wsplitpath_s()

hkBattousai
źródło
2

To też powinno działać:

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

Jeśli możesz go użyć, Qt zapewnia QString (z podziałem, przycinaniem itp.), QFile, QPath, QFileInfo itp. Do manipulowania plikami, nazwami plików i katalogami. I oczywiście jest to również platforma krzyżowa.

typedef
źródło
4
Ze względu na przyszłych czytelników twojego kodu, używaj tymczasowych zmiennych o znaczących nazwach zamiast upychać wszystko w jednym wierszu kodu (a kiedy już to zrobisz, zamknij to wszystko w funkcję getFilenamelub coś w tym rodzaju).
Luc Touraille,
edytowane. Chodziło jednak o to, aby było to krótkie, ponieważ udzielono już kilku roboczych odpowiedzi.
napisany
1
Myślę, że to źle. Nie należy zamieniać ostatniej części: "strPath.size () - strPath.find_last_of (". ")" Na "strPath.find_last_of (". ") - iLastSeparator"
taktak004
@ taktak004 masz rację, powinno to być `return strPath.substr ((iLastSeparator = strPath.find_last_of (" / "))! = std :: string :: npos? iLastSeparator + 1: 0, strPath.find_last_of (". " ) - iLastSeparator); `
phenmod
2

Możesz użyć std :: filesystem, aby zrobić to całkiem przyjemnie:

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

fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();
GuidedHacking
źródło
0

Długo szukałem funkcji potrafiącej poprawnie rozłożyć ścieżkę do pliku. Dla mnie ten kod działa doskonale zarówno dla Linuksa, jak i Windowsa.

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

Przykładowe wyniki to:

[]
  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:  ''

Mam nadzieję, że to Ci pomoże :)

nikt specjalny
źródło
0

shlwapi.lib/dllużywa HKCUgałęzi rejestru wewnętrznie.

Najlepiej nie podawać linków, shlwapi.libjeśli tworzysz bibliotekę lub produkt nie ma interfejsu użytkownika. Jeśli piszesz bibliotekę, kod może być używany w dowolnym projekcie, w tym w tych, które nie mają interfejsów użytkownika.

Jeśli piszesz kod, który działa, gdy użytkownik nie jest zalogowany (np. Usługa [lub inna] ustawiona na uruchamianie podczas rozruchu lub uruchamiania), nie ma HKCU. Wreszcie shlwapi to funkcje rozliczeniowe; w rezultacie wysoko na liście przestarzałych w nowszych wersjach systemu Windows.

Potok
źródło
0

Powolne, ale proste rozwiązanie regex:

    std::string file = std::regex_replace(path, std::regex("(.*\\/)|(\\..*)"), "");
Sava B.
źródło
0

Zaimplementowałem funkcję, która może zaspokoić Twoje potrzeby. Opiera się na funkcji constexpr string_view find_last_of (od C ++ 17), którą można obliczyć w czasie kompilacji

constexpr const char* base_filename(const char* p) {
    const size_t i = std::string_view(p).find_last_of('/');
    return std::string_view::npos == i ? p : p + i + 1 ;
}

//in the file you used this function
base_filename(__FILE__);
ZhiyongZHAO
źródło