Получить имя файла из пути



какой самый простой способ получить имя файла из пути?



string filename = "C:MyDirectoryMyFile.bat"


в этом примере я должен получить "MyFile". без расширения.

813   17  

17 ответов:

_splitpath должны делать то, что вам нужно. Вы могли бы, конечно, сделать это вручную, но _splitpath обрабатывает все специальные случаи, а также.

EDIT:

как упоминал BillHoag рекомендуется использовать более безопасную версию _splitpath под названием раздел _splitpath_s при наличии.

или если вы хотите что-то портативное, вы можете просто сделать что-то вроде этого

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;

самое простое решение-использовать что-то вроде boost::filesystem. Если по какой-то причине это не вариант...

для правильного выполнения этого потребуется некоторый системный зависимый код: под Окна, либо '\' или '/' может быть разделителем пути; под Unix, только '/' работает и под другими системами, кто знает. Очевидное решение будет что-то вроде:

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

С MatchPathSeparator определяется в системно-зависимом заголовке как либо:

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

для Unix, или:

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

для Windows (или что-то еще другое для некоторых других неизвестных система.)

EDIT: я пропустил тот факт, что он также хотел подавить расширение. Для этого, больше того же:

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

код немного сложнее, потому что в этом случае база обратный итератор находится на неправильной стороне, где мы хотим вырезать. (Помните, что база обратного итератора одна за спиной символ, на который указывает итератор.) И даже это немного сомнительно: я не нравится тот факт, что он может возвращать пустую строку, например. (Если только '.' первый символ имени файла, я бы поспорил что вы должны вернуть полное имя файла. Для этого потребуется немного немного дополнительного кода, чтобы поймать особый случай.) }

возможное решение:

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

задача довольно проста, как основное имя-это только часть строки, начинающуюся в последний разделитель для папки:

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

если расширение должно быть удалено, а единственное, что нужно сделать, это найти последний . и забрать substr в этот момент

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

возможно, должна быть проверка, чтобы справиться с файлами, состоящими исключительно из расширений (ie .bashrc...)

если вы разделите это на отдельные функции, вы будете гибки повторное использование отдельных задач:

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

код шаблона, чтобы иметь возможность использовать его с различными std::basic_string экземпляры (т. е. std::string & std::wstring...)

недостатком шаблона является требование указать параметр шаблона, если const char * передается в функции.

так что вы могли бы либо:

A) использовать только std::string вместо шаблона кода

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

B) обеспечить функцию упаковки с помощью std::string (в качестве промежуточных звеньев, которые, вероятно, будут встроены / оптимизированы)

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

C) укажите параметр шаблона при вызове с помощью 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;

печать

MyFile

вы также можете использовать API пути оболочки PathFindFileName, PathRemoveExtension. Вероятно, хуже, чем _splitpath для этой конкретной проблемы, но эти API очень полезны для всех видов заданий синтаксического анализа пути, и они учитывают UNC-пути, косые черты и другие странные вещи.

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

недостатком является то, что вы должны связать с shlwapi.Либ, а я нет действительно уверен, почему это недостаток.

Если вы можете использовать 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();

Это все.

Я рекомендую вам использовать библиотеку Boost. Boost дает вам много удобств при работе с C++. Он поддерживает практически все платформы. Если вы используете Ubuntu, вы можете установить библиотеку boost только на одну строку sudo apt-get install libboost-all-dev (ref. Как установить boost на Ubuntu?)

:

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

тесты:

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

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

выходы:

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

самый простой способ в cpp17:

используйте #include experimental/filesystem и filename () для filename с расширением и stem () без расширения.

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

выход:

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

Ref:cppreference

вариант C++11 (вдохновленный версией Джеймса Канзе) с равномерной инициализацией и анонимной встроенной лямбдой.

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

он не удаляет расширение файла, хотя.

это единственное, что на самом деле, наконец, работал для меня:

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

в значительной степени то, что предложил Skrymsli, но не работает с wchar_t*, VS Enterprise 2015

_splitpath работал также, но мне не нравится гадать, сколько char[?] персонажи, которые мне понадобятся; некоторые люди, вероятно, нуждаются в этом контроле, я думаю.

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

Я не считаю, что какие-либо включает были необходимы для _splitpath. Никакие внешние библиотеки (например, boost) не были необходимы для любое из этих решений.

Я бы сделал это сам...

Поиск назад от конца строки, пока не найдете первый слеш/Слэш.

затем снова выполните поиск в обратном направлении от конца строки, пока не найдете первую точку (.)

тогда у вас есть начало и конец имени файла.

дурочка...

The boostfilesystem библиотека также доступна как experimental/filesystem библиотека и была объединена в ISO C++ для C++17. Вы можете использовать его следующим образом:

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

namespace fs = std::experimental::filesystem;

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

выход:

"bar.txt"

он также работает для std::string объекты.

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

переменная m_szFileName содержит имя файла.

Не используйте _splitpath() и _wsplitpath(). Они небезопасны, и они устарели!

вместо этого используйте их безопасные версии, а именно _splitpath_s() и _wsplitpath_s()

Это должно работать :

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

Если вы можете использовать его, Qt предоставляет QString (с разделением, обрезкой и т. д.), QFile, QPath, QFileInfo и т. д. Для управления файлами, именами файлов и каталогами. И, конечно, это также крест plaftorm.

долгое время я искал функцию, способную правильно разложить путь к файлу. Для меня этот код прекрасно работает на Linux и 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);
}

пример результатов:

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

Я надеюсь, что это поможет вам также :)

Comments

    Ничего не найдено.