首页 > 编程语言 >C++文件系统操作6 - 跨平台实现文件和文件夹的拷贝

C++文件系统操作6 - 跨平台实现文件和文件夹的拷贝

时间:2024-07-26 22:28:21浏览次数:16  
标签:return str dstpath C++ 跨平台 文件夹 file srcpath include

1. 关键词

C++ 文件系统操作 拷贝文件 拷贝文件夹 跨平台

2. fileutil.h


#pragma once

#include <string>
#include <cstdio>
#include <cstdint>
#include "filetype.h"
#include "filepath.h"

namespace cutl
{

    /**
     * @brief The file guard class to manage the FILE pointer automatically.
     * file_guard object can close the FILE pointer automatically when his scope is exit.
     */
    class file_guard
    {
    public:
        /**
         * @brief Construct a new file guard object
         *
         * @param file the pointer of the FILE object
         */
        explicit file_guard(FILE *file);

        /**
         * @brief Destroy the file guard object
         *
         */
        ~file_guard();

        /**
         * @brief Get the FILE pointer.
         *
         * @return FILE*
         */
        FILE *getfd() const;

    private:
        FILE *file_;
    };

    /**
     * @brief Copy a file or symbolic link(shortcut on windows).
     *
     * @param srcpath the filepath of the source file or symbolic link to be copied
     * @param dstpath the filepath of the destination file or symbolic link to be copied to
     * @param attributes whether to copy the file attributes, default is false.
     * If true, means copy the file attributes, like the 'cp -p' command.
     * @return true if the file or symbolic link is copied successfully, false otherwise.
     * @note If the destination file or directory already exists, it will be overwritten.
     * @return false
     */
    bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes = false);

    // copy directory recursively
    /**
     * @brief Copy a directory recursively.
     *
     * @param srcdir the filepath of the source directory to be copied
     * @param dstdir the filepath of the destination directory to be copied to
     * @return true if the directory is copied successfully, false otherwise.
     * @note If the destination directory already exists, it will be overwritten.
     */
    bool copydir(const filepath &srcdir, const filepath &dstdir);

} // namespace cutl

3. fileutil.cpp

#include <cstdio>
#include <map>
#include <iostream>
#include <cstring>
#include <sys/stat.h>
#include "fileutil.h"
#include "inner/logger.h"
#include "inner/filesystem.h"
#include "strutil.h"

namespace cutl
{
    file_guard::file_guard(FILE *file)
        : file_(file)
    {
    }

    file_guard::~file_guard()
    {
        if (file_)
        {
            // CUTL_DEBUG("close file");
            int ret = fclose(file_);
            if (ret != 0)
            {
                CUTL_ERROR("fail to close file, ret" + std::to_string(ret));
            }
            file_ = nullptr;
        }
        // ROBOLOG_DCHECK(file_ == nullptr);
    }

    FILE *file_guard::getfd() const
    {
        return file_;
    }

    bool copyfile(const filepath &srcpath, const filepath &dstpath, bool attributes)
    {
        // CUTL_INFO("file type: " + std::to_string(srcpath.type()) + ", " + filetype_flag(srcpath.type()) + ", " + srcpath.str() + ", dstpath:" + dstpath.str());

        // copy file content
        if (srcpath.isfile())
        {
            if (dstpath.exists())
            {
                // remove if exists
                removefile(dstpath);
            }
            file_guard frd(fopen(srcpath.str().c_str(), "rb"));
            if (frd.getfd() == nullptr)
            {
                CUTL_ERROR("open file failed, " + srcpath.str());
                return false;
            }
            file_guard fwt(fopen(dstpath.str().c_str(), "wb"));
            if (fwt.getfd() == nullptr)
            {
                CUTL_ERROR("open file failed, " + dstpath.str());
                return false;
            }

            static constexpr size_t buf_size = 8 * 1024;
            uint8_t buffer[buf_size] = {0};
            size_t read_len = 0;
            size_t write_len = 0;
            while ((read_len = fread(buffer, 1, buf_size, frd.getfd())) > 0)
            {
                write_len = fwrite(buffer, 1, read_len, fwt.getfd());
                if (write_len != read_len)
                {
                    CUTL_ERROR("write file failed, only write " + std::to_string(write_len) + ", read_len:" + std::to_string(read_len));
                    return false;
                }
            }
            // flush file to disk
            int ret = fflush(fwt.getfd());
            if (0 != ret)
            {
                CUTL_ERROR("fail to flush file:" + dstpath.str());
                return false;
            }
            if (!file_sync(fwt.getfd()))
            {
                CUTL_ERROR("file_sync failed for " + dstpath.str());
                return false;
            }
        }
        else if (srcpath.issymlink())
        {
            if (dstpath.exists())
            {
                // remove if exists
                file_removelink(dstpath.str());
            }
            auto link_path = file_readlink(srcpath.str());
            if (link_path.empty())
            {
                CUTL_ERROR("readlink failed for " + srcpath.str());
                return false;
            }
            if (!file_createlink(link_path, dstpath.str()))
            {
                CUTL_ERROR("createlink failed for " + dstpath.str());
                return false;
            }
        }
        else
        {
            CUTL_ERROR("not a file or symlink, cannot copy: [" + filetype_flag(srcpath.type()) + "]" + srcpath.str());
            return false;
        }

        // copy file attributes
        if (attributes && srcpath.isfile())
        {
            return copy_attributes(srcpath.str(), dstpath.str());
        }

        return true;
    }

