wake-up-neo.com

Holen Sie sich einen Dateinamen aus einem Pfad

Was ist der einfachste Weg, um den Dateinamen von einem Pfad zu bekommen? 

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

In diesem Beispiel sollte ich "MyFile" erhalten. ohne Verlängerung.

66
nidhal

_splitpath sollte das tun, was Sie brauchen. Sie können es natürlich manuell tun, aber _splitpath behandelt auch alle Sonderfälle.

BEARBEITEN:

Wie von BillHoag erwähnt, wird empfohlen, die sicherere Version von _splitpath mit dem Namen _splitpath_s zu verwenden, sofern verfügbar.

Oder wenn Sie etwas tragbares möchten, können Sie einfach so etwas tun

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;
26
Anders

Eine mögliche Lösung:

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

Die einfachste Lösung ist etwas wie boost::filesystem. Wenn .__ aus irgendeinem Grund ist dies keine Option ...

Wenn Sie dies richtig machen, ist ein systemabhängiger Code erforderlich: UnterWindows kann entweder '\\' oder '/' ein Pfadtrennzeichen sein; Unter Unix funktioniert nur '/' und unter anderen Systemen wer weiß. Die offensichtliche -Lösung wäre etwa so:

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

, wobei MatchPathSeparator in einem systemabhängigen Header als entweder definiert ist:

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

für Unix oder:

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

für Windows (oder etwas anderes für ein anderes unbekanntes System).

EDIT: Ich habe die Tatsache übersehen, dass er auch die Erweiterung unterdrücken wollte ...__ Dafür mehr davon:

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

Der Code ist etwas komplexer, da in diesem Fall die Basis von Der Reverse-Iterator auf der falschen Seite liegt, wo wir schneiden möchten ..__ (Denken Sie daran, dass die Basis eines Reverse-Iterators eine dahinter ist das -Zeichen, auf das der Iterator zeigt.) Und selbst das ist ein wenig zweifelhaft: Ich mag es nicht, dass er einen leeren String zurückgeben kann, zum Beispiel . (Wenn der einzige '.' der Als erstes Zeichen des Dateinamens würde ich argumentieren, dass Sie den vollständigen Dateinamen zurückgeben müssen. Dies würde etwas etwas zusätzlichen Code erfordern, um den Sonderfall abzufangen.) }

38
James Kanze

Die Aufgabe ist ziemlich einfach, da der Basisdateiname nur der Teil der Zeichenfolge ist, der am letzten Trennzeichen für Ordner beginnt:

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

Wenn auch die Erweiterung entfernt werden soll, müssen Sie nur den letzten . suchen und eine substr zu diesem Punkt bringen

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

Vielleicht sollte es eine Überprüfung geben, um mit Dateien fertig zu werden, die nur aus Erweiterungen bestehen (zB .bashrc...).

Wenn Sie dies in separate Funktionen aufteilen, können Sie die einzelnen Aufgaben flexibel wiederverwenden:

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

Der Code ist so angelegt, dass er mit verschiedenen std::basic_string-Instanzen verwendet werden kann (d. H. std::string & std::wstring...).

Der Nachteil der Vorlage ist die Anforderung, den Vorlagenparameter anzugeben, wenn ein const char * an die Funktionen übergeben wird.

Sie können also entweder:

A) Verwenden Sie nur std::string anstelle des Codes

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

B) Binden Sie die Wrapping-Funktion mit std::string ein (als Zwischenprodukte, die wahrscheinlich weggerissen/optimiert werden).

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

C) Geben Sie den Template-Parameter an, wenn Sie mit const char * aufrufen.

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

Ergebnis

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

Drucke

MyFile
34
Pixelchemist

Sie können auch die Shell Path APIs PathFindFileName PathRemoveExtension verwenden. Wahrscheinlich schlimmer als _splitpath für dieses spezielle Problem, aber diese APIs sind für alle Arten von Pfadanalyseoperationen sehr nützlich und berücksichtigen UNC-Pfade, Schrägstriche und andere seltsame Dinge. 

wstring filename = L"C:\\MyDirectory\\MyFile.bat";
wchar_t* filepart = PathFindFileName(filename.c_str());
PathRemoveExtension(filepart); 

http://msdn.Microsoft.com/de-de/library/windows/desktop/bb773589(v=vs.85).aspx

Der Nachteil ist, dass Sie auf shlwapi.lib verlinken müssen, aber ich bin mir nicht wirklich sicher, warum das ein Nachteil ist.

12
Skrymsli

Wenn Sie Boost verwenden können, 

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

Das ist alles.

