目录
一:前言
网络中上传下载文件(网络中文件传输功能)
需要使用到 拆分拼合 文件操作
二:需求
文件的拆分和文件的拼合操作
要求如下
1.程序运行给定一个文件路径
2.写一个拆分文件函数,创建一个文件夹,将给定路径拆分成碎片文件保存在这个文件夹中
3.要求拆分出来的N个碎片文件总大小和原文件相同
4.写一个拼合文件函数,将前面拆分好的碎片文件拼合成一个完整的文件
例如:
一个1.jpg文件,拆分等 到一个文件夹内部N个x.temp碎片文件,
最终可以将这些碎片文件拼合成一个完整的final.jpg,并且这个图片文件依然可以双击查看
拆分:read读一个文件,write写多个文件
拼合:read读多个文件,write写一个文件
主要问题:
1.存储字节设计
//1000个字节 1KB 拆分出来的不是1024而是1000,按照硬盘产商规格来写
//产商制作硬盘是按照1000制作 1024 = 1.03KB
char buf[1000] = { 0 };
2.拆分拼合文件名的排序问题
a.链表排序(指针指向),会出现如下排序 是乱序的
b. 需要排序成 1 2 3 4 5 顺序 【有顺序的才能成为排序】
本来打算使用自带的排序,但是会出现问题 (按照字符串ASCII排序)
由于
//保存所有路径
//list内存空间利用率高,适合频繁插删
list<string> paths;//list容器用来保存多个路径(读多个文件,所有文件有路径,需要保存)
list<string>::iterator iter;//遍历 迭代器
是string字符串,字符串排序是ASCII码排序,会出现如下排序
因此不能使用自带的排序
VS2019中多行注释快捷键:
ctrl + shift + / 注释多行
//paths.sort();
//快速排序 sort(起始位置,结束位置,排序算法函数指针)、
/*
* bool true false
* return 0 交换位置
* return 1 保持不变
*
bool cmp(string &a,string &b)
{
return a>b;
}
sort(paths.begin(),paths.end(),cmp)
大口朝前大在前(从大到小),小口朝前小在前(从小到大)
*/
/*cout << "--------------排序后-----------------------------------" << endl;
for (iter = paths.begin(); iter != paths.end(); iter++)
{
cout << *iter << endl;
}*/
c. 目标是排序成1 2 3 4 5顺序
最后使用如下排序解决问题
//重构 list<string> 路径列表的排序sort
paths.sort(
[](const string& a, const string& b)
{
//字符串排序->整型排序 find得到下标先转char类型 再atoi转整型
return atoi(a.substr(10, a.find(".")).c_str()) < atoi(b.substr(10, b.find(".")).c_str());
}
);
三:实现
文件的拆分+文件的拼合 [代码实现]
目标,将3.jpg图片文件,拆分文件,
拼合文件至 123文件夹下
具体实现如下
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>
#include <fcntl.h>
#include <list>
#include <algorithm>
using namespace std;
//拆分文件:读一个文件,写(创建)多个文件
int SplitFile(char filepath[])
{
int readfd = 0;
int writefd = 0;
//返回值
int resbuf = 0;
//1000个字节 1KB 拆分出来的不是1024而是1000,按照硬盘产商规格来写
//产商制作硬盘是按照1000制作 1024 = 1.03KB
char buf[1000] = { 0 };
char writePath[30] = { 0 };
//第一个文件设置为1
int num = 1;
umask(0);
readfd = open(filepath, O_RDONLY, 0777);
if (readfd < 0)
{
perror("open dir error");
}
else {
while ((resbuf = read(readfd, buf, sizeof(buf))) > 0)
{
//第一个文件:1.temp
sprintf(writePath, "/root/123/%d.temp", num);
cout << "writePath = " << writePath << endl;
//拼接好路径后再open打开
writefd = open(writePath, O_CREAT | O_WRONLY, 0777);
//resbuf读到多少,写多少
int res = write(writefd, buf, resbuf);
if (res > 0)
{
//文件拆分,先关闭写,文件还未读完
close(writefd);
//写一次就一个文件,num++下一个就是 2.temp文件
num++;
//读内容的清空
bzero(buf, sizeof(buf));
//路径的清空,因为在下一次生成一个新的路径 num++
bzero(writePath, sizeof(writePath));
}
}
//文件拆分 先关闭写 后关闭读
close(readfd);
return num;
}
return 0;
}
//拼合文件:读多个文件,写一个文件
int CohereFile(char filepath[])
{
int readfd = 0;
int writefd = 0;
int resbuf = 0;
//一个buf装一个零碎的temp文件
char buf[1000] = { 0 };
//读路径
char writePath[30] = { 0 };
//写路径
char readPath[30] = { 0 };
//保存所有路径
//list内存空间利用率高,适合频繁插删
list<string> paths;//list容器用来保存多个路径(读多个文件,所有文件有路径,需要保存)
list<string>::iterator iter;//遍历 迭代器
umask(0);
//结构体指针DIR
DIR* dir = NULL;
//结构体指针dirent
struct dirent* pdir = NULL;
if ((dir = opendir(filepath)) == NULL)
{
perror("opendir error");
}
else {
while ((pdir = readdir(dir)) != NULL)
{
// /root/123/1.temp /root/123/2.temp /root/123/3.temp
if (strcmp(pdir->d_name, ".") == 0 || strcmp(pdir->d_name, "..") == 0)
{
continue;
}
bzero(readPath, sizeof(writePath));
//完整路径拼接
strcat(readPath, filepath);
strcat(readPath, "/");
strcat(readPath, pdir->d_name);
//各个.temp文件路径 存入链表中
paths.push_back(readPath);
}
cout << "paths.size() = " << paths.size() << endl;
//测试打印路径
//for (iter = paths.begin(); iter != paths.end(); iter++)
//{
// cout << *iter << endl;
//}
// 例如/root/123/1.temp /root/123/10.temp 路径 只要获取1 10
//重构 list<string> 路径列表的排序sort
paths.sort(
[](const string& a, const string& b)
{
//字符串排序->整型排序 find得到下标先转char类型 再atoi转整型
return atoi(a.substr(10, a.find(".")).c_str()) < atoi(b.substr(10, b.find(".")).c_str());
}
);
bzero(writePath, sizeof(writePath));
strcat(writePath, filepath);
strcat(writePath, "/");
strcat(writePath, "CohereFile.jpg");
writefd = open(writePath, O_CREAT | O_WRONLY, 0777);
for (iter = paths.begin(); iter != paths.end(); iter++)
{
//排序后打印路径
cout << *iter << endl;
//迭代器遍历 链表中存放的 路径
readfd = open((*iter).c_str(), O_RDONLY, 0777);
cout << "(*iter).c_str() = " << (*iter).c_str() << endl;
if (readfd < 0)
{
perror("open dir error");
}
else {
cout << "sizeof(buf) = " << sizeof(buf) << endl;
while ((resbuf = read(readfd, buf, sizeof(buf))) != 0)
{
write(writefd, buf, resbuf);
}
close(readfd);
}
}
close(writefd);
return 1;
}
return 0;
}
int main()
{
int x = SplitFile("/root/3.jpg");
if (x > 0)
{
cout << "拆分文件完成" << endl;
cout << "可以开始拼合文件" << endl;
int n = CohereFile("/root/123");
if (n > 0)
{
cout << "拼合文件完成" << endl;
}
else {
cout << "拼合文件失败" << endl;
}
}
else {
cout << "拆分文件失败" << endl;
}
return 0;
}
查看结果:
打开123文件夹,发现文件成功拆分 、文件成功拼合
同时拼合的文件可以双击查看(拆分 拼合 文件 没有问题)