    // https://www.cnblogs.com/harrypotterjackson/p/12113382.html
    bool copydir(const filepath &srcdir, const filepath &dstdir)
    {
        if (!srcdir.isdir())
        {
            CUTL_ERROR("srcdir is not a directory: " + srcdir.str());
            return false;
        }

        if (!dstdir.exists() && !createdir(dstdir, true))
        {
            CUTL_ERROR("createdir failed for " + dstdir.str());
            return false;
        }

        auto filelist = list_files(srcdir, filetype::all, true);
        for (size_t i = 0; i < filelist.size(); i++)
        {
            auto file = filelist[i];
            auto src_file = file.filepath;
            auto reletive_path = src_file.substr(srcdir.str().length() + 1);
            auto dstpath = dstdir.join(reletive_path);
            auto srcpath = cutl::path(src_file);
            if (file.type == filetype::file || file.type == filetype::symlink)
            {
                if (!copyfile(srcpath, dstpath, true))
                {
                    return false;
                }
            }
            else if (file.type == filetype::directory)
            {
                if (!createdir(dstpath, true))
                {
                    return false;
                }

                if (!copy_attributes(src_file, dstpath.str(), true))
                {
                    return false;
                }
            }
            else
            {
                CUTL_WARN("the file cannot be copy: [" + filetype_flag(srcpath.type()) + "]" + srcpath.str());
                continue;
            }
        }

        return true;
    }

4. filesystem_win.h


#include <vector>
#include <string>

#pragma once

namespace cutl
{
    // 拷贝文件/文件夹的属性,isdir参数只在windows下生效,Unix-like系统下不起作用
    bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir = false);
} // namespace cutl

5. filesystem_win.cpp

#if defined(_WIN32) || defined(__WIN32__)

#include <io.h>
#include <direct.h>
#include <Windows.h>
#include <stdlib.h>
#include "strutil.h"
#include "filesystem.h"
#include "logger.h"

