Як я можу витягти ім'я та розширення файлу із шляху в 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.