使用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();
}
}