Bir yoldan dosya adını almanın en basit yolu nedir?
string filename = "C:\\MyDirectory\\MyFile.bat"
Bu örnekte "MyFile" almalıyım. uzatma olmadan.
Bir yoldan dosya adını almanın en basit yolu nedir?
string filename = "C:\\MyDirectory\\MyFile.bat"
Bu örnekte "MyFile" almalıyım. uzatma olmadan.
Yanıtlar:
_splitpath ihtiyacınız olanı yapmalıdır. Elbette manuel olarak da yapabilirsiniz, ancak _splitpath
tüm özel durumları da ele alır.
DÜZENLE:
BillHoag'ın belirttiği gibi, mevcut olduğunda _splitpath_s_splitpath
adlı daha güvenli sürümün kullanılması önerilir .
Veya taşınabilir bir şey istiyorsanız, bunun gibi bir şey yapabilirsiniz
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;
_splitpath
.
<stdlib.h>
. Taşınabilirliğe gelince, belki “mükemmel derecede iyi taşınabilir çözüm” lerden bazı örnekleri listeleyebilirsiniz?
<stdlib.h>
. Ve en belirgin taşınabilir çözüm şudur boost::filesystem
.
_splitpath
içinde stdlib.h
VS Kopyanızın? O zaman VS'nin onarım kurulumunu yapmak isteyebilirsiniz.
Olası bir çözüm:
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);
}
Temel dosya adı dizenin klasörler için son sınırlamadan başlayan kısmı olduğundan, görev oldukça basittir:
std::string base_filename = path.substr(path.find_last_of("/\\") + 1)
Uzantı da kaldırılacaksa, yapılacak tek şey sonuncuyu bulmak .
ve substr
bu noktaya gitmek.
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
Belki de yalnızca uzantılardan oluşan dosyalarla başa çıkmak için bir kontrol yapılmalıdır (ör .bashrc
.
Bunu ayrı işlevlere ayırırsanız, tek görevleri yeniden kullanma esnekliğine sahip olursunuz:
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;
}
Kod, farklı std::basic_string
örneklerle (yani std::string
& std::wstring
...) kullanabilmek için şablon haline getirilmiştir.
Şablonun dezavantajı const char *
, işlevlere bir geçilirse şablon parametresini belirtme gereksinimidir .
Yani şunlardan birini yapabilirsiniz:
std::string
Kodu şablonlamak yerine yalnızca kullanınstd::string base_name(std::string const & path)
{
return path.substr(path.find_last_of("/\\") + 1);
}
std::string
(muhtemelen satır içi / optimize edilecek ara ürünler olarak)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;
Baskılar
MyFile
base_name
Önce uygulayın .)
En basit çözüm, gibi bir şey kullanmaktır boost::filesystem
. Herhangi bir nedenle bu bir seçenek değilse ...
Bunu doğru bir şekilde yapmak, sisteme bağlı bazı kodlar gerektirecektir: Windows altında, bir yol ayırıcı ya '\\'
da '/'
olabilir; Unix altında, yalnızca '/'
çalışır ve diğer sistemlerde kim bilir. Bariz çözüm şunun gibi bir şey olacaktır:
std::string
basename( std::string const& pathname )
{
return std::string(
std::find_if( pathname.rbegin(), pathname.rend(),
MatchPathSeparator() ).base(),
pathname.end() );
}
, MatchPathSeparator
sisteme bağlı bir başlıkta şu şekilde tanımlanır:
struct MatchPathSeparator
{
bool operator()( char ch ) const
{
return ch == '/';
}
};
Unix için veya:
struct MatchPathSeparator
{
bool operator()( char ch ) const
{
return ch == '\\' || ch == '/';
}
};
Windows için (veya başka bir bilinmeyen sistem için hala farklı bir şey).
DÜZENLEME: Uzantıyı da bastırmak istediği gerçeğini kaçırdım. Bunun için aynı şeylerden daha fazlası:
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 );
}
Kod biraz daha karmaşıktır, çünkü bu durumda ters yineleyicinin tabanı kesmek istediğimiz yerin yanlış tarafındadır. (Bir ters yineleyicinin tabanının yineleyicinin işaret ettiği karakterin arkasında olduğunu unutmayın.) Ve bu bile biraz şüpheli: Örneğin boş bir dizge döndürebilmesi gerçeğinden hoşlanmıyorum. (Eğer '.'
dosya adının ilk karakteri tek ise, tam dosya adını döndürmeniz gerektiğini savunurum. Bu, özel durumu yakalamak için biraz fazladan kod gerektirir.)}
string::find_last_of
Ters yineleyicileri kullanmak yerine kullanmaya ne dersiniz ?
string
, bu yüzden onları yine de öğrenmelisiniz. Ve onları öğrendikten sonra, tüm şişirilmiş arayüzü öğrenmeye zahmet etmenize gerek yok std::string
.
C ++ 17'nin en basit yolu şudur:
uzantısı ile ve uzantısız dosya adı için #include <filesystem>
ve kullanın .filename()
stem()
#include <iostream>
#include <filesystem>
namespace fs = std::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';
}
çıktı:
MyFile.bat
MyFile
"bar.txt"
".bar"
"."
"."
".."
"."
".."
"/"
Referans: cppreference
PathFindFileName, PathRemoveExtension kabuk Yol API'lerini de kullanabilirsiniz. Muhtemelen bu özel sorun için _splitpath'den daha kötüdür, ancak bu API'ler her türlü yol ayrıştırma işi için çok kullanışlıdır ve UNC yollarını, eğik çizgileri ve diğer garip şeyleri hesaba katarlar.
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
Dezavantajı, shlwapi.lib'e bağlanmanız gerekmesidir, ancak bunun neden bir dezavantaj olduğundan emin değilim.
Boost kullanabiliyorsanız,
#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();
Hepsi bu.
Yükseltme kitaplığını kullanmanızı tavsiye ederim. Boost, C ++ ile çalışırken size birçok kolaylık sağlar. Hemen hemen tüm platformları destekler. Ubuntu kullanıyorsanız, artırma kitaplığını yalnızca bir satırla yükleyebilirsiniz sudo apt-get install libboost-all-dev
(ref. Ubuntu'ya yükseltme nasıl kurulur ? )
İşlev:
#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);
}
Testler:
#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 ++ Belgelerinden - 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;
}
Çıktılar:
Splitting: /usr/bin/man
path: /usr/bin
file: man
Splitting: c:\windows\winhelp.exe
path: c:\windows
file: winhelp.exe
find_last_of
döneceğini unutmayın (ve halletmeyi) string::npos
.
string::npos
bunun nasıl string::substr
uygulandığı ve uygulandığı için kontrolün yapılması gerekmiyor . a) string::npos
"uzunluk" olarak geçmiştir => substr
sonuna kadar tümünü okuma davranışını belgelemiştir. b) substr
"verilir string::npos + 1
:" ve uzunluk string::npos
bir değere sahip belgelenmiştir -1
için değerlendirir böylece, 0
için => dize başlar ve uzunlukları varsayılan değer substr
olan npos
çok sadece dosya adı 'konulu => eserler' cplusplus.com/reference / string / string / substr cplusplus.com/reference/string/string/npos
Tek tip başlatma ve anonim satır içi lambda ile C ++ 11 varyantı (James Kanze'nin sürümünden esinlenmiştir).
std::string basename(const std::string& pathname)
{
return {std::find_if(pathname.rbegin(), pathname.rend(),
[](char c) { return c == '/'; }).base(),
pathname.end()};
}
Ancak dosya uzantısını kaldırmaz.
return c == '/' || c == '\\';
pencerelerde çalışması için her zaman değiştirebilirsiniz
if (pathname.size() == 0) return "."; auto iter = pathname.rbegin(); auto rend = pathname.rend(); while (iter != rend && *iter == '/') ++iter; if (iter == rend) /* pathname has only path separators */ return "/"; pathname = std::string(pathname.begin(), iter.base());
boost
filesystem
Kütüphane olarak da kullanılabilir experimental/filesystem
kütüphane ve C ++ 17 ++ için ISO C içine birleştirilmiştir. Bunu şu şekilde kullanabilirsiniz:
#include <iostream>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
int main () {
std::cout << fs::path("/foo/bar.txt").filename() << '\n'
}
Çıktı:
"bar.txt"
Aynı zamanda std::string
nesneler için de işe yarar.
nihayet benim için işe yarayan tek şey bu:
#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'nin önerdiği ancak wchar_t *, VS Enterprise 2015 ile çalışmadığı gibi
_splitpath de çalıştı, ancak kaç tane karakter [?] karaktere ihtiyacım olacağını tahmin etmekten hoşlanmıyorum; Bazı insanların muhtemelen bu kontrole ihtiyacı var, sanırım.
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 için eklemeye gerek olduğuna inanmıyorum. Bu çözümlerden herhangi biri için harici kitaplıklara (yükseltme gibi) gerek yoktu.
Ben yapardım ...
İlk ters eğik çizgiyi / eğik çizgiyi bulana kadar dizenin sonundan geriye doğru arama yapın.
Ardından, ilk noktayı (.) Bulana kadar dizenin sonundan geriye doğru tekrar arama yapın.
Daha sonra dosya adının başlangıcına ve sonuna sahip olursunuz.
Basit ...
'\\'
Yol ayırıcı olarak kabul eden tek sistem de kullanır '/'
, bu yüzden ikisini de eşleştirmeniz gerekir.) Ve neyi dört gözle beklediğinizden emin değilim.
my.source.cpp
derlenir my.source.obj
(uzantısı ile .cpp
değiştirilir .obj
).
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 değişkeni fileName'i içerir.
boost::filesystem::path( path ).filename()
.
Dont kullanımı _splitpath()
ve _wsplitpath()
. Güvenli değiller ve modası geçmişler!
Bunun yerine, güvenli sürümlerini kullanın, yani _splitpath_s()
ve_wsplitpath_s()
Bu da işe yaramalı:
// 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("."));
}
Kullanabiliyorsanız, Qt, dosyaları, dosya adlarını ve dizinleri işlemek için QString (split, trim vb. İle), QFile, QPath, QFileInfo vb. Sağlar. Ve tabii ki aynı zamanda çapraz fırtına.
getFilename
veya buna benzer bir şey içine alın ).
Bunu oldukça güzel yapmak için std :: dosya sistemini kullanabilirsiniz:
#include <filesystem>
namespace fs = std::experimental::filesystem;
fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();
Uzun zamandır dosya yolunu düzgün şekilde ayrıştırabilen bir işlev arıyordum. Benim için bu kod hem Linux hem de Windows için mükemmel çalışıyor.
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);
}
Örnek sonuçlar:
[]
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: ''
Umarım bu size de yardımcı olur :)
shlwapi.lib/dll
HKCU
kayıt defteri kovanını dahili olarak kullanır .
shlwapi.lib
Bir kitaplık oluşturuyorsanız veya ürünün bir kullanıcı arayüzü yoksa, bağlantı vermemek en iyisidir . Bir kitaplık yazıyorsanız, kodunuz kullanıcı arabirimi olmayanlar da dahil olmak üzere herhangi bir projede kullanılabilir.
Bir kullanıcı oturum açmadığında çalışan bir kod yazıyorsanız (örneğin, hizmet [veya diğer] önyükleme veya başlangıçta başlayacak şekilde ayarlanmışsa), o zaman yok HKCU
. Son olarak, shlwapi yerleşim işlevleridir; ve sonuç olarak, Windows'un sonraki sürümlerinde kullanımdan kaldırılacak listede üst sıralarda.
İhtiyaçlarınızı karşılayabilecek bir işlev uyguladım. String_view'un compile zamanında hesaplanabilen find_last_of (c ++ 17'den beri) constexpr işlevine dayanmaktadır.
constexpr const char* base_filename(const char* p) {
const size_t i = std::string_view(p).find_last_of('/');
return std::string_view::npos == i ? p : p + i + 1 ;
}
//in the file you used this function
base_filename(__FILE__);