微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么当我使用 std::filesystem::directory_terator 遍历目录时有时会调用 abort() 方法?

如何解决为什么当我使用 std::filesystem::directory_terator 遍历目录时有时会调用 abort() 方法?

我正在用 C++ 创建一个简单的文件观察器。我正在使用 std::filesystem::directory_iterator,有时它会因调用 abort() 而崩溃。当我跟踪文件的创建和编辑时,它工作得很好(这并不奇怪,因为为此我使用了 map::iterator)。但是当我需要跟踪文件删除重命名时,directory_iterator 有时会崩溃。

问题是,它可以工作几次然后崩溃。而且次数总是不同的。我已经注释掉了可能导致问题的代码,所以问题肯定在 directory_iterator 中。可能是什么问题?

这是文件标题,如果相关的话

#ifndef FILEWATCHER_H
#define FILEWATCHER_H
#include <map>
#include <filesystem>

namespace fs = std::filesystem;

class FileWatcher
{
    size_t currentNumberOfFiles = 0;
    fs::path pathToWatch;
    std::map<fs::path,fs::file_time_type> pathsMap;
    std::string currentTime();
public:
    FileWatcher(fs::path path);
    void start();
};

#endif

和 .cpp 文件

#pragma warning(disable : 4996)
#include "FileWatcher.h"
#include "Event.h"
#include <iostream>

std::string FileWatcher::currentTime()
{
    auto Now = std::chrono::system_clock::Now();
    std::time_t NowTime = std::chrono::system_clock::to_time_t(Now);
    std::string currentSystemTime = std::ctime(&NowTime);
    return currentSystemTime;
}

FileWatcher::FileWatcher(fs::path pathToWatch)
{
    this->pathToWatch = pathToWatch;
    //create a map with last modification of a given file in the directory
    for (auto& file : fs::directory_iterator(this->pathToWatch))
    {
        pathsMap.emplace(file.path(),fs::last_write_time(file));
    }
}

void FileWatcher::start()
{
    while (true)
    {
        currentNumberOfFiles = std::distance(fs::directory_iterator(pathToWatch),fs::directory_iterator());
        
        auto it = pathsMap.begin();
        while (it != pathsMap.end())
        {
            if (!fs::exists(it->first.string()))
            {
                if (currentNumberOfFiles < pathsMap.size())
                {
                    //file deleted
                    //FileType fileType = (fs::is_regular_file(it->first) ? FileType::FILE : FileType::DIRECTORY);
                    //Event event(EventType::DELETED,fileType,it->first,currentTime());
                    //event.printEvent();
                    std::cout << "Remove" << std::endl;
                    it = pathsMap.erase(it);
                    break;
                }
                else
                {

                    //file is renamed
                    //ileType fileType = (fs::is_regular_file(it->first) ? FileType::FILE : FileType::DIRECTORY);
                    //Event event(EventType::RENAMED,currentTime());
                    //event.printEvent();
                    std::cout << "Renamed" << std::endl;
                    it = pathsMap.erase(it);
                    break;
                }
            }
            else
            {
                it++;
            }
        }

        for (auto& file : fs::directory_iterator(pathToWatch))
        {
            if (pathsMap.count(file.path()) == 0)
            {

                //file is create
                pathsMap.emplace(file.path(),fs::last_write_time(file));
                //FileType fileType = (fs::is_regular_file(file) ? FileType::FILE : FileType::DIRECTORY);
                //Event event(EventType::CREATED,file.path(),currentTime());
                //event.printEvent();
                std::cout << "Created" << std::endl;
                break;
            }
            else
            {
                if (pathsMap[file.path()] != fs::last_write_time(file))
                {

                    //file is modified
                    //pathsMap.emplace(file.path(),fs::last_write_time(file));
                    pathsMap[file.path()] = fs::last_write_time(file.path());
                    //FileType fileType = (fs::is_regular_file(file.path()) ? FileType::FILE : FileType::DIRECTORY);
                    //Event event(EventType::EDITED,currentTime());
                    //event.printEvent();
                    std::cout << "Modified" << std::endl;
                    break;
                }
            }
        }
    }
}

我还将附上生成abort() 消息:

abort() message

Visual Studio 2019 不会对输出日志产生任何错误

解决方法

我想你正在经历一场文件系统竞赛。考虑 start 方法中的两个内部循环。第一个检查是否已重命名或删除单个文件(因为您在发现任一情况时即跳出循环),而第二个循环检查是否有任何文件被创建或修改。

但是,如果在第二个循环运行时(由另一个进程)删除了一个文件,会发生什么情况?

来自cppreference

如果在创建目录迭代器后将文件或目录删除或添加到目录树中,则不确定是否会通过迭代器观察到更改。

此行为与 POSIX readdir_r 一致。

简而言之,这意味着您需要能够在迭代中处理文件更改。可能调用 abort 是因为 std::filesystem::last_write_time 在文件不存在时抛出 std::filesystem::filesystem_error,而您可能没有发现。您可以捕获异常,也可以向 std::error_code 提供额外的 last_write_time 参数。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。