在成功将 mac 由 10.10 升级到 10.12 后,我发现除了新增一个并不怎么好用的 Siri 外,原来支持 NTFS 硬盘的驱动居然也成功失效了。我那块 500 GB 的东芝硬盘,虽不至于成砖,但一块只能读不能写的硬盘,实在让人欲哭无泪。巧的是,最近需要频繁地将一些数据文件( GB 级别)拷贝到其他电脑,而手头又仅剩一些小容量 U 盘。于是,我突然萌生了写一个文件分割器的想法,将大的压缩文件分片后,再用这些小 U 盘搬到到其他电脑上去。

有人会问,这样的软件明明网上有的是,何必自己写呢?没错,我就是这么无聊的人。

需求分析

其实也不用怎么分析,功能非常简单。我需要两个函数(分别用于分割和合成),分割函数的输入是:一个文件、分片数量,输出是:分片文件、一个配置文件(记录分片文件的顺序);合成函数的输入是:配置文件,输出是:完整的数据文件(根据配置,程序会寻找分片文件用于合成)。

基于此,其实要实现的是两个这样的函数:

// 分割文件的函数,第三个参数指定配置文件名称
void segment(string file_name, int segment_num, string json_file);
// 合成文件的函数,参数为分割时生成的配置文件
void merge(string json_file);

配置文件的格式,我使用了 json(其实用简单的字符串记录一下也是可以的)。

另外,为了方便使用,最好再用一个类将两个方法封装一下。

难点分析

这么小的程序会有难点?!其实还是有一丢丢,就是切割文件的时候,由于文件可能太大,因此不能一口气读入内存中,所以这里采用分块的方法,读一小块写一小块。当然啦,速度方面的优化,这里先不考虑了。

程序实现

首先,我们把所有功能放在一个类FileSegment里面实现,对外只暴露上面的两个函数接口。

segment

上面的难度分析已经指出,我们需要分块读取文件,然后分块写入。

首先需要定义分块大小:const int FileSegment::kBlockSize = 1024 * 1024; ,这里设定一个块大小为1 MB。

我们再定义两个辅助函数,用来分块读文件、写文件:

// 从input流中读取size(默认大小kBlockSize)大小的字节到data里面
inline void read_file_in_block(char* data, ifstream &input, int size=kBlockSize) {input.read(data, size);
}
// 从data中将size(默认大小kBlockSize)大小的字节写入到output流
inline void write_file_in_block(char* data, ofstream &output, int size=kBlockSize) {output.write(data, size);
}

这两个函数因为要经常用到,所以把它们作为内联函数使用。

综合这两个辅助函数,我们定义另一个辅助函数,用于从输入文件中将大批量的数据写入到输出文件中:

// 将input流中读取input_size大小的字节内容,写入到output流中
void FileSegment::copy_file(ifstream &input, ofstream &output, size_t input_size) {char* data = new char[kBlockSize];for (size_t block = 0; block < input_size / kBlockSize; block++) {read_file_in_block(data, input);write_file_in_block(data, output);}// 读取剩余的字节size_t left_size = input_size % kBlockSize;if (left_size != 0) {read_file_in_block(data, input, left_size);write_file_in_block(data, output, left_size);}delete [] data;data = nullptr;
}

有了上面的辅助函数后,我们可以聚焦于segment()函数的核心代码部分了。

我们只需要利用copy_file()函数,将源文件分片写入到几个分片文件中即可。

