我采用了处理的文件大小与总文件大小比对的方式来判断是否到达了文件尾。然后用剩余的文件大小与缓存大小比对,来决定是继续分段读取,还是将最后不足一段的数据按照剩余大小读取。
每一段处理完后,已经处理的数据量增加,剩余处理的数据量减少。我们这里就是讲一下细节问题,供新手去实现参考。
我们要读取文件数据,使用ReadFile,读取文件要指定缓冲区地址,前面已经准备好了,然后也确定了要读取的数据量,还有一个就是实际读取的数据量。为了简化处理,如果实际读取的数据量和要读取的大小不一致,则重新读取。
需要注意的是,文件读写之后,文件指针自动向后移动了。要重新读取文件,需要自己将文件指针向前移动到原来的位置。移动的长度则是实际读取的字节数这个长度。移动文件指针使用SetFilePointer函数,对于SetFilePointer的使用,可以参考《设置文件指针的函数SetFilePointer的分析》。我们以当前位置为出发点,传入负数,表示向前移动,这样就可以恢复指针的位置,重新读取。一般情况下,要读取的数量和实际数量是会一致的。如果要读取的大小比文件大小还大,自然实际读取的数量比预期的小。或者在读取过程中,有点意外,迫使提前结束读取,也只读取一部分。
这一步我们处理了文件读取不正确的情况。确保文件正确的读取了指定的数量的字节数。如果不这样处理,你可以根据每次读取的数量来处理,而不用要求每次都读取指定的长度的数量,可能会提高一些性能,不过也是发生在经常出错的情况。如果一切正常,就进入下一个环节。
这个环节就是加密处理。这里具有更加宽广的用途。这里不仅仅是加密应用,像解码,渲染等等都是会用到的。这里只是拿加密应用而已。我们这里先用一个自定义函数HandleFile来表示对读取出来的这一段数据的处理。然后紧接着就是对文件处理的字节数以及剩余处理的字节数进行加减。
因为我们是分多次处理的,所以每一次读取文件数据的时候,都要将剩余的数据和缓冲区比较,如果不足一个缓冲区大小,那么就按照剩余文件大小来处理。其他的和前面的过程一样。
最后要记得删除缓冲区和关闭文件句柄。关闭文件句柄后,会迫使系统将所有未写入硬盘的数据全部写入硬盘。
这一段过程的代码如下:
HANDLE hFile = CreateFile(L"D:/科洛弗道10号.rmvb", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE ,NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)return;
LARGE_INTEGER FileSize,HandleFileSize,LeftFileSize;
HandleFileSize.QuadPart=0;
if(!GetFileSizeEx(hFile,&FileSize))
{
MessageBox(NULL,L"获取文件大小出错",L"出错",0);
return;
}
LeftFileSize.QuadPart = FileSize.QuadPart;
DWORD dwBufSize = 1024*1024*100;//100MB
char * pBuf = new char[dwBufSize];
DWORD dwRead;//实际读出的数量
DWORD dwWritten;//实际写入的数量
while(HandleFileSize.QuadPart<FileSize.QuadPart)
{
if (LeftFileSize.QuadPart>=dwBufSize)
{
//剩余文件大小大于缓存的大小
ReadFile(hFile,pBuf,dwBufSize,&dwRead,NULL);
if (dwRead<dwBufSize)
{
//只读取了一部分数据
//倒回去重新读取
SetFilePointer(hFile,dwRead*(-1),NULL,FILE_CURRENT);
continue;
}
else
{
//读完了本次的所有的数据,指针自动向后移动了
HandleFile(hFile,pBuf,dwRead,dwWritten);
HandleFileSize.QuadPart+=dwRead;
LeftFileSize.QuadPart-=dwRead;
}
}
else
{
//剩余文件大小不足缓存大小
ReadFile(hFile,pBuf,LeftFileSize.QuadPart,&dwRead,NULL);
if (dwRead<LeftFileSize.QuadPart)
{
//只读取了一部分数据
//倒回去重新读取
SetFilePointer(hFile,dwRead*(-1),NULL,FILE_CURRENT);
continue;
}
else
{
//读完了本次的所有的数据
HandleFile(hFile,pBuf,dwRead,dwWritten);
HandleFileSize.QuadPart+=dwRead;
LeftFileSize.QuadPart-=dwRead;
}
}
}
delete [] pBuf;
CloseHandle(hFile);
最后,我们将焦点放在了文件处理的部分,也就是自定义函数HandleFile。我们可以用最简单的加密方法,即对每一个字节进行一次运算,比如加法、减法、位操作,也可以进行二次运算,如加一个数字再减一个数字,或者更加复杂的加密算法。不过,我们需要做到的是,加密后一定要保证文件数据和原始的文件数据大小一样。这样不增加额外的空间,同时也可以加密。还要确保加密后能够对应解密。你可以使用各种典型的加密,或者自己写一套加密算法。这里你可以任意发挥。
处理数据还是很简单的。然后就是要将处理好的数据写回文件,这样才能让加密生效。我们知道,文件读取数据之后,会自动移动位置,所以,我们写回数据的时候,需要先将指针移到读取本次数据段开始的位置,好让我们的加密后的数据可以覆盖原始数据。如果这里不注意,就会导致后续的数据被覆盖,从而对文件造成了破坏,就不好恢复了。
写回文件的时候,也是和读取文件差不多,也有一个预期要写入的数据量和实际写入的数据量。因为写入直接影响数据是否被破坏,少一个字节都不行,所以我们用了循环不停的去重试。如果不是严重故障,一般不会循环多次。这个也算是一种心理安慰。
加密处理数据和写回数据的代码如下:
void HandleFile(HANDLE& hFile,char* pBuf,DWORD& dwRead,DWORD& dwWritten)
{
//加密处理
for (DWORD i=0;i<dwRead;i++)
{
//加密代码,此处用最简单的加密,注意加密必须能够解密,才能还原文件数据
pBuf[i]++;
}
//指针倒退到本次数据的起始位置
SetFilePointer(hFile,dwRead*(-1),NULL,FILE_CURRENT);
WriteFile(hFile,pBuf,dwRead,&dwWritten,NULL);
//倒回去重新写入文件,直到成功为止
while (dwWritten<dwRead)
{
SetFilePointer(hFile,dwWritten*(-1),NULL,FILE_CURRENT);
WriteFile(hFile,pBuf,dwRead,&dwWritten,NULL);
}
}
虽然这个程序算不上复杂,但是对于新手来说,要写好,还是需要很仔细的。本次非常兴详细的分析出来,是一个绝佳的开发实战引导。下面给出完整的代码,你可以自己试着运行一下,最好能够在看懂代码看懂文章的分析过程,然后自己再独立写出来。
#include <Windows.h>
#include <iostream>
using namespace std;
void HandleFile(HANDLE& hFile,char* pBuf,DWORD& dwRead,DWORD& dwWritten)
{
//加密处理
for (DWORD i=0;i<dwRead;i++)
{
//加密代码,此处用最简单的加密,注意加密必须能够解密,才能还原文件数据
pBuf[i]++;
}
//指针倒退到本次数据的起始位置
SetFilePointer(hFile,dwRead*(-1),NULL,FILE_CURRENT);
WriteFile(hFile,pBuf,dwRead,&dwWritten,NULL);
//倒回去重新写入文件,直到成功为止
while (dwWritten<dwRead)
{
SetFilePointer(hFile,dwWritten*(-1),NULL,FILE_CURRENT);
WriteFile(hFile,pBuf,dwRead,&dwWritten,NULL);
}
}
void main()
{
HANDLE hFile = CreateFile(L"D:/科洛弗道10号.rmvb", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE ,NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)return;
LARGE_INTEGER FileSize,HandleFileSize,LeftFileSize;
HandleFileSize.QuadPart=0;
if(!GetFileSizeEx(hFile,&FileSize))
{
MessageBox(NULL,L"获取文件大小出错",L"出错",0);
return;
}
LeftFileSize.QuadPart = FileSize.QuadPart;
DWORD dwBufSize = 1024*1024*100;//100MB
char * pBuf = new char[dwBufSize];
DWORD dwRead;//实际读出的数量
DWORD dwWritten;//实际写入的数量
while(HandleFileSize.QuadPart<FileSize.QuadPart)
{
if (LeftFileSize.QuadPart>=dwBufSize)
{
//剩余文件大小大于缓存的大小
ReadFile(hFile,pBuf,dwBufSize,&dwRead,NULL);
if (dwRead<dwBufSize)
{
//只读取了一部分数据
//倒回去重新读取
SetFilePointer(hFile,dwRead*(-1),NULL,FILE_CURRENT);
continue;
}
else
{
//读完了本次的所有的数据,指针自动向后移动了
HandleFile(hFile,pBuf,dwRead,dwWritten);
HandleFileSize.QuadPart+=dwRead;
LeftFileSize.QuadPart-=dwRead;
}
}
else
{
//剩余文件大小不足缓存大小
ReadFile(hFile,pBuf,LeftFileSize.QuadPart,&dwRead,NULL);
if (dwRead<LeftFileSize.QuadPart)
{
//只读取了一部分数据
//倒回去重新读取
SetFilePointer(hFile,dwRead*(-1),NULL,FILE_CURRENT);
continue;
}
else
{
//读完了本次的所有的数据
HandleFile(hFile,pBuf,dwRead,dwWritten);
HandleFileSize.QuadPart+=dwRead;
LeftFileSize.QuadPart-=dwRead;
}
}
}
delete [] pBuf;
CloseHandle(hFile);
}
我的代码不是最好的,我相信你会写出更好的代码实现,加油吧。如果有什么问题,请文章底部留言。