qq_39141417 2024-12-27 16:47 采纳率: 100%
浏览 39
已结题

C++文件操作读取问题

读取文件夹进行内视频用于截图操作时,碰到了以下问题。源代码如下,帮忙看下。
显示错误 无法识别文件夹Error: Directory does not exist or is not a directory.

img

#include <iostream>
#include <filesystem>
#include <fstream>
#include <vector>
#include <string>
#include <set>
#include <chrono>
//#include <thread> 
#include <cstdlib> // for std::system
#include <algorithm>
#include "mingw.thread.h"

namespace fs = std::filesystem;  //用fs简写代表std::filesystem 提供对文件系统的操作 目录遍历,创建,删除等

/**
 * @brief 判断给定文件路径是否为 IPC 文件
 *
 * 该函数检查文件路径的扩展名是否为 ".ipc",如果是,则返回 true,表示该文件是一个 IPC 文件;否则返回 false。
 * 
 * @param filePath 文件路径
 * @return true 如果文件路径的扩展名为 ".ipc"
 * @return false 如果文件路径的扩展名不是 ".ipc"
 */
bool isIPCFile(const fs::path &filePath)   //定义函数isIPCFile,接受一个检查的文件系统中的路径filePath参数名称,文件路径
{
    return filePath.extension() == ".mp4"; 
    //extension为获取文件的扩展名,如果文件路径的扩展名为".ipc",返回true,否则返回false
}

/**
 * @brief 从视频文件中截取屏幕截图并保存到指定目录
 *
 * @param videoPath 视频文件的路径
 * @param outputdir 截图保存的输出目录
 * @return void 如果截图过程中发生错误,将在标准错误流中输出错误信息
 *
 * 使用ffmpeg命令行工具,以每0.2秒截取一帧的速度从视频中截取屏幕截图,
 * 并将截图保存为PNG格式的文件,文件名格式为image0001.png, image0002.png等。
 * 如果ffmpeg命令执行失败(返回值不为0),则输出错误信息。
 */
void takeScreenshots(const fs::path &videoPath, const fs::path &outputdir)
//定义函数takeScreenshots,接收两个文件系统的路径,videoPath为视频文件的路径,outputdir为截图保存的输出目录
{
    std::string command = "ffmpeg -i \"" + videoPath.string() + "\" -vf fps=1/0.2 " + outputdir.string() + "/image%04d.png";
    //定义一个字符串变量command,用于存储构建的命令行
    //执行ffmpeg命令,以每0.2秒截取一帧的速度从视频中截取屏幕截图,并将截图保存为PNG格式的文件,文件名格式为image0001.png, image0002.png等。
    int result = std::system(command.c_str());
    //std::system执行一个系统命令,接受一个字符串参数
    //command.c_str()是一个成员函数调用,指向字符数组
    //定义一个变量result,用于存储ffmpeg命令执行的结果,表示命令执行的状态
    if (result != 0){
        std::cerr << "Error taking screenshots for" << videoPath << '\n'; //不为0则输出错误信息
    }
}

/**
 * @brief 查找指定目录及其子目录下的IPC文件,并将找到的文件路径存储到向量中。
 *
 * @param directoryPath 要搜索的目录路径。
 * @param foundFiles 一个集合,用于存储已经找到的文件路径,以避免重复添加。
 * @return std::vector<fs::path> 包含找到的IPC文件路径的向量。
 *
 * 该函数首先检查指定的目录是否存在且为一个目录。然后,递归地遍历该目录及其所有子目录,
 * 对于每个常规文件,如果其扩展名为".ipc"且未被添加到foundFiles集合中,则将其路径添加到ipcFiles向量中,
 * 并标记为已找到。最后返回包含所有找到的IPC文件路径的向量。
 */
