Можно ли использовать итератор, такой как std::filesystem::directory_iterator, вне цикла?

Я пытаюсь понять, как такие вещи, как итераторы, можно использовать в С++, и мне особенно хотелось бы лучше понять std::filesystem::directory_iterator.

Я понимаю такие прямые примеры:

#include <iostream>
#include <filesystem>
#include <string>

void doSomething(std::string filename)
{
    std::cout << filename;
};

int main()
{
    auto iterator = std::filesystem::directory_iterator("c:/somefolder");
    for (auto& i : iterator)
    {
        doSomething(std::filesystem::path(i.path()).filename().string());
    }
}

Но большая часть устаревшего кода не создается таким замкнутым кругом. Можно ли использовать directory_iterator аналогично WinAPI FindNextFile()?

Что-то похожее на это:

std::string getNextFilename(std::string path)
{
    // Notice the actual filesystem access code is neatly packed away in a replaceable function suitable for a HAL.
    static auto iterator = std::filesystem::directory_iterator(path);
    return std::filesystem::path(iterator.path()).filename().string();
}

int main()
{
    while (std::string fileName = getNextFilename("c:/somefolder"))
    {
        doSomething(fileName);
    }

    // or

    std::string fileFirst  = getNextFilename("c:/somefolder");
    std::string fileSecond = getNextFilename("c:/somefolder");
    std::string fileLast   = getNextFilename("c:/somefolder");

}

Ответьте, пожалуйста:

  1. В общем, для итераторов, можно или нельзя их использовать так, и почему?
  2. В частности, как выполнять такой поиск каталога по одному файлу/каталогу за раз.

edit1: Уточненный заголовок.

Не совсем понятно, что вы просите. Вы просто хотите использовать итератор файловой системы вне контекста цикла for? Или вы на самом деле требуете, чтобы итератор каждый раз «перестраивался» из имени файла?

Sneftel 18.03.2022 10:03

Диапазон, основанный на цикле for, по сути, является просто синтаксическим сахаром для увеличения итератора и проверки того, достиг ли он итератора end() (см.: en.cppreference.com/w/cpp/language/range-for), поэтому вы всегда можете вручную воспроизвести такое поведение. Однако для operator bool нет std::string, поэтому вам нужно структурировать getNextFilename по-другому, чтобы иметь «конечное» условие (например: вернуть std::optional<std::string>)

UnholySheep 18.03.2022 10:04
for (auto& e : range) можно перевести как for (auto it = std::begin(range), it != std::end(range); ++it)...
Jarod42 18.03.2022 10:04

По сути, я хотел знать, возможно ли каким-то образом вернуть первый результат, а при последующих вызовах возвращать следующие результаты по одному.

fsteff 18.03.2022 10:05

@UnholySheep Похоже на то, о чем я думаю ... но как это можно реализовать на самом деле?

fsteff 18.03.2022 10:18

@Sneftel Да, я бы хотел использовать его вне цикла for с диапазоном. Он не должен перестраиваться или перезапускаться, пока его специально не попросят сделать это.

fsteff 18.03.2022 10:22

Я бы настоятельно не советовал getNextFilename иметь static auto iterator = ..., так как теперь у вас возникла ситуация, когда getNextFilename можно использовать только для итерации каталога один, однажды во всей вашей программе.

Caleth 18.03.2022 10:48
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Легко можно вручную увеличить итераторы (поскольку это то, что делает цикл for на основе диапазона). Однако вам необходимо соответствующим образом настроить другой код, так как нет operator bool вместо std::string. Одно из возможных решений (лишь небольшое изменение исходного кода, который по-прежнему включает в себя все его проблемы) может выглядеть следующим образом (используя std::optional<std::string>, чтобы включить возврат условия «конец»):

#include <iostream>
#include <filesystem>
#include <string>
#include <optional>

void doSomething(std::string filename)
{
    std::cout << filename;
};

std::optional<std::string> getNextFilename(std::string path)
{
    static auto iterator = std::filesystem::directory_iterator(path);
    if (iterator != std::filesystem::directory_iterator()) {
        auto filename = std::filesystem::path(iterator->path()).filename().string();
        ++iterator; // advance iterator to next entry in directory
        return filename; // uses implicit constructor of `std::optional`
    } else {
        return {}; // return empty optional if we reached end of directory
    }
}

int main()
{
    while (auto fileName = getNextFilename("c:/somefolder"))
    {
        doSomething(*fileName); // dereference optional to get value
    }

    // or

    auto fileFirst  = getNextFilename("c:/somefolder");
    auto fileSecond = getNextFilename("c:/somefolder");
    auto fileLast   = getNextFilename("c:/somefolder");

}

@Caleth Как вы правильно указали в комментарии к моему вопросу, использование static auto iterator = требует особой осторожности, например, добавить это, чтобы убедиться, что его можно обновить:` static std::string oldPath; if (path.compare(oldPath)!= ​​0) { iterator = std::filesystem::directory_iterator(path); старый путь = путь; }` Не так изящно, но понятно.

fsteff 18.03.2022 13:48

Предполагая, что вы хотите иметь более или менее простую замену для FindFirstFile/FindNextFile, решение может выглядеть следующим образом:

struct HANDLE{
    std::filesystem::directory_iterator it;
};
bool FindNextFile(HANDLE &handle, std::string& output) {
    if (handle.it == std::filesystem::directory_iterator{}) return false;
    output=handle.it->path().string();
    handle.it++;
    return true;
}
HANDLE FindFirstFile(std::string input, std::string& output) {
    HANDLE h;
    h.it=std::filesystem::directory_iterator{input};
    FindNextFile(h,output);
    return h;
}

или просто using HANDLE = std::filesystem::directory_iterator

Caleth 18.03.2022 10:29

@Caleth Да, ты можешь это сделать, хороший совет. Преимущество структуры в том, что ее можно сделать непрозрачной, но тогда я бы вернул указатели на дескриптор.

gerum 18.03.2022 10:33

У вас может быть непрозрачный указатель на std::filesystem::directory_iterator, это тип класса, как и любой другой.

Caleth 18.03.2022 10:33

@Калет Возможно. Я не уверен в этом, потому что вам нужно будет предварительно объявить std::filesystem::directory_iterator, чтобы заголовок файловой системы не попадал в модуль компиляции. И предварительное объявление стандартного класса кажется мне сложным, потому что вы не знаете их реализации.

gerum 18.03.2022 10:38

Спасибо. Это очень полезное предложение для добавления в мой набор инструментов.

fsteff 18.03.2022 10:38

Другие вопросы по теме