使用C++17 标准库处理文件系统文件

2021-06-13  本文已影响0人  FredricZhu

C++ 17 std::filesystem库还是挺顺滑的,使用前需要注意把Visual Studio的默认Cpp标准设置为 C++17,如图所示。


image.png

否则自动提示会比较难受。

这个库需要注意的一点是,std::filesystem::path类重载了类型转换运算符
operator string_type() const,
这个意思就是,凡是需要传std::filesystem::path的地方,都可以直接传 std::string,标准库会帮你处理这一切的。

程序代码如下,
CMakeList.txt


cmake_minimum_required(VERSION 2.6)
project(chat_room)

add_definitions(-std=c++17)


find_package(Boost REQUIRED COMPONENTS
    system
    filesystem
    serialization
    program_options
    thread
    )

include_directories(${Boost_INCLUDE_DIRS})

file( GLOB APP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
foreach( sourcefile ${APP_SOURCES} )
        file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${sourcefile})
    if( "${filename}" STREQUAL "main.cpp")
        string(REPLACE ".cpp" "" file ${filename})
        add_executable(${file}  ${APP_SOURCES})
        target_link_libraries(${file} ${Boost_LIBRARIES})
        target_link_libraries(${file} pthread)
    endif()
endforeach( sourcefile ${APP_SOURCES})

main.cpp

#include "f_copy.h"
#include <filesystem>
#include <iostream>
#include <fstream>
#include <string>

int main(int argc, char* argv[]) {
    std::filesystem::path cur_path = std::filesystem::current_path().parent_path();
    std::filesystem::path textSrcF {cur_path};
    std::filesystem::path textDstF {cur_path};
    textSrcF /= "main.cpp";
    textDstF /= "dst.cpp";
    TextFileCopy(textSrcF, textDstF);

    std::filesystem::path binSrcF {R"(/Users/aabjfzhu/spark-snowflake_2.11-2.8.5-spark_2.4.jar)"};
    std::filesystem::path binDstF {cur_path};
    binDstF /= "spark-snowflake.jar";
    BinaryFileCopy(binSrcF, binDstF);
    return 0;
}

f_copy.h

#ifndef _FREDRIC_F_COPY_H_
#define _FREDRIC_F_COPY_H_

#include <string>

void TextFileCopy(const std::string& srcF, const std::string& dstF); 
void BinaryFileCopy(const std::string& srcF, const std::string& dstF);

#endif

f_copy.cpp

#include "f_copy.h"

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

void TextFileCopy(const std::string& srcF, const std::string& dstF) {
    std::ifstream input{srcF};
    if (!input) {
        std::cout << "Source File not found" << std::endl;
        return;
    }

    std::filesystem::path destPath{dstF};
    if (std::filesystem::exists(destPath)) {
        std::cout << "File already exists, will override the exist file: "
                  << dstF << std::endl;
    }

    std::ofstream output{dstF};
    if (!output) {
        std::cout << "Could not open the dest file" << std::endl;
        return;
    }

    std::string line;
    while (std::getline(input, line)) {
        output << line << std::endl;
    }

    input.close();
    output.close();
}

void BinaryFileCopy(const std::string& srcF, const std::string& dstF) {
    std::ifstream input{srcF, std::ios::in | std::ios::binary};
    if (!input) {
        std::cout << "Could not open the source file" << std::endl;
        return;
    }

    if (std::filesystem::exists(dstF)) {
        std::cout << "File Already exists, will override the exists file: "
                  << dstF << std::endl;
    }

    std::ofstream output{dstF, std::ios::out | std::ios::binary};
    if (!output) {
        std::cout << "Could not open the destination file" << std::endl;
        return;
    }

    auto file_size = std::filesystem::file_size(srcF);
    const unsigned BufferSize = 512;
    char buffer[BufferSize]{};

    std::cout << "Copying: " << srcF;
    // 文件不到一个Buffer大
    if (file_size < BufferSize) {
        if (!input.read(buffer, file_size)) {
            throw std::runtime_error("Error occurred during read operation");
        }
        if (!output.write(buffer, file_size)) {
            throw std::runtime_error("Error occurred during write operation");
        }
    } else {
        // 大文件分为多个块来拷,并打印进度,进度乘10,是为了不让结果看起来都是0,一直不打印
        auto chunks = file_size / BufferSize;
        // 这里用int是因为 剩余大小小于 512,用int就可以了,不需要 std::size_t
        int remaining = file_size % BufferSize;
        int progress{}, oldProgress{};
        for (int i = 0; i < chunks; ++i) {
            if (!input.read(buffer, BufferSize)) {
                throw std::runtime_error(
                    "Error occurred during read operation");
            }

            if (!output.write(buffer, BufferSize)) {
                throw std::runtime_error(
                    "Error occurred during write operation");
            }

            // 先转浮点避免全零,再转整型便于比较
            // 类型转换建议使用 static_cast,不要直接强转
            // 因为static_cast会做类型检查
            progress = static_cast<int>((10 * static_cast<float>(i) / chunks));
            if (progress != oldProgress) {
                std::cout << '.';
            }
            oldProgress = progress;
        }

        // 下一次的读取将会小于 BufferSize
        // 但是buffer 里面可能还有上次读取的残存数据,
        // 所以需要使用memset清空
        // 比较底层的C操作,但是没办法,因为读写二进制必须按char* [byte]处理
        memset(buffer, '\0', BufferSize);

        if (remaining > 0) {
            if (!input.read(buffer, remaining)) {
                throw std::runtime_error(
                    "Error occurred during read operation");
            }
            if (!output.write(buffer, remaining)) {
                throw std::runtime_error(
                    "Error occurred during write operation");
            }
            std::cout << '.';
        }

        std::cout << "Done! " << std::endl;

        input.close();
        output.close();
    }
}
上一篇下一篇

猜你喜欢

热点阅读