namespace cutl
{
    bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir)
    {
        // 获取 修改访问、修改时间
        FILETIME t_create, t_access, t_write;
        HANDLE h_src = NULL;
        if (isdir)
        {
            // 文件夹
            h_src = CreateFile(
                srcpath.c_str(),
                GENERIC_READ,
                FILE_SHARE_READ | FILE_SHARE_DELETE,
                NULL,
                OPEN_EXISTING,
                FILE_FLAG_BACKUP_SEMANTICS,
                NULL);
        }
        else
        {
            h_src = CreateFileA(
                srcpath.c_str(),
                GENERIC_READ,
                0,
                NULL,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
                NULL);
        }

        if (h_src == INVALID_HANDLE_VALUE)
        {
            CUTL_ERROR("Failed to open file " + srcpath + ", error code: " + std::to_string(GetLastError()));
            CloseHandle(h_src);
            return false;
        }
        if (!GetFileTime(h_src, &t_create, &t_access, &t_write))
        {
            CUTL_ERROR("Failed to get file times for " + srcpath + ", error code: " + std::to_string(GetLastError()));
            CloseHandle(h_src);
            return false;
        }
        CloseHandle(h_src);

        // 设置 修改访问、修改时间
        HANDLE h_dst = NULL;
        if (isdir)
        {
            h_dst = CreateFile(
                dstpath.c_str(),
                GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_READ | FILE_SHARE_DELETE,
                NULL,
                OPEN_EXISTING,
                FILE_FLAG_BACKUP_SEMANTICS,
                NULL);
        }
        else
        {
            h_dst = ::CreateFileA(
                dstpath.c_str(),
                // GENERIC_WRITE,
                // GENERIC_READ | GENERIC_WRITE,
                GENERIC_ALL,
                0,
                NULL,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL,
                NULL);
        }

        if (h_dst == INVALID_HANDLE_VALUE)
        {
            CUTL_ERROR("Failed to open file " + dstpath + ", error code: " + std::to_string(GetLastError()));
            CloseHandle(h_dst);
            return false;
        }
        if (!SetFileTime(h_dst, &t_create, &t_access, &t_write))
        {
            CUTL_ERROR("Failed to set file times for " + dstpath + ", error code: " + std::to_string(GetLastError()));
            CloseHandle(h_dst);
            return false;
        }
        CloseHandle(h_dst);

        // 修改文件属性
        // 注意:文件访问权限的属性设置要放在文件时间的修改后面,因为只读权限的文件不允许修改时间
        DWORD attributes = GetFileAttributesA(srcpath.c_str());
        if (attributes == INVALID_FILE_ATTRIBUTES)
        {
            CUTL_ERROR("Failed to get file attributes for " + srcpath + ", error code: " + std::to_string(GetLastError()));
            return false;
        }
        if (!SetFileAttributesA(dstpath.c_str(), attributes))
        {
            CUTL_ERROR("Failed to set file attributes for " + dstpath + ", error code: " + std::to_string(GetLastError()));
            return false;
        }
        return true;
    }
} // namespace cutl

#endif // defined(_WIN32) || defined(__WIN32__)

6. filesystem_unix.cpp

#if defined(_WIN32) || defined(__WIN32__)
// do nothing
#else

#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stack>
#include <cstring>
#include <utime.h>
#include <stdlib.h>
#include <sys/time.h>
#include "filesystem.h"
#include "inner/logger.h"

namespace cutl
{
    bool copy_attributes(const std::string &srcpath, const std::string &dstpath, bool isdir)
    {
        struct stat attr_of_src;
        int ret = lstat(srcpath.c_str(), &attr_of_src);
        if (ret != 0)
        {
            CUTL_ERROR("lstat error. srcpath:" + srcpath + ", error:" + strerror(errno));
            return false;
        }

        // 修改文件属性
        ret = chmod(dstpath.c_str(), attr_of_src.st_mode);
        if (ret != 0)
        {
            CUTL_ERROR("chmod error. dstpath:" + dstpath + ", error:" + strerror(errno));
            return false;
        }
        // 修改文件用户组
        ret = chown(dstpath.c_str(), attr_of_src.st_uid, attr_of_src.st_gid);
        if (ret != 0)
        {
            CUTL_ERROR("chown error. dstpath:" + dstpath + ", error:" + strerror(errno));
            return false;
        }

        // 修改文件访问、修改时间
        if (S_ISLNK(attr_of_src.st_mode))
        {
            // TODO: 编译还有问题,需要确定编译宏
            // struct timeval time_buf[2];
            // time_buf[0].tv_sec = attr_of_src.st_atim.tv_sec;
            // time_buf[0].tv_usec = attr_of_src.st_atim.tv_nsec / 1000;
            // time_buf[1].tv_sec = attr_of_src.st_mtim.tv_sec;
            // time_buf[1].tv_usec = attr_of_src.st_mtim.tv_nsec / 1000;
            // ret = lutimes(dstpath.c_str(), time_buf);
            // if (ret != 0)
            // {
            //     CUTL_ERROR("lutimes error. dstpath:" + dstpath + ", error:" + strerror(errno));
            //     return false;
            // }
        }
        else
        {
            struct utimbuf tbuf;
            tbuf.actime = attr_of_src.st_atime;
            tbuf.modtime = attr_of_src.st_mtime;
            ret = utime(dstpath.c_str(), &tbuf);
            if (ret != 0)
            {
                CUTL_ERROR("utime error. dstpath:" + dstpath + ", error:" + strerror(errno));
                return false;
            }
        }

        return true;
    }
} // namespace cutl

