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