Как вывести список файлов в каталоге с помощью Windows API?

У меня есть этот код, и он отображает папку с самим каталогом, а не его содержимым. Я хочу показать его содержимое. Я не хочу использовать boost :: filesystem.

Как я могу это решить?

Код:

#include <windows.h>
#include <iostream>

int main()
{
    WIN32_FIND_DATA data;
    HANDLE hFind = FindFirstFile("C:\\semester2", &data);      // DIRECTORY

    if ( hFind != INVALID_HANDLE_VALUE ) {
        do {
            std::cout << data.cFileName << std::endl;
        } while (FindNextFile(hFind, &data));
        FindClose(hFind);
    }
}

Вывод:

semester2

person John Escobia    schedule 31.12.2016    source источник
comment
Возможный дубликат Как получить текущий каталог?   -  person    schedule 31.12.2016
comment
@RawN Ну, не совсем, но это дало мне идею! Теперь работает! Спасибо! :)   -  person John Escobia    schedule 31.12.2016
comment
Примечание: вы используете узкий символьный литерал с API-интерфейсами ширины символа, выбранной во время компиляции. Современные среды сборки, как правило, по умолчанию используют сборки Unicode, что может привести к поломке. Я настоятельно рекомендую либо использовать явно типы Unicode и API везде (добавить суффикс W к WIN32_FIND_DATA, FindFirstFile, & FindNextFile, L префикс к литералу пути, заменить std::cout на std::wcout) или использовать последовательно TCHARs (добавить #include <tchar.h>, сделать строковый литерал _T("C:\\semester2"), и условно псевдоним от std::tcout до _12 _ / _ 13_ в зависимости от ситуации).   -  person ShadowRanger    schedule 12.06.2019


Ответы (4)


HANDLE hFind = FindFirstFile("C:\\semester2", &data);       // DIRECTORY

Вы получили каталог, потому что это то, о чем вы просили. Если вам нужны файлы, попросите их:

HANDLE hFind = FindFirstFile("C:\\semester2\\*", &data);  // FILES

(Вместо этого вы можете использовать *.*, если хотите, но, очевидно, это работает только из-за взлома обратной совместимости, поэтому, вероятно, следует избегать. См. Комментарии и ответ RbMm.)

person Harry Johnston    schedule 31.12.2016
comment
Потрясающий! Спасибо! Один вопрос, почему перед отображением файлов ставятся точки (.)? Я отредактировал вывод на свой вопрос с вашими изменениями :) - person John Escobia; 31.12.2016
comment
Каждый каталог (кроме корневого) содержит ссылку на себя . и ссылку на свой родительский .., если вы не хотите включать их, их легко пропустить. - person Harry Johnston; 31.12.2016
comment
Понял. Как это можно пропустить? - person John Escobia; 31.12.2016
comment
Ничего сложного. Обычно всего пара сравнений строк и оператор if. - person Harry Johnston; 31.12.2016
comment
Ах хорошо. Попался. Спасибо :) - person John Escobia; 31.12.2016
comment
*.* - это просто возврат к DOS. На мой взгляд, более ясно использовать вместо этого *. - person David Heffernan; 31.12.2016

Позвольте мне сделать несколько заметок о "*.*" и "*". Эти фильтраторы не равны.

В нашей папке могут находиться 2 разных файла: somefile и somefile..

Если бы мы использовали API низкого уровня ZwQueryDirectoryFile с "*.*" в качестве поискового выражения (это 10-й параметр - FileName [in, optional]) - мы получим только somefile.. Но если бы мы использовали "*", то получили бы оба файла - somefile и somefile..

Если мы попробуем FindFirstFile("C:\\semester2\\*.*", &data);, мы можем заметить, что возвращаются оба файла somefile и somefile.. Итак, здесь "*.*" vs "*" имеют тот же эффект - никакой разницы в использовании.

Почему так происходит? Потому что внутри FindFirstFileEx в kernelbase (kernel32) сделайте специальную проверку на "*.*" маску, и если это правда - замените на "" (пустое имя, которое имеет тот же эффект, что и "*").

Я думаю, что это сделано для исправления очень распространенной ошибки, когда пользователи передают "*.*" вместо правильного "*", и для обратной совместимости с устаревшим кодом.

. и .. фактически не являются частью каталога, поскольку они хранятся на диске, но добавляются Win32 API.

Это неправда.

  • для файловой системы в стиле FAT это действительно хранится в каталоге FAT как 2 первые записи.
  • в NTFS таких записей нет, но NTFS.sys искусственно добавляют эти 2 записи, если они в маске.

