Як я можу витягти ім'я та розширення файлу із шляху в C ++


77

У мене є список файлів, що зберігаються в a .logв такому синтаксисі:

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

Я хочу витягти ім'я та розширення з цих файлів. Чи можете ви навести приклад простого способу зробити це?

Відповіді:


178

Щоб витягти ім'я файлу без розширення, використовуйте 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

Домовились. Відповідає на запитання найкоротше.
— AndyUK

13
Насправді p.filename () має тип шляху і буде оточений лапками при неявному перетворенні, тож ви отримаєте: ім'я файлу та розширення: "file.ext" Натомість вам може знадобитися p.filename (). String ().
— Джеймс Гіршхорн,

6
З C ++ 14 / C ++ 17 ви можете використовувати std::experimental::filesystemвідп std::filesystem. Дивіться публікацію Ючен Чжун нижче.
— Рой Дантон,

1
Аскер хотів простого способу. Додавання імпульсу до проекту лише для цієї функціональності не є простим способом. std :: файлова система - це простий спосіб.
— KKlouzal

1
C ++ 17 включив <файлову систему> в стандартну бібліотеку. Використовуйте свіжий компілятор ... або посилення імпорту.
— Микола Меркін

31

Для C ++ 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

Привіт @RoiDanton, дякую за редагування! Я щойно перевірив приклад коду у посиланні посилання, здається, не потрібно перетворювати тип повернення з std::filesystem::pathна std::string, щоб мати можливість використовувати std::cout. en.cppreference.com/w/cpp/filesystem/path/filename. Але якщо ви думаєте інакше, сміливо коментуйте або редагуйте публікацію ще раз.
— Yuchen Zhong

Це правда, std::coutможна покладатися на неявне перетворення. Однак, оскільки коментарі після std::coutсказати file.ext та файл, або .string()їх потрібно додати до коментарів, або вони повинні бути "file.ext" та "file". З Visual C ++ насправді немає ніякої різниці (навіть без string()виводу без лапок), але з gcc 6.1 результат виводиться із лапок, якщо .string()його не вказано. Див. Coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
— Рой Дантон,

@RoiDanton, привіт, це цікаве розуміння. Я ще раз оновлю допис. Дякуємо, що поділилися цим!
— Yuchen Zhong

19

Якщо ви хочете безпечний спосіб (тобто переносний між платформами, а не розміщення припущень на шляху), я рекомендую скористатися цим boost::filesystem.

Це виглядало б якось так:

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

Тоді ви можете витягти різні дані з цього шляху. Ось документація об'єкта path.


BTW: Також пам'ятайте, що для використання шляху типу

c:\foto\foto2003\shadow.gif

вам потрібно уникнути \рядкового літералу:

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

Або /натомість використовуйте :

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

Це стосується лише зазначення буквальних рядків у ""лапках, проблема не існує, коли ви завантажуєте шляхи з файлу.


2
+1 Визначено шлях. Приклад на головному сайті дає спосіб пошуку в каталозі: Використовуйте метод path.extension () для пошуку журналів (див. Boost.org/doc/libs/1_36_0/libs/filesystem/doc/index.htm )
— Том

Дійсно, це в більшості випадків шлях, однак він передбачає додавання в деяких випадках небажаної залежності від зовнішньої бібліотеки. Якщо ви хочете працювати лише з тим, що передбачено стандартом C ++, я пропоную розглянути регулярний вираз C ++, де ви можете визначити регулярний вираз, щоб робити те, що хочете (безліч прикладів в Інтернеті). Перевага - відсутність накладних витрат через деякі додаткові залежності. Однак це також залишає одне питання відкритим - чи потрібна мультиплатформінг? Boost дбає про стиль шляху незалежно від того, використовуєте ви Windows або Linux. Використовуючи регулярний вираз, ви повинні робити це самостійно.
— rbaleksandar

17

Вам доведеться прочитати імена файлів із файлу в std::string. Ви можете використовувати оператор вилучення рядків з std::ostream. Отримавши ім’я файлу в a 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  = "";
    }
}

2
Не хочу бути розумною дупою, але це має бути path.substr, а не path.substring, так?
— Бьорн,

3

Не код, але ось ідея:

  1. Зчитайте a std::stringіз вхідного потоку ( std::ifstream), кожен зчитуваний екземпляр буде повним шляхом
  2. Виконайте find_last_ofна рядку для\
  3. Витягніть підрядок з цієї позиції до кінця, тепер це дасть вам ім'я файлу
  4. Виконайте find_last_offor ., і підрядок з будь-якої сторони дасть вам ім'я + розширення.

І -1 за те, що вони не переносяться :)
— Кос,

Чому голос проти? Якщо щось не так із тим, що я сказав, дайте мені знати, і я виправлю!
— Нім

2
@Kos, ну це суворо! він відповідає тому, що хоче OP, файл на базі Windows, і не було вимог щодо перенесення!
— Нім

Принаймні, дійсний шлях Windows може також мати каталоги, розділені /також. І я навіть не знаю, чи існує більше застережень у специфікаціях шляхів, тому моє мислення просте - якщо є хороша бібліотека, яка робить те, що я хочу, я повинен використовувати її, бо це, можливо, досягне моєї мети краще, ніж я можу. ;)
— Кос

3
@Nim, але чи немає для цього boost::insects::disperser<T>загального шаблону? :)
— Кос,

0

Я також використовую цей фрагмент для визначення відповідного косого символу:

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

а потім замініть косу риску на потрібну косу риску для ОС. Корисно, якщо хтось постійно розгортає між Linux / Windows.


0

Для машин Linux або Unix ОС має дві функції, що займаються назвами шляхів та файлів. використовуйте man 3 basename, щоб отримати більше інформації про ці функції. Перевага використання функціональності, що надається системою, полягає в тому, що вам не потрібно встановлювати boost або писати власні функції.

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

Приклад коду зі сторінки користувача:

   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 (), це трохи непрямо вперед, використовуючи це всередині коду C ++. Ось простий приклад з моєї бази коду:

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

Використання new / delete не є хорошим стилем. Я міг би помістити це в блок try / catch, якщо щось трапиться між двома дзвінками.


0

Спробуйте наступний трюк, щоб витягти ім'я файлу із шляху без розширення в c ++ без зовнішніх бібліотек у c ++:

#include <iostream>
#include <string>

using std::string;

string getFileName(const string& s) {
char sep = '/';
#ifdef _WIN32
sep = '\\';
#endif
size_t i = s.rfind(sep, s.length());
if (i != string::npos) 
{
string filename = s.substr(i+1, s.length() - i);
size_t lastindex = filename.find_last_of("."); 
string rawname = filename.substr(0, lastindex); 
return(rawname);
}

return("");
}

int main(int argc, char** argv) {

string path = "/home/aymen/hello_world.cpp";
string ss = getFileName(path);
std::cout << "The file name is \"" << ss << "\"\n";
}

-1

Відповіді Миколая Меркіна та Ючен Чжун є чудовими, але з коментарів видно, що вони не є повністю точними.

Неявне перетворення в std :: string під час друку оберне ім'я файлу в лапки. Коментарі також не є точними.

path::filename()і path::stem()повертає новий об'єкт шляху і path::string()повертає посилання на рядок. Таким чином, щось подібне std::cout << file_path.filename().string() << "\n"може спричинити проблеми із звисаючим посиланням, оскільки рядок, на який посилає посилання, може бути знищений.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.