如何从路径中获取文件名的扩展名?

3
我想从const char*文件路径中提取出一个const char*文件名。我尝试使用正则表达式,但失败了:
const char* currentLoadedFile = "D:\files\file.lua";
char fileName[256];
if (sscanf(currentLoadedFile, "%*[^\\]\\%[^.].lua", fileName)) {
return (const char*)fileName; // WILL RETURN "D:\files\file!!
}

问题在于返回的是"D:\files\file"而不是所需的"file"(注意:没有".lua")。

3
[s|f]scanf 无法执行正则表达式匹配 - 您需要自己编写程序来实现它(例如使用 strtok)。 - dialer
就像dialer所说的那样,但我也不理解你的正则表达式。匹配文件名应该是类似于"\\/$"(非常宽松且不安全于用户输入)。 - OlivierD
找到最后一个反斜杠(从末尾开始的第一个),它后面的内容就是文件名。 - Marius Bancila
这个问题部分重复了这个问题。你只需要从文件名中去掉.lua扩展名就可以得到词干。 - razlebe
@BatchyX - 问题的正则表达式元素使其成为部分重复。注意,是部分。我绝不会暗示C++或C#元素中存在重复-它们是完全不同的语言。两个问题都可以通过说“使用以下正则表达式:...”来回答,这是与语言无关的。现有的答案没有做到这一点并不影响这两个问题的部分重复性质。我的评论清楚吗? - razlebe
显示剩余2条评论
8个回答

15

只需使用boost::filesystem即可。

#include <boost/filesystem.hpp>

std::string filename_noext;
filename_noext = boost::filesystem::path("D:\\files\\file.lua").stem().string().
const char* result_as_const_char = filename_noext.c_str();

或者,如果你想自己引入错误:

// have fun defining that to the separator of the target OS.
#define PLATFORM_DIRECTORY_SEPARATOR '\\'

// the following code is guaranteed to have bugs.
std::string input = "D:\\files\\file.lua";
std::string::size_type filename_begin = input.find_last_of(PLATFORM_DIRECTORY_SEPERATOR);
if (filename_begin == std::string::npos)
    filename_begin = 0;
else
    filename_begin++;
std::string::size_type filename_length = input.find_last_of('.');
if (filename_length != std::string::npos)
    filename_length = filename_length - filename_begin;

std::string result = input.substr(filename_begin, filename_length);

const char* bugy_result_as_const_char = result.c_str();

4
为什么?正则表达式不是标准 C++ 的一部分,因此您必须使用非标准函数。重写现有的良好且免费的代码是浪费大家的时间。良好的编程是善用资源。 - mmmmmm
1
@luac:boost::filesystem 是 TR2 计划中的新增内容。 - Yakov Galka
2
@luac 如果你不能使用Boost,我可以假设这是一道作业题吗? :P - Gavin Anderegg
1
@Gavin Anderegg :) 我无法将包含文件添加到我正在修改的现有项目中。 - luac
1
@luac,是什么阻止了您添加包含文件,但允许您添加代码——这似乎是一个非常人为的限制。如果您需要一个新类,会发生什么? - mmmmmm
显示剩余5条评论

15

那使用 std::string 呢? 例如:

  std::string path("d:\\dir\\subdir\\file.ext");
  std::string filename;

  size_t pos = path.find_last_of("\\");
  if(pos != std::string::npos)
    filename.assign(path.begin() + pos + 1, path.end());
  else
    filename = path;

2
这将返回"file.ext",而操作者想要返回"file"。此外,该方法在UNIX上不可行。 - BatchyX

2
你可以使用C++17中的新文件系统库,轻松地实现可移植的操作。
#include <cstdint>
#include <cstdio>
#include <filesystem>

int main()
{
    std::filesystem::path my_path("D:/files/file.lua");
    std::printf("filename: %s\n", my_path.filename().u8string().c_str());
    std::printf("stem: %s\n", my_path.stem().u8string().c_str());
    std::printf("extension: %s\n", my_path.extension().u8string().c_str());
}

输出:

filename: file.lua
stem: file
extension: .lua

请注意,目前你可能需要同时使用#include <experimental/filesystem>std::experimental::filesystem,直到标准库完全符合要求为止。
有关std::filesystem更多的文档,请查看文件系统库参考资料

1

你可以轻松地提取文件:

int main()
{
    char pscL_Dir[]="/home/srfuser/kush/folder/kushvendra.txt";
    char pscL_FileName[50];
    char pscL_FilePath[100];
    char *pscL;
    pscL=strrchr(pscL_Dir,'/');
    if(pscL==NULL)
        printf("\n ERROR :INvalid DIr");
    else
    {
        strncpy(pscL_FilePath,pscL_Dir,(pscL-pscL_Dir));
        strcpy(pscL_FileName,pscL+1);
        printf("LENTH [%d}\n pscL_FilePath[%s]\n pscL_FileName[%s]",(pscL-pscL_Dir),pscL_FilePath,pscL_FileName);
    }
    return 0;
}


output: 
LENTH [25}
 pscL_FilePath[/home/srfuser/kush/folder]
 pscL_FileName[kushvendra.txt

你好 Kushvendra,请在你的个人资料页面中添加你的电子邮件地址。 - eeerahul
@kushvendra - 在评论区或聊天中讨论你的答案是合适的地方。 - razlebe

0
// Set short name:
char *Filename;
Filename = strrchr(svFilename, '\\');
if ( Filename == NULL )
    Filename = svFilename;

if ( Filename[0] == '\\')
    ++Filename;
if ( !lstrlen(Filename) )
{
    Filename = svFilename;
}
fprintf( m_FileOutput, ";\n; %s\n;\n", Filename );

0

这里可以找到一个例子。我不是说它是最好的,我相信你可以改进它,但它只使用标准的C++(至少现在被认为是标准的)。当然,你不会拥有boost::filesystem的功能(例子中的函数只与普通字符串一起使用,并不保证/检查你实际上正在使用真正的文件系统路径)。


0
如果您要在Windows上向用户“显示”文件名,应该尊重其外壳设置(显示/隐藏扩展名等)。
通过使用“SHGFI_DISPLAYNAME”标志调用SHGetFileInfo,可以获取正确格式的文件名。

0
你可以使用_splitpath_s函数将路径名拆分为其组成部分。我不知道这是标准C还是Windows特定的。无论如何,以下是该函数:
#include <stdlib.h>
#include <string>

using std::string;

bool splitPath(string const &path, string &drive, string &directory, string &filename, string &extension) {
    // validate path
    drive.resize(_MAX_DRIVE);
    directory.resize(_MAX_DIR);
    filename.resize(_MAX_FNAME);
    extension.resize(_MAX_EXT);

    errno_t result;
    result = _splitpath_s(path.c_str(), &drive[0], drive.size(), &directory[0], directory.size(), &filename[0], filename.size(), &extension[0], extension.size()); 
    //_splitpath(path.c_str(), &drive[0], &directory[0], &filename[0], &extension[0]); //WindowsXp compatibility
    _get_errno(&result);
    if (result != 0) {
        return false;
    } else {
        //delete the blank spaces at the end
        drive = drive.c_str();
        directory = directory.c_str();
        filename = filename.c_str();
        extension = extension.c_str();
        return true;
    }
}

使用 std::string 更加容易和安全,但你也可以修改为使用 TCHAR*wcharchar)...

针对你的特定情况:

int main(int argc, char *argv[]) {
    string path = argv[0];
    string drive, directory, filename, extension;
    splitPath(path, drive, directory, filename, extension);
    printf("FILE = %s%s", filename.c_str(), extension.c_str());
    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接