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

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

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

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

Ответ 1

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

Ответ 2

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

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

Ответ 3

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

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

Возможно, должна быть проверка, чтобы справиться с файлами, состоящими исключительно из расширений (т.е. .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...)

Недостатком шаблона является требование указать параметр шаблона, если a 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

Ответ 4

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

Для правильного выполнения этого потребуется некоторый системный код: Windows, либо '\\', либо '/' может быть разделителем путей; под 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 );
}

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

Ответ 5

Если вы можете использовать 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 дает вам много удобств, когда вы работаете с С++. Он поддерживает практически все платформы. Если вы используете Ubuntu, вы можете установить библиотеку boost только одной строкой sudo apt-get install libboost-all-dev (ссылка Как установить boost на Ubuntu?)

Ответ 6

Вы также можете использовать API-интерфейс Path Path 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.lib, но я не уверен, почему этот недостаток.

Ответ 7

Функция:

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

Ответ 8

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

используйте #include экспериментальная/файловая система и 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

Ответ 9

Из документов С++ - строка:: 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

Ответ 10

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

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

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

Ответ 11

Библиотека boost filesystem также доступна как библиотека 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.

Ответ 12

это единственное, что на самом деле окончательно помогло мне:

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

в значительной степени то, что предложил Скрымсли, но не работает с 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).

Ответ 13

Я бы сделал это с помощью...

Искать назад от конца строки до тех пор, пока не найдете первую косую черту/косую черту.

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

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

Simples...

Ответ 14

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 содержит имя_файла.

Ответ 15

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

// 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 и т.д. для управления файлами, именами файлов и каталогами. И, конечно же, он также пересекает плоскостопие.

Ответ 16

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

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

Ответ 17

Кроме того, возможно, стоит посмотреть на регулярные выражения. Вы можете найти много информации здесь: http://www.regular-expressions.info/tutorial.html

Ответ 18

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

Ответ 19

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

Надеюсь, это тоже поможет вам:)

Ответ 20

shlwapi.lib/dll использует куст реестра HKCU для внутреннего использования.

Лучше не ссылаться на shlwapi.lib, если вы создаете библиотеку или продукт не имеет пользовательского интерфейса. Если вы пишете библиотеку, тогда ваш код может быть использован в любом проекте, включая те, которые не имеют пользовательского интерфейса.

Если вы пишете код, который выполняется, когда пользователь не вошел в систему (например, служба [или другое] настроена на запуск при загрузке или запуске), тогда HKCU нет. Наконец, шлвапи - это расчетные функции; и в результате высоко в списке, чтобы осудить в более поздних версиях Windows.

Ответ 21

Вы можете использовать std::filesystem для этого:

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

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