Подтвердить что ты не робот

Как я могу извлечь имя файла и расширение из пути в С++

У меня есть список файлов, хранящихся в .log в этом синтаксисе:

c:\foto\foto2003\shadow.gif
D:\etc\mom.jpg

Я хочу извлечь имя и расширение из этих файлов. Можете ли вы привести пример простого способа сделать это?

4b9b3361

Ответ 1

Чтобы извлечь имя файла без расширения, используйте boost:: filesystem:: path:: stem вместо уродливого std::string:: find_last_of ( "." )

boost::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension : " << p.filename() << std::endl; // file.ext
std::cout << "filename only          : " << p.stem() << std::endl;     // file

Ответ 2

Если вам нужен безопасный способ (т.е. переносимый между платформами и не устанавливающий предположения на пути), я бы рекомендовал использовать boost::filesystem.

Это выглядело бы так:

boost::filesystem::path my_path( filename );

Затем вы можете извлечь различные данные с этого пути. Здесь документация объекта пути.


Кстати: Также помните, что для использования пути, типа

c:\foto\foto2003\shadow.gif

вам нужно избежать \ в строковом литерале:

const char* filename = "c:\\foto\\foto2003\\shadow.gif";

Или используйте / вместо этого:

const char* filename = "c:/foto/foto2003/shadow.gif";

Это относится только к заданию литеральных строк в кавычках "", проблема не возникает при загрузке путей из файла.

Ответ 3

Вам нужно будет прочитать ваши имена файлов из файла в std::string. Вы можете использовать оператор извлечения строки std::ostream. Когда у вас есть имя файла в std::string, вы можете использовать метод std::string::find_last_of, чтобы найти последний разделитель.

Что-то вроде этого:

std::ifstream input("file.log");
while (input)
{
    std::string path;
    input >> path;

    size_t sep = path.find_last_of("\\/");
    if (sep != std::string::npos)
        path = path.substr(sep + 1, path.size() - sep - 1);

    size_t dot = path.find_last_of(".");
    if (dot != std::string::npos)
    {
        std::string name = path.substr(0, dot);
        std::string ext  = path.substr(dot, path.size() - dot);
    }
    else
    {
        std::string name = path;
        std::string ext  = "";
    }
}

Ответ 4

Не код, но вот идея:

  • Прочитайте std::string из входного потока (std::ifstream), каждый прочитанный экземпляр будет полным путем
  • Сделайте find_last_of в строке для \
  • Извлеките подстроку из этой позиции до конца, теперь она даст вам имя файла
  • Сделайте find_last_of для ., а подстрока с обеих сторон даст вам имя + расширение.

Ответ 5

Для С++ 17:

#include <filesystem>

std::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension: " << p.filename() << std::endl; // "file.ext"
std::cout << "filename only: " << p.stem() << std::endl;              // "file"

Справка о файловой системе: http://en.cppreference.com/w/cpp/filesystem


Как было предложено @RoiDanto, для форматирования вывода std::out может окружать вывод с помощью котировок, например:

filename and extension: "file.ext"

Вы можете преобразовать std::filesystem::path в std::string на p.filename().string(), если это вам нужно, например:

filename and extension: file.ext

Ответ 6

Я также использую этот фрагмент, чтобы определить соответствующий символ косой черты:

boost::filesystem::path slash("/");
    boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native();

а затем замените косые черты на предпочтительную косую черту для ОС. Полезно, если вы постоянно развертываете Linux/Windows.

Ответ 7

Для Linux или Unix-устройств os имеет две функции, связанные с именами путей и файлов. используйте man 3 basename, чтобы получить дополнительную информацию об этих функциях. Преимущество использования предоставляемой системой функциональности заключается в том, что вам не нужно устанавливать boost или писать собственные функции.

#include <libgen.h>
       char *dirname(char *path);
       char *basename(char *path);

Пример кода с man-страницы:

   char *dirc, *basec, *bname, *dname;
           char *path = "/etc/passwd";

           dirc = strdup(path);
           basec = strdup(path);
           dname = dirname(dirc);
           bname = basename(basec);
           printf("dirname=%s, basename=%s\n", dname, bname);

Из-за типа аргумента non-const функции basename() это немного не прямолинейно, используя этот код внутри С++. Вот простой пример из моей базы кода:

string getFileStem(const string& filePath) const {
   char* buff = new char[filePath.size()+1];
   strcpy(buff, filePath.c_str());
   string tmp = string(basename(buff));
   string::size_type i = tmp.rfind('.');
   if (i != string::npos) {
      tmp = tmp.substr(0,i);
   }
   delete[] buff;
   return tmp;
}

Использование нового/удаления не является хорошим стилем. Я мог бы положить его в попытку/поймать блок в случае, если что-то произошло между двумя вызовами.