Ich empfehle Ihnen die Boost-Bibliothek zu verwenden. Boost bietet Ihnen viele Vorteile, wenn Sie mit C++ arbeiten. Es unterstützt fast alle Plattformen. Wenn Sie Ubuntu verwenden, können Sie die Boost-Bibliothek nur über eine Zeile Sudo apt-get install libboost-all-dev (Ref. Wie installiert man Boost auf Ubuntu?

12
plhn

Funktion:

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

Tests:

#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");
}
10
Rian Quinn

Der einfachste Weg in cpp17 ist:

verwenden Sie #include experimental/filesystem und filename () für Dateinamen mit der Erweiterung und den Stamm () ohne Erweiterung.

   #include <iostream>
    #include <experimental/filesystem>
    namespace fs = std::experimental::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';
    }

ausgabe:

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

Ref: cppreference

7
eliasetm

Aus C++ - Dokumenten - 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;
}

Ausgänge:

Splitting: /usr/bin/man
 path: /usr/bin
 file: man
Splitting: c:\windows\winhelp.exe
 path: c:\windows
 file: winhelp.exe
7
jave.web

C++ 11-Variante (inspiriert von James Kanze's Version) mit einheitlicher Initialisierung und anonymem Inline-Lambda.

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

Die Dateierweiterung wird jedoch nicht entfernt.

5
alveko

Die Bibliothek boostfilesystem ist auch als experimental/filesystem-Bibliothek verfügbar und wurde in ISO C++ für C++ 17 zusammengeführt. Sie können es so verwenden:

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

namespace fs = std::experimental::filesystem;

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

Ausgabe:

"bar.txt"

Es funktioniert auch für std::string-Objekte.

4
Adam Erickson

das ist das Einzige, was tatsächlich für mich funktioniert hat:

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

ziemlich genau das, was Skrymsli vorschlug, funktioniert aber nicht mit wchar_t *, VS Enterprise 2015 

_splitpath hat auch funktioniert, aber ich mag es nicht zu raten, wie viele Zeichen [?] Zeichen ich brauche. Einige Leute brauchen wahrscheinlich diese Kontrolle, denke ich.

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

Ich glaube nicht, dass für _splitpath ein Include benötigt wurde. Für diese Lösungen waren keine externen Bibliotheken (wie Boost) erforderlich.

4
Fractal

Ich würde es tun, indem ...

Suchen Sie vom Ende der Zeichenfolge rückwärts, bis Sie den ersten Backslash/Schrägstrich gefunden haben.

Suchen Sie dann wieder rückwärts vom Ende der Zeichenfolge, bis Sie den ersten Punkt (.) Finden.

Sie haben dann den Anfang und das Ende des Dateinamens. 

Einfaches ...

3
TomP89

Das sollte auch funktionieren:

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

Wenn Sie es verwenden können, bietet Qt QString (mit Teilen, Trimmen usw.), QFile, QPath, QFileInfo usw. an, um Dateien, Dateinamen und Verzeichnisse zu bearbeiten. Und natürlich ist es auch Cross Plaftorm.

2
typedef

Verwenden Sie nicht _splitpath() und _wsplitpath(). Sie sind nicht sicher und sie sind veraltet!

Verwenden Sie stattdessen ihre sichere Version, nämlich _splitpath_s() und _wsplitpath_s()

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

Die Variable m_szFileName enthält den Dateinamen.

2
Lucian

Ich habe lange nach einer Funktion gesucht, die den Dateipfad richtig zerlegt. Für mich funktioniert dieser Code sowohl für Linux als auch für 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);
}

Beispielergebnisse sind:

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

Ich hoffe das hilft dir auch :)

0
no one special

shlwapi.lib/dll verwendet intern die HKCU-Registrierungsstruktur. 

Es empfiehlt sich, keine Verknüpfung zu shlwapi.lib herzustellen, wenn Sie eine Bibliothek erstellen oder das Produkt keine Benutzeroberfläche hat. Wenn Sie eine lib schreiben, kann Ihr Code in jedem Projekt verwendet werden, auch in solchen, die keine Benutzeroberflächen haben. 

Wenn Sie Code schreiben, der ausgeführt wird, wenn ein Benutzer nicht angemeldet ist (z. B. Dienst [oder ein anderer], der beim Start oder beim Start festgelegt ist), gibt es keine HKCU. Schließlich sind shlwapi Abwicklungsfunktionen; Infolgedessen wird die Liste in späteren Versionen von Windows als veraltet eingestuft. 

0
Brook

Sie können das std :: -Dateisystem verwenden, um es ganz schön zu machen:

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

fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();
0
GuidedHacking
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);
}
0
X Stylish