Каков самый простой способ получить имя файла из пути?
string filename = "C:\\MyDirectory\\MyFile.bat"
В этом примере я должен получить "MyFile". без расширения.
Каков самый простой способ получить имя файла из пути?
string filename = "C:\\MyDirectory\\MyFile.bat"
В этом примере я должен получить "MyFile". без расширения.
_ 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;
Возможное решение:
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);
Возможно, должна быть проверка, чтобы справиться с файлами, состоящими исключительно из расширений (т.е. .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 *
передается функциям.
Итак, вы можете:
std::string
вместо шаблонов кодаstd::string base_name(std::string const & path)
{
return path.substr(path.find_last_of("/\\") + 1);
}
std::string
(в качестве промежуточных элементов, которые, вероятно, будут отложены/оптимизированы)inline std::string string_base_name(std::string const & path)
{
return base_name(path);
}
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
Самое простое решение - использовать что-то вроде 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 );
}
Код немного сложнее, потому что в этом случае база
обратный итератор находится на той стороне, где мы хотим вырезать.
(Помните, что основание обратного итератора является одним за
характер, на который указывает итератор.) И даже это немного сомнительно: я
не нравится тот факт, что он может возвращать пустую строку, например.
(Если только '.'
является первым символом имени файла, я бы спорил
что вы должны вернуть полное имя файла. Для этого потребуется немного
бит дополнительного кода, чтобы поймать особый случай.) }
Если вы можете использовать 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?)
Вы также можете использовать 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, но я не уверен, почему этот недостаток.
Функция:
#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");
}
Самый простой способ в 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
Из документов С++ - строка:: 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
Вариант С++ 11 (вдохновленный версией Джеймса Канзе) с равномерной инициализацией и анонимной встроенной лямбдой.
std::string basename(const std::string& pathname)
{
return {std::find_if(pathname.rbegin(), pathname.rend(),
[](char c) { return c == '/'; }).base(),
pathname.end()};
}
Он не удаляет расширение файла, хотя.
Библиотека 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
.
это единственное, что на самом деле окончательно помогло мне:
#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).
Я бы сделал это с помощью...
Искать назад от конца строки до тех пор, пока не найдете первую косую черту/косую черту.
Затем повторите поиск назад с конца строки до тех пор, пока не найдете первую точку (.)
У вас есть начало и конец имени файла.
Simples...
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 содержит имя_файла.
Это тоже должно работать:
// 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 и т.д. для управления файлами, именами файлов и каталогами. И, конечно же, он также пересекает плоскостопие.
Не используйте _splitpath()
и _wsplitpath()
. Они небезопасны, и они устарели!
Вместо этого используйте свои безопасные версии, а именно _splitpath_s()
и _wsplitpath_s()
Кроме того, возможно, стоит посмотреть на регулярные выражения. Вы можете найти много информации здесь: http://www.regular-expressions.info/tutorial.html
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);
}
В течение длительного времени я искал функцию, способную правильно разложить путь к файлу. Для меня этот код отлично работает как для 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: ''
Надеюсь, это тоже поможет вам:)
shlwapi.lib/dll
использует куст реестра HKCU
для внутреннего использования.
Лучше не ссылаться на shlwapi.lib
, если вы создаете библиотеку или продукт не имеет пользовательского интерфейса. Если вы пишете библиотеку, тогда ваш код может быть использован в любом проекте, включая те, которые не имеют пользовательского интерфейса.
Если вы пишете код, который выполняется, когда пользователь не вошел в систему (например, служба [или другое] настроена на запуск при загрузке или запуске), тогда HKCU
нет. Наконец, шлвапи - это расчетные функции; и в результате высоко в списке, чтобы осудить в более поздних версиях Windows.
Вы можете использовать std::filesystem для этого:
#include <filesystem>
namespace fs = std::experimental::filesystem;
fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();