std::vector<fs::path> findIPCFiles(const fs::path &directoryPath, std::set<fs::path> &foundFiles)
//返回一个std::vector容器,用于存储找到的IPC文件路径
//定义函数findIPCFiles,接受两个参数,directoryPath为要搜索的目录路径,foundFiles为一个集合,用于存储已经找到的文件路径,以避免重复添加
{
    std::vector<fs::path> ipcFiles; // 声明一个名为ipcFiles的向量,用于存储找到的IPC文件路径

    if (!fs::exists(directoryPath) || !fs::is_directory(directoryPath)){// 判断指定的目录是否存在且是一个目录
        std::cerr<<"Error: Directory does not exist or is not a directory.\n"; //不存在则error 
        return ipcFiles;  //返回向量空路径
    }

    //对于directoryPath指定的目录及其所有子目录中的每一个文件和目录项,执行循环体内的代码。
    //整个循环会遍历 directoryPath 指定的目录及其所有子目录中的文件和目录项。使用 entry 来访问当前遍历到的目录项的信息,例如它的路径、文件名等。
    for (const auto &entry : fs::recursive_directory_iterator(directoryPath)){
         //entry指代fs::recursive_directory_iterator迭代器返回的每一个目录项
        if (fs::is_regular_file(entry.status()) && isIPCFile(entry.path()) && foundFiles.find(entry.path()) == foundFiles.end()) { 
        //如果entry指向的文件是一个常规文件,且扩展名为".ipc",且该文件未被添加到foundFiles集合中,则将文件路径添加到ipcFiles向量中,并标记为已找到
            ipcFiles.push_back(entry.path());//将entry指向的文件路径添加到ipcFiles向量vector的末尾
            foundFiles.insert(entry.path()); //标记该文件为已找到         
            //将找到的文件路径添加到两个容器中去
        }       
    }
    return ipcFiles;
}


/**
 * @brief 删除指定目录下旧的截图文件夹
 *
 * 该函数会遍历指定目录下的所有子目录,并根据最后修改时间进行排序。
 * 然后删除除了最新的keepCount个目录之外的所有目录。
 *
 * @param baseDir 需要处理的目录路径
 * @param keepCount 需要保留的最新目录数量,默认为2
 */
void deleteOldScreenshotDirectories(const fs::path &baseDir, int keepCount = 2)
{                                          // 定义函数deleteoldScreenshotDirectories
    std::vector<fs::directory_entry> dirs; // 创建一个vector容器,用于存储baseDir目录下的所有子目录
    for (const auto &entry : fs::directory_iterator(baseDir))
    { // 遍历baseDir目录下的所有子目录
        if (fs::is_directory(entry.status()))
        {                          // 如果entry指向的文件是一个目录
            dirs.push_back(entry); // 将entry指向的文件路径添加到dirs容器中
        }
    }

    std::sort(dirs.begin(), dirs.end(), [](const fs::directory_entry &a, const fs::directory_entry &b) { // 对dirs容器中的所有目录进行排序
        return fs::last_write_time(a) > fs::last_write_time(b);                                          // 按照最后修改时间进行排序
    });

    for (size_t i = 0; i < dirs.size() - keepCount; ++i)
    {                                   // 删除dirs容器中最后keepCount个目录
        fs::remove_all(dirs[i].path()); // 删除dirs容器中第i个目录
    }
}

/**
 * @file main.cpp
 * @brief 主程序入口,负责搜索指定目录下的IPC文件,并对每个文件进行截图处理。
 * @details 本程序会无限循环检查指定目录下的IPC文件,对每个找到的文件创建一个目录用于保存截图,
 *          然后调用截图函数进行处理。同时,程序会定期删除旧的截图目录以避免空间占用过多。
 * @author [你的名字]
 * @version 1.0
 * @date [日期]
 * @copyright 版权所有 (C) [年份] [你的名字或公司名]
 * @warning 本程序设计为无限循环运行,请确保在适当的时候手动停止。
 */
