كيفية الحصول على امتداد الملف من السلسلة في C++

StackOverflow https://stackoverflow.com/questions/51949

  •  09-06-2019
  •  | 
  •  

سؤال

نظرا لسلسلة "filename.conf", ، كيف يمكنني التحقق من جزء التمديد؟

أحتاج إلى حل عبر النظام الأساسي.

هل كانت مفيدة؟

المحلول

عليك التأكد من الاهتمام بأسماء الملفات التي تحتوي على أكثر من نقطة واحدة.مثال: c:\.directoryname\file.name.with.too.many.dots.ext لن يتم التعامل معها بشكل صحيح من قبل strchr أو find.

المفضل لدي سيكون تعزيز مكتبة نظام الملفات التي لها وظيفة ملحق (المسار).

نصائح أخرى

هل هذا حل بسيط للغاية؟

#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;
  }
}

أفضل طريقة هي عدم كتابة أي كود يقوم بذلك ولكن استدعاء الأساليب الموجودة.في النوافذ، PathFindExtension ربما تكون الطريقة هي الأبسط.

فلماذا لا تكتب بنفسك؟

حسنًا، خذ مثال 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 المحددة التي ذكرها الآخرون في الموضوع.

ذكر شخص آخر التعزيز ولكني أردت فقط إضافة الكود الفعلي للقيام بذلك:

#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,'.') 

شيء واحد يجب تذكره:لو '.' غير موجود في اسم الملف، وسوف يكون تحويلة 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::نظام الملفات::path::stem, std::نظام الملفات::path::اسم الملف.

يؤدي استخدام std::string's find/rfind إلى حل هذه المشكلة، ولكن إذا كنت تعمل كثيرًا مع المسارات، فيجب عليك النظر إلى Boost::filesystem::path لأنه سيجعل التعليمات البرمجية الخاصة بك أكثر نظافة من العبث بفهرسات/مكررات السلسلة الأولية.

أقترح التعزيز نظرًا لأنها مكتبة مجانية عالية الجودة ومختبرة جيدًا (مفتوحة المصدر وتجاريًا) ومحمولة بالكامل.

بالنسبة للسلاسل النصية من نوع 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));
}

النقطة الأخيرة في بعض الحالات هي أنه يتم إعطاء مجلد لاسم الملف كوسيطة ويتضمن نقطة في اسم المجلد، وستعيد الوظيفة النقطة اللاحقة للمجلد لذا فمن الأفضل أولاً أن يتحقق المستخدم من أن الاسم المحدد هو اسم ملف وليس اسم مجلد.

إصدار NET/CLI يستخدم System::String

   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;

}

المتطلبات الإضافية التي يتم استيفاؤها بواسطة طريقة regex:

  1. لو اسم الملف يشبه .config أو شيء من هذا القبيل، امتداد سيكون سلسلة فارغة و اسم الملف بدون امتداد سوف يكون .config.
  2. إذا لم يكن لاسم الملف أي امتداد، فسيكون الامتداد عبارة عن سلسلة فارغة، اسم الملف بدون امتداد سيكون ال اسم الملف دون تغيير.

يحرر:

يمكن أيضًا تلبية المتطلبات الإضافية من خلال ما يلي:

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;
}

ملحوظة:

قم بتمرير أسماء الملفات فقط (وليس المسار) في الوظائف المذكورة أعلاه.

حاول استخدام strstr

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;
            }
        }
    }

هذا الرمز هو عبر منصة

إذا كنت تستخدم مكتبة Qt، يمكنك تجربة ذلك QFileInfoلاحقة ()

إليك دالة تأخذ المسار/اسم الملف كسلسلة وترجع الامتداد كسلسلة.كل ذلك هو معيار c++، ويجب أن يعمل عبر الأنظمة الأساسية لمعظم الأنظمة الأساسية.

على عكس العديد من الإجابات الأخرى هنا، فهو يتعامل مع الحالات الفردية التي يعالجها PathFindExtension الخاص بنظام Windows، استنادًا إلى وثائق 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 );
}

هذا هو الحل الذي توصلت إليه.ثم لاحظت أنه مشابه لما نشره @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;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top