#endif // defined(_WIN32) || defined(__WIN32__)

7. 源码地址

更多详细代码,请查看本人写的C++ 通用工具库: common_util, 本项目已开源,代码简洁,且有详细的文档和Demo。


【SunLogging】 扫码二维码,关注微信公众号,阅读更多精彩内容

标签:return,str,dstpath,C++,跨平台,文件夹,file,srcpath,include
From: https://www.cnblogs.com/luoweifu/p/18326385

相关文章

  • 【C++/STL】map和set介绍
    ......
  • C++11 emplace系列函数:性能提升的秘密武器
    C++11引入的emplace系列函数(如emplace_back、emplace、emplace_hint等)为STL容器提供了一种更高效的元素插入方式。这些函数不仅可以提高代码的性能,还能让我们的代码更加简洁优雅。今天,我们将深入探讨emplace函数的优势,并通过实例来展示它们的强大之处。emplace函数的主要优势......
  • C++初学者指南-6.函数对象--lambdas(基础)
    C++初学者指南-6.函数对象–lambdas(基础)文章目录C++初学者指南-6.函数对象--lambdas(基础)提醒:函数类和对象Lambdas变量捕获保存闭包通用Lambdas(C++14)广义捕获(C++14)相关内容幻灯片提醒:函数类和对象类至少提供一个operator()(…){…}函数能像一个......
  • 大牛直播SDK跨平台RTMP直播推送模块技术设计和功能列表
    整体架构大牛直播SDK跨平台RTMP直播推送模块,始于2015年,支持Windows、Linux(x64_64架构|aarch64)、Android、iOS平台,支持采集推送摄像头、屏幕、麦克风、扬声器、编码前、编码后数据对接,功能强大,性能优异,配合大牛直播SDK的SmartPlayer播放器,轻松实现毫秒级的延迟体验,满足大多数行......
  • Visual C++ 官方版下载与安装教程(微软常用运行库合集|DLL报错必装)
    前言MicrosoftVisualC++Redistributable(简称MSVC,VB/VC,系统运行库)是Windows操作系统应用程序的基础类型库组件。此版VisualC++运行库组件合集(微软常用运行库合集)由国内封装爱好者@Dreamcast打包而成,整合VisualC++组件安装包运行库所有版本,提供图形安装界面,可自选更新V......
  • C++中的内存管理
    目录一.C/C++的内存分布二.C++内存管理方式 1.new/delete操作内置类型2.new/delete操作自定义类型 三.operatornew与operatordelete函数 四.new和delete的实现原理1.实现内置类型 2.实现自定义类型五.定位new表达式(placement-new)六.malloc/free......
  • C++优先队列 涵盖各种易错,技巧,应用和原理(附手写代码)
    当然也可以不看==> 阅读我的文章前请务必先阅读此文章! 都是废话这个文章里有视频,不知道为什么一点开文章就会播放,可以先翻到最后暂停一下再回来看目录阅读文章须知引言优先队列的概念优先队列的创建优先队列的操作*还有一些不常用的:优先队列的技巧如果类型是结构......
  • C++queue,deque浅显了解及运用(信息学竞赛专用)
    当然也可以不看==> 阅读我的文章前请务必先阅读此文章! 都是废话目录阅读文章须知引言队列(queue)队列简介​编辑队列的创建队列的操作手写队列双端队列(deque)双端队列简介双端队列的创建双端队列的操作 手写双端队列(原理)写在最后阅读文章须知为了得到......
  • Windows11重置后出现Windows.old文件夹无法删除,报错C:\Windows.old\WINDOWS\System
    问题:Windows11重置后出现Windows.old文件夹无法删除,报错C:\Windows.old\WINDOWS\System32\WDI-目录不是空的。网上的各种方法均不奏效:https://baike.baidu.com/item/windows.old/2431751?fr=ge_ala最终的解决方法:发现在WDI文件夹下是空的,于是在这个文件夹下面建立了......
  • C#调用C++的dll方法
    C#调用C++的dll方法有时候用一些硬件厂家的库函数,厂家没有支持C#的,就只有C、C++语言,这个时候只能将C、C++编译成dll文件,然后用C#来调用这些接口。下面使用环境为vs2010,win32,x86C++打包成为dll首先创建一个win32的C++项目然后点击向导中的dll然后在这个文件中编写dll的函数......