パスからファイル名を取得する最も簡単な方法は何ですか?
string filename = "C:\\MyDirectory\\MyFile.bat"
この例では、「MyFile」を取得する必要があります。拡張子なし。
パスからファイル名を取得する最も簡単な方法は何ですか?
string filename = "C:\\MyDirectory\\MyFile.bat"
この例では、「MyFile」を取得する必要があります。拡張子なし。
回答:
_splitpathは必要なことを実行する必要があります。もちろん手動で行うこともできますが、_splitpath
すべての特殊なケースも処理します。
編集:
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;
_splitpath
私のマシンのインクルードには何もありません。
<stdlib.h>
ます。移植性については、「完璧に優れたポータブルソリューション」の例をいくつか挙げることができますか?
<stdlib.h>
。そして、明らかなポータブルソリューションはboost::filesystem
です。
_splitpath
にstdlib.h
VSのコピーの?次に、VSの修復インストールを実行することをお勧めします。
考えられる解決策:
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
...)
テンプレーションの欠点は、次の場合にテンプレートパラメータを指定する必要があることです。 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
base_name
最初に適用してください。)
最も簡単な解決策は、のようなものを使用することです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 );
}
この場合、逆イテレータのベースがカットしたい場所の反対側にあるため、コードはもう少し複雑です。(逆イテレータのベースは、イテレータが指す文字の後ろにあることを忘れないでください。)そして、これでも少し疑わしいです。たとえば、空の文字列を返すことができるという事実は好きではありません。('.'
ファイル名の最初の文字のみである場合は、完全なファイル名を返す必要があると主張します。これには、特殊なケースをキャッチするために少し余分なコードが必要になります。)}
string::find_last_of
逆イテレータを操作する代わりに使用するのはどうですか?
string
になるため、とにかくそれらを学習する必要があります。そしてそれらを学んだので、への肥大化したインターフェースのすべてをわざわざ学ぶ理由はありませんstd::string
。
C ++ 17で最も簡単な方法は次のとおりです。
拡張子のあるファイル名と拡張子のないファイル名には、#include <filesystem>
と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';
}
出力:
MyFile.bat
MyFile
"bar.txt"
".bar"
"."
"."
".."
"."
".."
"/"
参照:cppreference
シェルパスAPIPathFindFileName、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 <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を使用している場合は、1行でBoostライブラリをインストールできますsudo apt-get install libboost-all-dev
(UbuntuにBoostをインストールする方法を参照してください)。
関数:
#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 ++ドキュメントから-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
find_last_of
返さstring::npos
れることを忘れないでください(そして処理すること)。
string::npos
これとstring::substr
実装方法のために、チェックを行う必要はありません。a)string::npos
「長さ」として渡されsubstr
ます=>最後まですべてを読み取る動作が文書化されています。b)substr
「string::npos + 1
」が指定され、長さstring::npos
がない:の値が文書化されている-1
ため、0
=>文字列の先頭に評価され、長さのデフォルト値substr
はnpos
=>「ファイル名だけ」でも機能し ますcplusplus.com/reference / string / string / substr cplusplus.com/reference/string/string/npos
均一な初期化と匿名のインラインラムダを備えた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()};
}
ただし、ファイル拡張子は削除されません。
return c == '/' || c == '\\';
に変更して、Windowsで機能するようにすることができます
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
ライブラリはライブラリとしても利用できexperimental/filesystem
、C ++ 17用のISOC ++に統合されました。次のように使用できます。
#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 Enterprise2015では機能しません
_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にインクルードが必要だったとは思いません。これらのソリューションのいずれにも、外部ライブラリ(ブーストなど)は必要ありませんでした。
私はそれをします...
最初のバックスラッシュ/フォワードスラッシュが見つかるまで、文字列の末尾から逆方向に検索します。
次に、最初のドット(。)が見つかるまで、文字列の末尾から逆方向に検索します。
これで、ファイル名の開始と終了がわかります。
シンプル...
'\\'
パス区切り文字として受け入れる1つのシステムもを使用する'/'
ため、どちらかを一致させる必要があります。)そして、何を期待しているのかわかりません。
my.source.cpp
コンパイルさmy.source.obj
れます(拡張子はに.cpp
置き換えられます.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にはfileNameが含まれています。
boost::filesystem::path( path ).filename()
。
とを使用_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(split、trimなど)、QFile、QPath、QFileInfoなどを提供して、ファイル、ファイル名、およびディレクトリを操作します。そしてもちろん、それはクロスプラフトルムでもあります。
getFilename
などにカプセル化してください)。
std :: filesystemを使用して、これを非常にうまく行うことができます。
#include <filesystem>
namespace fs = std::experimental::filesystem;
fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();
長い間、ファイルパスを適切に分解できる関数を探していました。私にとって、このコードは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
ライブラリを作成している場合、または製品にUIがない場合ないことをお勧めします。libを作成している場合、コードはUIを持たないプロジェクトを含むすべてのプロジェクトで使用できます。
ユーザーがログインしていないときに実行されるコードを記述している場合(たとえば、サービス[またはその他]が起動時または起動時に開始するように設定されている場合)、はありませんHKCU
。最後に、shlwapiは決済機能です。その結果、リストの上位にあり、それ以降のバージョンのWindowsでは非推奨になります。
私はあなたのニーズを満たすかもしれない機能を実装しました。これは、コンパイル時に計算できるstring_viewのconstexpr関数find_last_of(c ++ 17以降)に基づいています。
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__);