// 分片文件名
vector<string> segment_files;
for (int i = 0; i < segment_num; i++) {segment_files.push_back(file_name + to_string(i+1) + ".tmp");cout << "segment_file --- " << segment_files[i] << endl;
}ifstream src_file_input(file_name);
// 输入文件大小
size_t src_file_size = file_size(src_file_input);
// 分片文件大小
size_t segment_size = src_file_size / segment_num;// 分片输出文件
for (int i = 0; i < segment_num; i++) {ofstream segment_file_output(segment_files[i]);if (i == segment_num-1) {  // 最后一次,要将剩余文件片全部写入size_t left_size = src_file_size % segment_size;copy_file(src_file_input, segment_file_output, segment_size + left_size);} else {copy_file(src_file_input, segment_file_output, segment_size);}segment_file_output.close();
}src_file_input.close();

另外,我们需要将分片文件的文件名和分割顺序等信息写入配置文件中,这里使用json格式,并用这个第三方库来操纵json对象。

const string FileSegment::kSegmentFileNum = "SegmentNum";
const string FileSegment::kSourceFileName = "SourceFileName";
const string FileSegment::kSegmentFiles = "SegmentFiles";ofstream json_output(json_file);
json j;
j[kSegmentFileNum] = segment_num;
j[kSourceFileName] = file_name;
j[kSegmentFiles] = segment_files;   // 这里segment_files是vector对象
json_output << j;
json_output.close();

下面给出segment()函数的完整代码:

void FileSegment::segment(string file_name, int segment_num, string json_file) {// 检查源文件是否存在if (!exist(file_name)) {cout << "file [" << file_name << "] doesn't exist!" << endl;return;}// 检查分片数量是否大于0if (segment_num <= 0) {cout << "segment number should be greater than 0!" << endl;return;}// 分片文件名vector<string> segment_files;for (int i = 0; i < segment_num; i++) {segment_files.push_back(file_name + to_string(i+1) + ".tmp");cout << "segment_file --- " << segment_files[i] << endl;}ifstream src_file_input(file_name);// 输入文件大小size_t src_file_size = file_size(src_file_input);// 分片文件大小size_t segment_size = src_file_size / segment_num;// 分片输出文件for (int i = 0; i < segment_num; i++) {ofstream segment_file_output(segment_files[i]);if (i == segment_num-1) {  // 最后一次,要将剩余文件片全部写入size_t left_size = src_file_size % segment_size;copy_file(src_file_input, segment_file_output, segment_size + left_size);} else {copy_file(src_file_input, segment_file_output, segment_size);}segment_file_output.close();}src_file_input.close();ofstream json_output(json_file);json j;j[kSegmentFileNum] = segment_num;j[kSourceFileName] = file_name;j[kSegmentFiles] = segment_files;json_output << j;json_output.close();
}

merge

有了前面的辅助函数后,merge()函数的实现基本是依葫芦画瓢。首先需要从配置文件中读取出json对象,根据配置信息去合成文件:

json j;if (!exist(json_file)) {cout << "json file [" << json_file << "] doesn't exist!" << endl;return;
}ifstream json_input(json_file);
json_input >> j;// 源文件名
string src_file = j[kSourceFileName];// 检查源文件是否已经存在
if (exist(src_file)) {src_file += ".copy";
}
ofstream result(src_file);// 文件分片数量
int segment_num = j[kSegmentFileNum];
// 分片文件名
vector<string> segment_files = j[kSegmentFiles];

之后,根据分片文件来合成大文件:

// 合并文件
for (auto it = segment_files.begin(); it != segment_files.end(); it++) {cout << "copy file [" << *it << "]" << endl;ifstream seg_input(*it);size_t seg_input_size = file_size(seg_input);  // 计算分片文件大小copy_file(seg_input, result, seg_input_size);seg_input.close();
}

接下来照例给出merge()函数完整实现:

void FileSegment::merge(string json_file) {json j;if (!exist(json_file)) {cout << "json file [" << json_file << "] doesn't exist!" << endl;return;}ifstream json_input(json_file);json_input >> j;// 源文件名string src_file = j[kSourceFileName];// 检查源文件是否已经存在if (exist(src_file)) {src_file += ".copy";}ofstream result(src_file);// 文件分片数量int segment_num = j[kSegmentFileNum];// 分片文件名vector<string> segment_files = j[kSegmentFiles];// 检查文件分片是否齐全for (auto it = segment_files.begin(); it != segment_files.end(); ++it) {if (!exist(*it)) {cout << "segment file [" << *it << "] doesn't exist!" << endl;return; }}// 合并文件for (auto it = segment_files.begin(); it != segment_files.end(); it++) {cout << "copy file [" << *it << "]" << endl;ifstream seg_input(*it);size_t seg_input_size = file_size(seg_input);copy_file(seg_input, result, seg_input_size);seg_input.close();}json_input.close();result.close();
}

main

main()中,直接实例化FileSegment类,通过segment()merge()函数分割或者合成文件。

int main(int argc, char const *argv[]) {FileSegment fs;// 分割data.zip文件,分为4片fs.segment("data.zip", 4, "config.json");// 根据config.json文件合成最终文件fs.merge("config.json");
}

另外,为了方便使用,我特意写了一个解析命令的类InputParser,然后,我们可以按照如下方式使用该程序:

分割文件

./main -s data.zip 4 config.json

合成文件

./main -m config.json

完整工程代码,请看:https://github.com/Jermmy/file_segmentation

测试结果

在我的 mac (双核,2.7 GHz Intel Core i5) 上,将一个 7.35 G 的 zip 文件分割为 10 片,所用时间为 37.7 s。

同样的机器,将上面的 10 片文件合成原来的大文件,所用时间为 31.8 s。

用C++写一个文件分割器相关推荐

  1. VC 写 TXT 文件分割器 附代码

    TXT文件分割器主要将很大的TXT文档分割成预定大小的文档 主要通过cfile类来实现 这就是最后的效果. 选择路径主要通过cfiledlg类来实现 // TODO: Add your control ...

  2. java编写一个文件查找器

    我们学过文件File之后就想写一个文件搜索器我写了两个版本的搜索器,第一个是基于控制台的就是我的查找结果要显示在控制台上,这个比较适合初学者学习,另外我又写了一个基于界面的文件搜索器,用起来比较好看, ...

  3. C语言自制文件分割器(简单)

    记得初中的时候用MP3看电子书,MP3支持的文本文档格式不能超过1M,否则打不开,然后在网上下了一个文件分割器,感觉真的很强大啊(别吐槽,当时真的很白). 等自己学了C语言后,便想自己弄一个文件分割器 ...

  4. 文件分割器,一个读取流,相应多个输出流,并且生成的碎片文件都有有序的编号...

    import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja ...

  5. 用python写一个变声器,要求导入mp4或者mp3文件,将视频中的声音变成女声

    用python写一个变声器,要求导入mp4或者mp3文件,将视频中的声音变成女声 1.安装 pydub 库 2.然后可以使用以下代码导入 mp4 或 mp3 文件并将其变成女声: 1.安装 pydub ...

  6. 用python写一个文件管理程序下载_Python管理文件神器 os.walk

    原标题:Python管理文件神器 os.walk 来自:CSDN,作者:诡途 [导语]:有没有想过用python写一个文件管理程序?听起来似乎没思路?其实是可以的,因为Python已经为你准备好了神器 ...

  7. java 文件分割器_Java分割器

    java 文件分割器 Java Spliterator is one of the four iterators – Enumeration, Iterator, ListIterator and S ...

  8. 怎样写一个拼写检查器-贝叶斯-python

    怎样写一个拼写检查器 Peter Norvig 翻译: Eric You XU 原版:http://norvig.com/spell-correct.html 翻译:http://blog.youxu ...

  9. 【ZT】怎样写一个拼写检查器

    这篇真的写的很棒,用心领会吧! 怎样写一个拼写检查器 Peter Norvig 翻译: Eric You XU 上个星期, 我的两个朋友 Dean 和 Bill 分别告诉我说他们对 Google 的快 ...

最新文章

  1. 分布式id 实现方式
  2. 超图桌面版制作分段专题图学习
  3. Appium下载安装及环境配置
  4. XJOJ - 路径数(最短路+最短路路径数量)
  5. 直播系统开发如何才可以抓住用户眼球
  6. 前端学习(1377):express路由参数
  7. python每个字符后添加空格_python实现指定字符串补全空格的方法
  8. Leetcode每日一题:203.remove-linked-list-elements(移除链表元素)
  9. [重要] Cocos2dx 3.0 PageView ListView 在Android设备下背景显示为绿色的问题的解决方案
  10. c语言函数定义的语法格式,C语言函数 -C语言函数定义的语法格式
  11. 金融公司大量U盾如何管理
  12. Linux环境下利用perl脚本批量筛选VCF文件指定样本
  13. 高等代数中涉及到“正交”的名词总结
  14. 猿创征文|laravel中JWT的使用
  15. VIT ①Python based
  16. spring boot车辆管理系统毕业设计源码031034
  17. 2021全国大学生数学建模竞赛论文格式规范
  18. 手机应用软件下载导航php源码_腾讯地图官方版下载-腾讯地图手机导航9.1.1 最新版...
  19. 适用于linux的解压工具,Linux下的压缩/解压缩工具
  20. 鸿蒙源码导读-01:蓝海与红海

热门文章

  1. java tess4j ddl_JAVA 截图+tess4j识别
  2. 一个域名可以同时在多家服务器商备案吗?
  3. RADOS:一种可扩展高可用的PB级存储集群(Ceph)
  4. 云借阅图书管理系统的测试项目实践
  5. rust使用睡袋_rust怎么弄睡袋 | 手游网游页游攻略大全
  6. Cadence Allegro PCB如何快速放置网表器件
  7. 账号状态存储在MPT中的应用
  8. SBM模型分析全流程
  9. GeoServer样式(style)设置
  10. strcasecmp与stricmp