int main()
{
    // 搜索指定目录下的IPC文件
    fs::path directoryPath = "D:/tVideo";       // 使用directoryPath为实际的目录路径
    fs::path baseScreenshotDir = "D:/tScreenshots"; // 使用baseScreenshotDir为实际的截图目录路径
    std::set<fs::path> foundFiles; // 用于存储已经找到的文件路径,以避免重复添加

    // 确保截图目录存在
    if (!fs::exists(baseScreenshotDir)) {
        fs::create_directories(baseScreenshotDir);
    }

    while (true) { // 用于无限循环检查并处理
        // 查找IPC文件
        std::vector<fs::path> ipcFiles = findIPCFiles(directoryPath, foundFiles);

        // 用于处理找到的IPC文件
        for (const auto& ipcFile : ipcFiles) {//循环ipcFiles向量中的每一个文件
            //创建一个目录用于保存截图
            fs::path videoName = ipcFile.stem(); // 获取视频名称
            fs::path outputDir = baseScreenshotDir / videoName.string();//用于保存截图的目录
            fs::create_directories(outputDir);//用于保存截图的目录

            // 用于截取截图
            takeScreenshots(ipcFile, outputDir);
        }

        // 删除旧的截图目录
        deleteOldScreenshotDirectories(baseScreenshotDir);

        // 睡眠一段时间,然后再次检查
        std::this_thread::sleep_for(std::chrono::hours(1)); // 确保程序不会占用过多资源
    }

    return 0;
}

  • 写回答

