如何解决为什么当我使用 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()
消息:
Visual Studio 2019 不会对输出日志产生任何错误。
解决方法
我想你正在经历一场文件系统竞赛。考虑 start
方法中的两个内部循环。第一个检查是否已重命名或删除单个文件(因为您在发现任一情况时即跳出循环),而第二个循环检查是否有任何文件被创建或修改。
但是,如果在第二个循环运行时(由另一个进程)删除了一个文件,会发生什么情况?
如果在创建目录迭代器后将文件或目录删除或添加到目录树中,则不确定是否会通过迭代器观察到更改。
此行为与 POSIX readdir_r
一致。
简而言之,这意味着您需要能够在迭代中处理文件更改。可能调用 abort
是因为 std::filesystem::last_write_time
在文件不存在时抛出 std::filesystem::filesystem_error
,而您可能没有发现。您可以捕获异常,也可以向 std::error_code
提供额外的 last_write_time
参数。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。