パスからファイル名を取得する
パスからファイル名を取得する最も簡単な方法は何ですか?
string filename = "C:\\MyDirectory\\MyFile.bat"
この例では、「MyFile」を取得する必要があります。拡張なし。
_ splitpath は必要なことを行う必要があります。もちろん手動で行うこともできますが、_splitpath
はすべての特殊なケースも処理します。
編集:
BillHoagが述べたように、利用可能であれば _ splitpath_s と呼ばれる_splitpath
のより安全なバージョンを使用することをお勧めします。
または、ポータブルなものが必要な場合は、次のようにするだけです
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);
}
最も簡単な解決策は、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の場合(または他の未知のシステムではまだ異なるもの)。
編集:私も彼が拡張を抑制したいという事実を見逃した。そのためには、より多くの同じ:
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 );
}
コードはもう少し複雑です。この場合、逆イテレータのベースはカットしたい場所の反対側にあるためです。 (逆イテレータのベースはイテレータが指す文字の後ろにあることに注意してください。)そして、これでも少し疑わしいです。たとえば、空の文字列を返すことができるという事実は好きではありません。 (唯一の'.'
がファイル名の最初の文字である場合、完全なファイル名を返す必要があると主張します。これには、特殊なケースをキャッチするために少し余分なコードが必要です。)}
基本ファイル名はフォルダーの最後のデリミタから始まる文字列の一部にすぎないため、タスクは非常に簡単です。
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
...)
テンプレートの欠点は、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
ブーストを使用できる場合、
#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は、C++で作業するときに多くの便利さを提供します。ほとんどすべてのプラットフォームをサポートしています。 Ubuntuを使用する場合、boostライブラリは1行でインストールできますSudo apt-get install libboost-all-dev
(ref。 buntuにboostをインストールする方法? )
シェルパス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.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 experimental/filesystemおよび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"
"."
"."
".."
"."
".."
"/"
参照: cppreference
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
均一な初期化と匿名インラインラムダを備えたC++ 11バリアント(James Kanzeのバージョンに触発された)。
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
ライブラリとしても利用でき、C++ 17用のISO C++にマージされました。次のように使用できます。
#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);
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にインクルードが必要だとは思わない。これらのソリューションのいずれにも、外部ライブラリ(ブーストなど)は必要ありませんでした。
私はそれをするだろう...
最初のバックスラッシュ/スラッシュが見つかるまで、文字列の末尾から後方に検索します。
次に、最初のドット(。)が見つかるまで、文字列の末尾から逆方向に再度検索します。
これで、ファイル名の始まりと終わりがわかります。
シンプル...
これも動作するはずです:
// 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()
を使用してください
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にはfileNameが含まれています。
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
レジストリハイブを内部的に使用します。
ライブラリを作成する場合、または製品にUIがない場合は、shlwapi.lib
にリンクしないことをお勧めします。ライブラリを作成している場合、UIを持たないプロジェクトを含む任意のプロジェクトでコードを使用できます。
ユーザーがログインしていないときに実行するコードを作成している場合(例:サービス[またはその他]が起動または起動時に開始するように設定されている場合)HKCU
はありません。最後に、shlwapiは決済機能です。その結果、リストの上位に位置し、後のバージョンのWindowsで廃止されます。
Std :: filesystemを使用すると、非常にうまくできます。
#include <filesystem>
namespace fs = std::experimental::filesystem;
fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();