题
给定一个字符串 "filename.conf"
, ,如何验证扩展部分?
我需要一个跨平台解决方案。
解决方案
您必须确保使用多个点来处理文件名。例子: c:\.directoryname\file.name.with.too.many.dots.ext
将无法正确处理 strchr
或者 find.
我最喜欢的是 boost 文件系统库 具有扩展(路径)功能
其他提示
这个解决方案是不是太简单了?
#include <iostream>
#include <string>
int main()
{
std::string fn = "filename.conf";
if(fn.substr(fn.find_last_of(".") + 1) == "conf") {
std::cout << "Yes..." << std::endl;
} else {
std::cout << "No..." << std::endl;
}
}
最好的方法是不编写任何执行此操作的代码,而是调用现有方法。在 Windows 中, 路径查找扩展 方法可能是最简单的。
那么你为什么不自己写呢?
好吧,以 strrchr 为例,当您对以下字符串“c:\program files\AppleGate.Net eadme”使用该方法时会发生什么?扩展名是“.Net eadme”吗?编写适用于少数示例案例的代码很容易,但编写适用于所有案例的代码可能会困难得多。
假设您有权访问 STL:
std::string filename("filename.conf");
std::string::size_type idx;
idx = filename.rfind('.');
if(idx != std::string::npos)
{
std::string extension = filename.substr(idx+1);
}
else
{
// No extension found
}
编辑: 这是一个跨平台解决方案,因为您没有提到平台。如果您专门使用 Windows,您将需要利用线程中其他人提到的 Windows 特定功能。
其他人提到了 boost,但我只想添加实际的代码来执行此操作:
#include <boost/filesystem.hpp>
using std::string;
string texture = foo->GetTextureFilename();
string file_extension = boost::filesystem::extension(texture);
cout << "attempting load texture named " << texture
<< " whose extensions seems to be "
<< file_extension << endl;
// Use JPEG or PNG loader function, or report invalid extension
实际上STL不需要太多代码就可以做到这一点,我建议你了解一些STL,因为它可以让你做一些奇特的事情,无论如何这就是我使用的。
std::string GetFileExtension(const std::string& FileName)
{
if(FileName.find_last_of(".") != std::string::npos)
return FileName.substr(FileName.find_last_of(".")+1);
return "";
}
即使对于像“this.a.b.c.d.e.s.mp3”这样的字符串,此解决方案也将始终返回扩展名,如果找不到扩展名,它将返回“”。
其实最简单的方法就是
char* ext;
ext = strrchr(filename,'.')
要记住一件事:如果 '.'
文件名中不存在,ext 将是 NULL
.
我今天自己偶然发现了这个问题,尽管我已经有了一个工作代码,但我发现它在某些情况下不起作用。
虽然有些人已经建议使用一些外部库,但我更喜欢编写自己的代码以用于学习目的。
一些答案包括我首先使用的方法(寻找最后一个“.”),但我记得在linux上隐藏文件/文件夹以“.”开头。因此,如果文件文件是隐藏的并且没有扩展名,则整个文件名将被用作扩展名。为了避免这种情况,我写了这段代码:
bool getFileExtension(const char * dir_separator, const std::string & file, std::string & ext)
{
std::size_t ext_pos = file.rfind(".");
std::size_t dir_pos = file.rfind(dir_separator);
if(ext_pos>dir_pos+1)
{
ext.append(file.begin()+ext_pos,file.end());
return true;
}
return false;
}
我还没有对此进行充分测试,但我认为它应该有效。
_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w
我认为这仅限于 Windows(平台 SDK)?
使用 C++17 及其 std::filesystem::path::extension
(该库是 boost::filesystem 的后继者)您可以使您的声明比使用例如更具表现力 std::string
.
#include <iostream>
#include <filesystem> // C++17
namespace fs = std::filesystem;
int main()
{
fs::path filePath = "my/path/to/myFile.conf";
if (filePath.extension() == ".conf") // Heed the dot.
{
std::cout << filePath.stem() << " is a valid type."; // Output: "myFile is a valid type."
}
else
{
std::cout << filePath.filename() << " is an invalid type."; // Output: e.g. "myFile.cfg is an invalid type"
}
}
也可以看看 std::文件系统::路径::干, std::文件系统::路径::文件名.
使用 std::string 的 find/rfind 解决了这个问题,但是如果你经常使用路径,那么你应该看看 boost::filesystem::path 因为它会让你的代码比摆弄原始字符串索引/迭代器干净得多。
我建议使用 boost,因为它是一个高质量、经过充分测试、(开源和商业)免费且完全可移植的库。
对于 char 数组类型字符串,您可以使用以下命令:
#include <ctype.h>
#include <string.h>
int main()
{
char filename[] = "apples.bmp";
char extension[] = ".jpeg";
if(compare_extension(filename, extension) == true)
{
// .....
} else {
// .....
}
return 0;
}
bool compare_extension(char *filename, char *extension)
{
/* Sanity checks */
if(filename == NULL || extension == NULL)
return false;
if(strlen(filename) == 0 || strlen(extension) == 0)
return false;
if(strchr(filename, '.') == NULL || strchr(extension, '.') == NULL)
return false;
/* Iterate backwards through respective strings and compare each char one at a time */
for(int i = 0; i < strlen(filename); i++)
{
if(tolower(filename[strlen(filename) - i - 1]) == tolower(extension[strlen(extension) - i - 1]))
{
if(i == strlen(extension) - 1)
return true;
} else
break;
}
return false;
}
除了文件名之外还可以处理文件路径。适用于 C 和 C++。并且跨平台。
很好的答案,但我发现其中大多数都存在一些问题:首先,我认为一个好的答案应该适用于具有路径标题的完整文件名,它也应该适用于 Linux 或 Windows,或者如上所述,它应该是跨平台的。对于大多数答案;如果文件名没有扩展名,但路径的文件夹名称包含点,则该函数将无法返回正确的扩展名:一些测试用例的示例如下:
const char filename1 = {"C:\\init.d\\doc"}; // => No extention
const char filename2 = {"..\\doc"}; //relative path name => No extention
const char filename3 = {""}; //emputy file name => No extention
const char filename4 = {"testing"}; //only single name => No extention
const char filename5 = {"tested/k.doc"}; // normal file name => doc
const char filename6 = {".."}; // parent folder => No extention
const char filename7 = {"/"}; // linux root => No extention
const char filename8 = {"/bin/test.d.config/lx.wize.str"}; // ordinary path! => str
"布莱恩·纽曼" 对于 filename1 和 filename4,建议将失败。大多数基于反向查找的其他答案对于 filename1 都会失败。我建议在您的源中包含以下方法:这是函数,返回扩展名的第一个字符的索引,或者如果未找到则返回给定字符串的长度。
size_t find_ext_idx(const char* fileName)
{
size_t len = strlen(fileName);
size_t idx = len-1;
for(size_t i = 0; *(fileName+i); i++) {
if (*(fileName+i) == '.') {
idx = i;
} else if (*(fileName + i) == '/' || *(fileName + i) == '\\') {
idx = len - 1;
}
}
return idx+1;
}
你可以在你的 C++ 应用程序中使用上面的代码,如下所示:
std::string get_file_ext(const char* fileName)
{
return std::string(fileName).substr(find_ext_idx(fileName));
}
在某些情况下,最后一点是,文件夹被赋予文件名作为参数,并在文件夹名称中包含一个点,该函数将返回文件夹的点尾随,因此最好首先让用户检查给定的名称是否是文件名而不是文件夹名称。
使用 System::String 的 NET/CLI 版本
System::String^ GetFileExtension(System::String^ FileName)
{
int Ext=FileName->LastIndexOf('.');
if( Ext != -1 )
return FileName->Substring(Ext+1);
return "";
}
我会和 boost::filesystem::extension
但如果您无法使用 Boost 并且只需验证扩展,一个简单的解决方案是:
bool ends_with(const std::string &filename, const std::string &ext)
{
return ext.length() <= filename.length() &&
std::equal(ext.rbegin(), ext.rend(), filename.rbegin());
}
if (ends_with(filename, ".conf"))
{ /* ... */ }
我使用这两个函数来获取 扩大 和 不带扩展名的文件名:
std::string fileExtension(std::string file){
std::size_t found = file.find_last_of(".");
return file.substr(found+1);
}
std::string fileNameWithoutExtension(std::string file){
std::size_t found = file.find_last_of(".");
return file.substr(0,found);
}
还有这些 regex
满足某些额外要求的方法:
std::string fileExtension(std::string file){
std::regex re(".*[^\\.]+\\.([^\\.]+$)");
std::smatch result;
if(std::regex_match(file,result,re))return result[1];
else return "";
}
std::string fileNameWithoutExtension(std::string file){
std::regex re("(.*[^\\.]+)\\.[^\\.]+$");
std::smatch result;
if(std::regex_match(file,result,re))return result[1];
else return file;
}
正则表达式方法满足的额外要求:
- 如果 文件名 就好像
.config
或者类似的东西, 扩大 将是一个空字符串并且 不带扩展名的文件名 将.config
. - 如果文件名没有任何扩展名,则扩展名将为空字符串, 不带扩展名的文件名 将是 文件名 不变。
编辑:
还可以通过以下方式满足额外要求:
std::string fileExtension(const std::string& file){
std::string::size_type pos=file.find_last_of('.');
if(pos!=std::string::npos&&pos!=0)return file.substr(pos+1);
else return "";
}
std::string fileNameWithoutExtension(const std::string& file){
std::string::size_type pos=file.find_last_of('.');
if(pos!=std::string::npos&&pos!=0)return file.substr(0,pos);
else return file;
}
笔记:
在上述函数中仅传递文件名(而不是路径)。
尝试使用 斯特斯特
char* lastSlash;
lastSlash = strstr(filename, ".");
或者你可以使用这个:
char *ExtractFileExt(char *FileName)
{
std::string s = FileName;
int Len = s.length();
while(TRUE)
{
if(FileName[Len] != '.')
Len--;
else
{
char *Ext = new char[s.length()-Len+1];
for(int a=0; a<s.length()-Len; a++)
Ext[a] = FileName[s.length()-(s.length()-Len)+a];
Ext[s.length()-Len] = '\0';
return Ext;
}
}
}
此代码是跨平台的
这是一个函数,它将路径/文件名作为字符串,并以字符串形式返回扩展名。它都是标准的 C++,并且应该可以跨平台工作于大多数平台。
与这里的其他几个答案不同,它根据 PathFindExtensions 的文档处理 Windows PathFindExtension 处理的奇怪情况。
wstring get_file_extension( wstring filename )
{
size_t last_dot_offset = filename.rfind(L'.');
// This assumes your directory separators are either \ or /
size_t last_dirsep_offset = max( filename.rfind(L'\\'), filename.rfind(L'/') );
// no dot = no extension
if( last_dot_offset == wstring::npos )
return L"";
// directory separator after last dot = extension of directory, not file.
// for example, given C:\temp.old\file_that_has_no_extension we should return "" not "old"
if( (last_dirsep_offset != wstring::npos) && (last_dirsep_offset > last_dot_offset) )
return L"";
return filename.substr( last_dot_offset + 1 );
}
这是我想出的一个解决方案。然后,我注意到它与 @serengeor 发布的内容相似。
它适用于 std::string
和 find_last_of
, ,但如果修改为使用,基本思想也将起作用 char
数组和 strrchr
。它处理隐藏文件和代表当前目录的额外点。它是平台无关的。
string PathGetExtension( string const & path )
{
string ext;
// Find the last dot, if any.
size_t dotIdx = path.find_last_of( "." );
if ( dotIdx != string::npos )
{
// Find the last directory separator, if any.
size_t dirSepIdx = path.find_last_of( "/\\" );
// If the dot is at the beginning of the file name, do not treat it as a file extension.
// e.g., a hidden file: ".alpha".
// This test also incidentally avoids a dot that is really a current directory indicator.
// e.g.: "alpha/./bravo"
if ( dotIdx > dirSepIdx + 1 )
{
ext = path.substr( dotIdx );
}
}
return ext;
}
单元测试:
int TestPathGetExtension( void )
{
int errCount = 0;
string tests[][2] =
{
{ "/alpha/bravo.txt", ".txt" },
{ "/alpha/.bravo", "" },
{ ".alpha", "" },
{ "./alpha.txt", ".txt" },
{ "alpha/./bravo", "" },
{ "alpha/./bravo.txt", ".txt" },
{ "./alpha", "" },
{ "c:\\alpha\\bravo.net\\charlie.txt", ".txt" },
};
int n = sizeof( tests ) / sizeof( tests[0] );
for ( int i = 0; i < n; ++i )
{
string ext = PathGetExtension( tests[i][0] );
if ( ext != tests[i][1] )
{
++errCount;
}
}
return errCount;
}
如果你碰巧使用 波科 你可以做的库:
#include <Poco/Path.h>
...
std::string fileExt = Poco::Path("/home/user/myFile.abc").getExtension(); // == "abc"
如果将扩展名视为最后一个点及其后面可能的字符,但仅当它们不包含目录分隔符时,以下函数将返回扩展名起始索引,如果未找到扩展名,则返回 -1。当你拥有它时,你可以做任何你想做的事情,比如剥离扩展、更改它、检查它等等。
long get_extension_index(string path, char dir_separator = '/') {
// Look from the end for the first '.',
// but give up if finding a dir separator char first
for(long i = path.length() - 1; i >= 0; --i) {
if(path[i] == '.') {
return i;
}
if(path[i] == dir_separator) {
return -1;
}
}
return -1;
}
我使用 PathFindExtension() 函数来了解它是否是有效的 tif 文件。
#include <Shlwapi.h>
bool A2iAWrapperUtility::isValidImageFile(string imageFile)
{
char * pStrExtension = ::PathFindExtension(imageFile.c_str());
if (pStrExtension != NULL && strcmp(pStrExtension, ".tif") == 0)
{
return true;
}
return false;
}