POSIX 或 Linux API 函数从路径获取文件扩展名

Posted

技术标签:

【中文标题】POSIX 或 Linux API 函数从路径获取文件扩展名【英文标题】:POSIX or Linux API function to get file extension from path 【发布时间】:2013-08-27 13:22:50 【问题描述】:

我需要一个 POSIX 或 Linux API 函数,它接受文件路径并返回此文件的扩展名。每个平台都应该有一个,但对于 Linux,我不能。它叫什么?

【问题讨论】:

文件扩展名在 unix 世界中没有什么意义 使用boost::filesystem 的更便携的解决方案不是更可取吗? @juanchopanza:如果我已经使用了 boost,可能是这样,但我不会只为这个功能包含它。在 Windows 上我有 WinAPI 函数,在 Mac 上我有 Cocoa 函数。 【参考方案1】:

首先使用strrchr 查找路径名中的最后一个'.'。如果它不存在,则没有“扩展”。

接下来,使用strchr检查最后一个'.'之后是否有'/'。如果是这样,则最后一个 '.' 位于目录组件中,而不是文件名中,因此没有扩展名。

否则,您找到了扩展程序。您可以将指向'.' 之后位置的指针直接用作C 字符串。无需将其复制到新存储中,除非原始字符串在您使用之前会被释放或破坏。

注意:以上假设您将“扩展”定义为仅最后一个'.'-delimited 组件。如果您想将 .tar.gz.cpp.bak 之类的东西视为扩展,则可以使用稍微不同的方法:

首先,使用strrchr 找到最终的'/'。如果未找到,则将字符串的开头视为结果。

其次,使用strchr从刚刚找到的位置开始,找到第一个'.'。结果就是你的扩展。

【讨论】:

-1 因为std::string::find 存在。无需在 C++ 中使用可能不安全的 C 风格函数。 以只读方式使用的任何函数都没有“不安全”的地方。此外,OP 在 C 和 C++ 中都标记了这个问题,因此在这两种语言中都可以使用的答案是唯一合适的答案。 错过了C 标签。删除了反对票。尽管如此,如果用户想要获得多个扩展名(在.cpp.bak 的情况下)并且文件名以. 开头,这仍然不起作用。此外,如果用户使用的是 C++,那么使用 C 风格的函数是不习惯的。 好的,我为您的扩展定义添加了一个替代版本。 @R..:公平点,C 标签已删除。感谢您指出我的问题标题表述不佳。【参考方案2】:

我认为这没有默认功能。

在我的文件系统库中,我只是应用字符串操作。

首先,我从完整路径中获取带有扩展名的文件名,寻找/ 分隔符并提取最后一个之后的所有内容。然后,我抓取第一个 . 点字符之后的所有内容,包括点本身。到目前为止效果很好。

请记住,某些系统文件可以以 . 点字符开头 - 因此在提取扩展名之前检查文件名是否以点字符开头。


算法

    通过从左侧删除文件夹名称从完整路径获取文件名: /home/test/.myfile.cpp.bak -> /test/.myfile.cpp.bak -> /.myfile.cpp.bak -> .myfile.cpp.bak 检查文件名是否以.开头: 如果有,请将其从当前文件名中删除 .myfile.cpp.bak -> myfile.cpp.bak 现在,从左侧提取您遇到的第一个 . 之后的所有内容(如果您想要多个扩展名) - 否则,从左侧提取最后一个 . 之后的所有内容 myfile.cpp.bak -> .cpp.bak(第一种情况) myfile.cpp.bak -> .bak(第二种情况)

【讨论】:

如果它是一个以点开头的系统文件?当然,我可以手动完成,并且它适用于大多数情况。但不是全部。 这太复杂了。 @VittorioRomeo:“查找扩展名”不需要分离目录和文件名组件。这些是完全独立的操作,当您想要的是扩展时,它们不应该发挥作用。 如果你想使用“扩展”的定义(注意:“扩展”没有以任何标准方式定义)你可以strrchr首先找到最后一个'/',然后搜索第一个'.'。制作多个临时字符串绝对没有任何意义,这既低效又(更糟糕)可能会失败,必须检查正确的代码,这会使您的代码更加复杂和容易出错。跨度> R..:好点。如果用户想要做的只是获取文件的扩展名,那么您的答案是更合适的。【参考方案3】:

包括文件系统的提升有点太多了。但随着 boost 实现达到 TR2 并在 Visual Studio 中实现,也许是时候开始研究它了。http://cpprocks.com/introduction-to-tr2-filesystem-library-in-vs2012/http://msdn.microsoft.com/en-us/library/hh874694.aspx

【讨论】:

是的,我读过。如果即使 Microsoft 执行文件系统的 TR2 实现,也几乎可以肯定该实现将达到标准化。所以我们今天可以使用它,并且有一种已经是可移植的方式,也许是标准的方式来处理文件和路径。【参考方案4】:

在我看来,解决这个问题的最佳方法(在没有 API 函数的情况下,这本身很奇怪)是将 Vittorio 和 R. 的答案与 basename 函数结合起来,该函数采用路径并返回文件名,如果路径指向一个文件:http://linux.die.net/man/3/basename

我还使用mbstowcs 将结果字符串转换为UTF-16,并使用std::wstring 进行所有查找:

std::wstring fileExtFromPath (const char * path)

   const char * fileName = basename(filePath);
   wchar_t buffer [MAX_PATH] = 0; // Use mblen if you don't like MAX_PATH
   const std::wstring fileNameW (buffer);
   const size_t pointPosition = fileNameW.rfind(L".");
   const std::wstring fileExtW = pointPosition == 0 ? std::wstring() : fileNameW.substr( + 1);
   return fileExtW;

【讨论】:

以上是关于POSIX 或 Linux API 函数从路径获取文件扩展名的主要内容,如果未能解决你的问题,请参考以下文章

如何在linux中使用POSIX API发送带有消息队列的整数?

Python os模块--路径文件系统命令等操作

如何在 Linux / POSIX 中获取任意时区的信息?

system v和posix的共享内存对比

Linux时间子系统 POSIX timer

POSIX 消息队列的替代方案