26条回答 默认 最新

  • 叫兽-郭老师 新星创作者: Java技术领域 2024-12-27 16:57
    关注

    🕒下午好,题主!目前:🟢[在线]
    📢参考通义千问、由郭老师整理提供!
    📣今日诗号:桃李春风一杯酒,江 湖夜雨十年灯。

    你提供的代码有几个潜在的问题需要解决。我会逐步分析并提出解决方案。

    1. 修正 isIPCFile 函数

    你当前的 isIPCFile 函数检查的是 .mp4 文件,但你在注释中提到的是 .ipc 文件。你需要修正这个函数。

    bool isIPCFile(const fs::path &filePath)
    {
        return filePath.extension() == ".mp4"; // 修改为 ".ipc"
    }
    

    2. 修正 takeScreenshots 函数

    ffmpeg 命令中的 -vf fps=1/0.2 可能会导致一些问题,因为 fps 的值应该是正数。你可以改为 fps=5(即每秒5帧)来实现每0.2秒截取一帧的效果。

    void takeScreenshots(const fs::path &videoPath, const fs::path &outputdir)
    {
        std::string command = "ffmpeg -i \"" + videoPath.string() + "\" -vf fps=5 " + outputdir.string() + "/image%04d.png";
        int result = std::system(command.c_str());
        if (result != 0) {
            std::cerr << "Error taking screenshots for " << videoPath << '\n';
        }
    }
    

    3. 修正 findIPCFiles 函数

    确保你正确地遍历目录并检查文件。

    std::vector<fs::path> findIPCFiles(const fs::path &directoryPath, std::set<fs::path> &foundFiles)
    {
        std::vector<fs::path> ipcFiles;
        if (!fs::exists(directoryPath) || !fs::is_directory(directoryPath)) {
            std::cerr << "Error: Directory does not exist or is not a directory.\n";
            return ipcFiles;
        }
        for (const auto &entry : fs::recursive_directory_iterator(directoryPath)) {
            if (fs::is_regular_file(entry.status()) && isIPCFile(entry.path()) && foundFiles.find(entry.path()) == foundFiles.end()) {
                ipcFiles.push_back(entry.path());
                foundFiles.insert(entry.path());
            }
        }
        return ipcFiles;
    }
    

    4. 修正 deleteOldScreenshotDirectories 函数

    确保你正确地删除旧的截图目录。

    void deleteOldScreenshotDirectories(const fs::path &baseDir, int keepCount = 2)
    {
        std::vector<fs::directory_entry> dirs;
        for (const auto &entry : fs::directory_iterator(baseDir)) {
            if (fs::is_directory(entry.status())) {
                dirs.push_back(entry);
            }
        }
        std::sort(dirs.begin(), dirs.end(), [](const fs::directory_entry &a, const fs::directory_entry &b) {
            return fs::last_write_time(a) > fs::last_write_time(b);
        });
        for (size_t i = 0; i < dirs.size() - keepCount; ++i) {
            fs::remove_all(dirs[i].path());
        }
    }
    

    5. 完整的 main 函数

    确保 main 函数正确地调用其他函数。

    #include <iostream>
    #include <filesystem>
    #include <fstream>
    #include <vector>
    #include <string>
    #include <set>
    #include <chrono>
    #include <cstdlib>
    #include <algorithm>
    
    namespace fs = std::filesystem;
    
    bool isIPCFile(const fs::path &filePath)
    {
        return filePath.extension() == ".mp4";
    }
    
    void takeScreenshots(const fs::path &videoPath, const fs::path &outputdir)
    {
        std::string command = "ffmpeg -i \"" + videoPath.string() + "\" -vf fps=5 " + outputdir.string() + "/image%04d.png";
        int result = std::system(command.c_str());
        if (result != 0) {
            std::cerr << "Error taking screenshots for " << videoPath << '\n';
        }
    }
    
    std::vector<fs::path> findIPCFiles(const fs::path &directoryPath, std::set<fs::path> &foundFiles)
    {
        std::vector<fs::path> ipcFiles;
        if (!fs::exists(directoryPath) || !fs::is_directory(directoryPath)) {
            std::cerr << "Error: Directory does not exist or is not a directory.\n";
            return ipcFiles;
        }
        for (const auto &entry : fs::recursive_directory_iterator(directoryPath)) {
            if (fs::is_regular_file(entry.status()) && isIPCFile(entry.path()) && foundFiles.find(entry.path()) == foundFiles.end()) {
                ipcFiles.push_back(entry.path());
                foundFiles.insert(entry.path());
            }
        }
        return ipcFiles;
    }
    
    void deleteOldScreenshotDirectories(const fs::path &baseDir, int keepCount = 2)
    {
        std::vector<fs::directory_entry> dirs;
        for (const auto &entry : fs::directory_iterator(baseDir)) {
            if (fs::is_directory(entry.status())) {
                dirs.push_back(entry);
            }
        }
        std::sort(dirs.begin(), dirs.end(), [](const fs::directory_entry &a, const fs::directory_entry &b) {
            return fs::last_write_time(a) > fs::last_write_time(b);
        });
        for (size_t i = 0; i < dirs.size() - keepCount; ++i) {
            fs::remove_all(dirs[i].path());
        }
    }
    
    int main()
    {
        fs::path directoryPath = "D:/tVideo";
        fs::path baseScreenshotDir = "D:/tScreenshots";
        std::set<fs::path> foundFiles;
    
        if (!fs::exists(baseScreenshotDir)) {
            fs::create_directories(baseScreenshotDir);
        }
    
        while (true) {
            std::vector<fs::path> ipcFiles = findIPCFiles(directoryPath, foundFiles);
            for (const auto& ipcFile : ipcFiles) {
                fs::path videoName = ipcFile.stem();
                fs::path outputDir = baseScreenshotDir / videoName.string();
                fs::create_directories(outputDir);
                takeScreenshots(ipcFile, outputDir);
            }
            deleteOldScreenshotDirectories(baseScreenshotDir);
            std::this_thread::sleep_for(std::chrono::hours(1));
        }
    
        return 0;
    }
    

    以上是完整的代码和修正思路。请确保你的环境支持这些库和函数,并且路径和文件名格式正确。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(25条)

报告相同问题?

问题事件

  • 系统已结题 1月7日
  • 已采纳回答 12月30日
  • 赞助了问题酬金15元 12月27日
  • 创建了问题 12月27日