web-dev-qa-db-ja.com

パスからファイル名を取得する

パスからファイル名を取得する最も簡単な方法は何ですか?

string filename = "C:\\MyDirectory\\MyFile.bat"

この例では、「MyFile」を取得する必要があります。拡張なし。

70
nidhal

_ 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;
26
Anders

可能な解決策:

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);
}
50
hmjd

最も簡単な解決策は、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 );
}

コードはもう少し複雑です。この場合、逆イテレータのベースはカットしたい場所の反対側にあるためです。 (逆イテレータのベースはイテレータが指す文字の後ろにあることに注意してください。)そして、これでも少し疑わしいです。たとえば、空の文字列を返すことができるという事実は好きではありません。 (唯一の'.'がファイル名の最初の文字である場合、完全なファイル名を返す必要があると主張します。これには、特殊なケースをキャッチするために少し余分なコードが必要です。)}

38
James Kanze

基本ファイル名はフォルダーの最後のデリミタから始まる文字列の一部にすぎないため、タスクは非常に簡単です。

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::stringstd::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
37
Pixelchemist

ブーストを使用できる場合、

#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をインストールする方法?

13
plhn

シェルパス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にリンクする必要があることですが、なぜそれが欠点なのかはよくわかりません。

12
Skrymsli

関数:

#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");
}
10
Rian Quinn

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

8
eliasetm

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
7
jave.web

均一な初期化と匿名インラインラムダを備えた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()};
}

ただし、ファイル拡張子は削除されません。

5
alveko

boostfilesystemライブラリは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オブジェクトに対しても機能します。

5
Adam Erickson

これは実際に私のために実際に働いた唯一のものです:

#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にインクルードが必要だとは思わない。これらのソリューションのいずれにも、外部ライブラリ(ブーストなど)は必要ありませんでした。

4
Fractal

私はそれをするだろう...

最初のバックスラッシュ/スラッシュが見つかるまで、文字列の末尾から後方に検索します。

次に、最初のドット(。)が見つかるまで、文字列の末尾から逆方向に再度検索します。

これで、ファイル名の始まりと終わりがわかります。

シンプル...

3
TomP89

これも動作するはずです:

// 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などを提供して、ファイル、ファイル名、ディレクトリを操作します。そしてもちろん、それはまた十字架です。

2
typedef

_splitpath()および_wsplitpath()を使用しないでください。彼らは安全ではなく、時代遅れです!

代わりに、安全なバージョン、つまり _splitpath_s() および _wsplitpath_s() を使用してください

2
hkBattousai
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が含まれています。

2
Lucian
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);
}
1
X Stylish

長い間、ファイルパスを適切に分解できる機能を探していました。私にとって、このコードは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:  ''

これがあなたにも役立つことを願っています:)

0
no one special

shlwapi.lib/dllHKCUレジストリハイブを内部的に使用します。

ライブラリを作成する場合、または製品にUIがない場合は、shlwapi.libにリンクしないことをお勧めします。ライブラリを作成している場合、UIを持たないプロジェクトを含む任意のプロジェクトでコードを使用できます。

ユーザーがログインしていないときに実行するコードを作成している場合(例:サービス[またはその他]が起動または起動時に開始するように設定されている場合)HKCUはありません。最後に、shlwapiは決済機能です。その結果、リストの上位に位置し、後のバージョンのWindowsで廃止されます。

0
Brook

Std :: filesystemを使用すると、非常にうまくできます。

#include <filesystem>
namespace fs = std::experimental::filesystem;

fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();
0
GuidedHacking