Так что это делается не на уровне Win32 API, а в ядре - на уровне драйвера.

В заключение, "*.*" будет правильно работать с Win32 API, как минимум сейчас, но правильный и чистый способ - использовать здесь "*".
"*.*" будет ошибкой с _ 33_ api.

person RbMm    schedule 31.12.2016
comment
Поддержка *.* - это функция обратной совместимости, короткие имена файлов всегда имеют расширения. (Расширение могло быть пустым, но оно было там.) Вы правы в том, что точка и двойная точка являются частью ядра, а не Win32, я заметил, что длинные пути, в которых они использовались, не работали в командной строке (например, завершение табуляции не работает), но при более внимательном рассмотрении кажется, что это как-то связано с тем, как работает cmd.exe. Длинный путь с точкой работает, например, в блокноте. - person Harry Johnston; 01.01.2017
comment
@HarryJohnston - да, я быстро ищу FindFirstFileEx и заметил, что он делает специальную проверку для "*.*" и исправляет это до "" - так что быстрее всего для взлома обратной совместимости. Я, однако, не был знаком с этим раньше, потому что всегда использую только ZwQueryDirectoryFile в собственном коде, а здесь "*.*" не возвращает файлы типа somefile (но возвращает somefile.) - person RbMm; 01.01.2017
comment
Надеюсь, вы меня простите, если я укажу, что это одна из причин, по которой люди рекомендуют не использовать собственный API без надобности. :-) - person Harry Johnston; 01.01.2017
comment
@HarryJohnston - извините, я упомянул NT api здесь. это кажется нецензурной бранью для 99% + программистов пользовательского режима. но здесь только для того, чтобы показать, на каком уровне система взламывает "*.*". про NT api у меня другое мнение (думаю, это правильно, что он не делает таких хаков), мне он нравится за прямой возврат кода ошибки (без его преобразования с потерей точности), строгие унифицированные подписи (сравните win32), многое более мощная и эффективная сравните win32, для возможности которой вообще нет в win32 .. но все это уже не по теме. Я не советую никому использовать это :) - person RbMm; 01.01.2017
comment
@HarryJohnston - в любом случае спасибо. извините мой английский и родной api :) - person RbMm; 01.01.2017

Вот пример реализации:

#include <iostream>
#include <vector>
#include <string>
#include <Windows.h>

std::vector<std::string>
list_directory(
    const std::string &directory)
{
    WIN32_FIND_DATAA findData;
    HANDLE hFind = INVALID_HANDLE_VALUE;
    std::string full_path = directory + "\\*";
    std::vector<std::string> dir_list;

    hFind = FindFirstFileA(full_path.c_str(), &findData);

    if (hFind == INVALID_HANDLE_VALUE)
        throw std::runtime_error("Invalid handle value! Please check your path...");

    while (FindNextFileA(hFind, &findData) != 0)
    {
        dir_list.push_back(std::string(findData.cFileName));
    }

    FindClose(hFind);

    return dir_list;
}

Примечание: было бы намного лучше использовать что-то вроде boost :: filesystem, если вы используете C ++ 11 или std :: filesystem, если вы используете C ++ 17. Также входной путь должен быть похож на C: \ path, а не C: \ path \ иначе это не сработает !!

person Ferdi Kedef    schedule 19.01.2021
comment
std::filesystem::path доступен начиная с C ++ 17, что может быть предпочтительнее ускорения. - person MasterHD; 19.01.2021
comment
Я знаю, но некоторые люди до сих пор используют C ++ 11 - person Ferdi Kedef; 19.01.2021
comment
Обновил ... - person Ferdi Kedef; 19.01.2021

Ответ Гарри фактически даст файлы и папки с расширением в желаемой папке "C:\\semester2".

Так, например, если у вас есть папка с именем "C:\\semester2\\math.course", она также будет найдена в приведенном выше примере. Более того, если у вас будет файл с именем "C:\\semester2\\math_scores" (обратите внимание, что у него нет расширения), он не будет найден.

Принимая во внимание вышесказанное, я бы предложил следующее решение:

HANDLE hFind = FindFirstFile("C:\\semester2\\*", &data); 

Это отобразит полный список элементов в каталоге. Фильтровать каталоги можно следующим образом:

if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// directory
}
else
{
// file
}

В качестве ссылок можно использовать следующее: Константы FileAttributes, Структура FIND_DATA, FindFirstFile API

person papadp    schedule 31.12.2016
comment
Это не правда. *.* найдет файлы без расширений. Подробнее см. Ответ RbMm. - person Harry Johnston; 01.01.2017