Cho một chuỗi "filename.conf"
, làm cách nào để xác minh phần mở rộng?
Tôi cần một giải pháp đa nền tảng.
Câu trả lời:
Bạn phải đảm bảo rằng bạn quan tâm đến các tên tệp có nhiều hơn một dấu chấm. ví dụ: c:\.directoryname\file.name.with.too.many.dots.ext
sẽ không được xử lý chính xác bởi strchr
hoặcfind.
Yêu thích của tôi là thư viện hệ thống tệp tăng cường có chức năng mở rộng (đường dẫn)
Đây là một giải pháp quá đơn giản phải không?
#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;
}
}
return "Yes...";
mà không cần kiểm tra - ngụ ý rằng giải pháp sẽ hoạt động cho các đầu vào khác. Như một ví dụ về bộ đếm khác, một tệp có tên đơn giản là "conf" không có phần mở rộng cũng sẽ trả về "Có ..." như ở trên.
boost::filesystem
, điều này là nhỏ để sử dụng, nhưng cung cấp sự hỗ trợ cần thiết. Xem boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm
Cách tốt nhất là không viết bất kỳ mã nào thực hiện nó mà hãy gọi các phương thức hiện có. Trong cửa sổ, PathFindExtension phương thức có lẽ là đơn giản nhất.
Vì vậy, tại sao bạn không viết của riêng bạn?
Lấy ví dụ về strrchr, điều gì sẽ xảy ra khi bạn sử dụng phương thức đó trên chuỗi sau "c: \ program files \ AppleGate.Net \ readme"? ".Net \ readme" có phải là tiện ích mở rộng không? Viết một cái gì đó phù hợp với một số trường hợp ví dụ thì dễ, nhưng có thể khó hơn nhiều để viết một cái gì đó phù hợp với mọi trường hợp.
Giả sử bạn có quyền truy cập vào 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
}
Chỉnh sửa: Đây là một giải pháp đa nền tảng vì bạn không đề cập đến nền tảng. Nếu bạn đặc biệt sử dụng Windows, bạn sẽ muốn tận dụng các chức năng cụ thể của Windows mà những người khác đề cập trong chuỗi.
Ai đó đã đề cập đến boost nhưng tôi chỉ muốn thêm mã thực để thực hiện việc này:
#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
thực sự STL có thể làm điều này mà không cần nhiều mã, tôi khuyên bạn nên tìm hiểu một chút về STL bởi vì nó cho phép bạn làm một số điều thú vị, dù sao thì đây là những gì tôi sử dụng.
std::string GetFileExtension(const std::string& FileName)
{
if(FileName.find_last_of(".") != std::string::npos)
return FileName.substr(FileName.find_last_of(".")+1);
return "";
}
giải pháp này sẽ luôn trả về phần mở rộng ngay cả trên các chuỗi như "this.abcdesmp3" nếu nó không thể tìm thấy phần mở rộng, nó sẽ trả về "".
Với C ++ 17 và nó std::filesystem::path::extension
(thư viện là sự kế thừa để thúc đẩy :: hệ thống tập tin), bạn sẽ làm cho câu lệnh của mình diễn đạt hơn là sử dụng ví dụ 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"
}
}
Xem thêm std :: filesystem :: path :: stem , std :: filesystem :: path :: filename .
Hôm nay bản thân tôi cũng tình cờ gặp câu hỏi này, mặc dù tôi đã có một đoạn mã làm việc, tôi nhận ra rằng nó sẽ không hoạt động trong một số trường hợp.
Mặc dù một số người đã đề xuất sử dụng một số thư viện bên ngoài, nhưng tôi thích viết mã của riêng mình cho mục đích học tập.
Một số câu trả lời bao gồm phương pháp tôi đã sử dụng ngay từ đầu (tìm kiếm "." Cuối cùng), nhưng tôi nhớ rằng trên linux các tệp / thư mục ẩn bắt đầu bằng ".". Vì vậy, nếu tệp tệp bị ẩn và không có phần mở rộng, toàn bộ tên tệp sẽ được lấy để mở rộng. Để tránh điều đó, tôi đã viết đoạn mã này:
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;
}
Tôi đã không kiểm tra điều này đầy đủ, nhưng tôi nghĩ rằng nó sẽ hoạt động.
Sử dụng std :: string's find / rfind giải quyết được vấn đề NÀY, nhưng nếu bạn làm việc nhiều với các đường dẫn thì bạn nên xem đường dẫn boost :: filesystem :: vì nó sẽ làm cho mã của bạn sạch hơn nhiều so với việc loay hoay với các chỉ mục / trình vòng lặp chuỗi thô.
Tôi khuyên bạn nên tăng cường vì đó là một thư viện miễn phí và hoàn toàn di động (mã nguồn mở và thương mại) chất lượng cao, đã được kiểm tra kỹ lưỡng.
Đối với chuỗi kiểu mảng char, bạn có thể sử dụng điều này:
#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ó thể xử lý các đường dẫn tệp ngoài tên tệp. Hoạt động với cả C và C ++. Và đa nền tảng.
strlen(extension)
trong for
điều kiện. Sau đó, nếu các ký tự không khớp, trả về false. for
Vòng lặp bên ngoài trả về true.
Câu trả lời tốt nhưng tôi thấy hầu hết chúng đều có một số vấn đề: Trước hết, tôi nghĩ rằng một câu trả lời tốt sẽ phù hợp với các tên tệp hoàn chỉnh có tiêu đề đường dẫn của chúng, nó cũng phải hoạt động cho linux hoặc windows hoặc như đã đề cập, nó phải là nền tảng chéo. Đối với hầu hết các câu trả lời; tên tệp không có phần mở rộng nhưng một đường dẫn có tên thư mục bao gồm dấu chấm, hàm sẽ không trả lại đúng phần mở rộng: ví dụ về một số trường hợp thử nghiệm có thể như sau:
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
Đề xuất " brian newman " sẽ không thành công đối với tên tệp1 và tên tệp 4. và hầu hết các câu trả lời khác dựa trên tìm kiếm ngược sẽ không thành công cho tên tệp1. Tôi khuyên bạn nên bao gồm phương pháp sau trong nguồn của bạn: đó là hàm trả về chỉ mục của ký tự đầu tiên của phần mở rộng hoặc độ dài của chuỗi đã cho nếu không tìm thấy.
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;
}
bạn có thể sử dụng mã trên trong ứng dụng c ++ của mình như dưới đây:
std::string get_file_ext(const char* fileName)
{
return std::string(fileName).substr(find_ext_idx(fileName));
}
Điểm cuối cùng trong một số trường hợp, một thư mục được đặt cho tên tệp làm đối số và bao gồm một dấu chấm trong tên thư mục, hàm sẽ trả về dấu chấm của thư mục, vì vậy trước tiên người dùng nên kiểm tra xem tên đã cho có phải là tên tệp chứ không phải tên thư mục hay không.
Phiên bản NET / CLI sử dụng System :: String
System::String^ GetFileExtension(System::String^ FileName)
{
int Ext=FileName->LastIndexOf('.');
if( Ext != -1 )
return FileName->Substring(Ext+1);
return "";
}
Tôi muốn sử dụng boost::filesystem::extension
( std::filesystem::path::extension
với C ++ 17) nhưng nếu bạn không thể sử dụng Boost và bạn chỉ cần xác minh tiện ích mở rộng, một giải pháp đơn giản là:
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"))
{ /* ... */ }
Đây là một giải pháp mà tôi đã nghĩ ra. Sau đó, tôi nhận thấy rằng nó giống với những gì @serengeor đã đăng.
Nó hoạt động với std::string
và find_last_of
, nhưng ý tưởng cơ bản cũng sẽ hoạt động nếu được sửa đổi để sử dụng char
mảng và strrchr
. Nó xử lý các tệp ẩn và các dấu chấm phụ đại diện cho thư mục hiện tại. Nó độc lập với nền tảng.
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;
}
Bài kiểm tra đơn vị:
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;
}
Tôi sử dụng hai hàm này để lấy phần mở rộng và tên tệp không có phần mở rộng :
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);
}
Và những regex
cách tiếp cận này cho một số yêu cầu bổ sung nhất định:
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;
}
Các yêu cầu bổ sung được đáp ứng bằng phương pháp regex:
.config
hoặc tương tự như thế này, phần mở rộng sẽ là một chuỗi trống và tên tệp không có phần mở rộng sẽ là .config
.BIÊN TẬP:
Các yêu cầu bổ sung cũng có thể được đáp ứng bằng cách sau:
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;
}
Ghi chú:
Chỉ chuyển tên tệp (không phải đường dẫn) trong các hàm trên.
Hoặc bạn có thể sử dụng cái này:
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;
}
}
}
Mã này đa nền tảng
Đây là một hàm nhận đường dẫn / tên tệp dưới dạng chuỗi và trả về phần mở rộng dưới dạng chuỗi. Tất cả đều là c ++ tiêu chuẩn và sẽ hoạt động đa nền tảng cho hầu hết các nền tảng.
Không giống như một số câu trả lời khác ở đây, nó xử lý các trường hợp kỳ lạ mà PathFindExtension của windows xử lý, dựa trên tài liệu của PathFindExtensions.
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 );
}
max( filename.rfind(L'\\'), filename.rfind(L'/') )
sẽ so sánh hai giá trị không dấu, một trong số chúng có thể npos
là số nguyên không dấu lớn nhất có thể. Vì vậy, nó có thể trông giống như không có thư mục ngay cả khi nó ở đó!
Nếu bạn coi phần mở rộng là dấu chấm cuối cùng và các ký tự có thể có sau nó, nhưng chỉ khi chúng không chứa ký tự phân tách thư mục, hàm sau sẽ trả về chỉ mục bắt đầu của phần mở rộng hoặc -1 nếu không tìm thấy phần mở rộng. Khi có điều đó, bạn có thể làm bất cứ điều gì bạn muốn, chẳng hạn như tách tiện ích mở rộng, thay đổi nó, kiểm tra nó, v.v.
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;
}
Tôi đã sử dụng hàm PathFindExtension () để biết liệu nó có phải là tệp tif hợp lệ hay không.
#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;
}
Bạn có thể sử dụng strrchr () để tìm lần xuất hiện cuối cùng của các tệp phần mở rộng dựa trên. (Dot) và get. (Dot). Kiểm tra mã dưới đây chẳng hạn.
#include<stdio.h>
void GetFileExtension(const char* file_name) {
int ext = '.';
const char* extension = NULL;
extension = strrchr(file_name, ext);
if(extension == NULL){
printf("Invalid extension encountered\n");
return;
}
printf("File extension is %s\n", extension);
}
int main()
{
const char* file_name = "c:\\.directoryname\\file.name.with.too.many.dots.ext";
GetFileExtension(file_name);
return 0;
}