实验七 使用无缓冲的方式实现文件读\写

本次实验旨在熟悉Windows系统的文件读写API,尤其是不使用高速缓存的方式。通过CreateFile()、ReadFile()和WriteFile()函数,实现了从source.txt到nobuffer.txt的内容复制,验证了无缓冲文件操作的正确性。

实验七 使用无缓冲的方式实现文件读\写

一、实验目的

  1. 熟悉Windows系统文件读\写相关API。
  2. 掌握无缓冲方式实现文件读\写相关参数的设置。

二、实验准备

相关API函数的介绍

文件创建

函数CreateFile( ) 用于创建一个新文件,如果文件已经存在,则得到该文件的句柄。该函数的参数dwFalgsAndAttributes决定了文件的传输方式,对于普通的文件传输,可将参数设置为FILE_ATTRIBUTE_NORMAL;而若设置为FILE_FLAG_NO_BUFFERING,表示不使用高速缓存进行文件传输;若同时使用标志FILE_FLAG_NO_BUFFERING和FILE_FLAG_OVERLAPPED,可对文件进行异步传输;若设置为FILE_FLAG_SEQUENTIAL_SCAN,表示使用高速缓存进行文件的传输。

  • 函数原型
HANDLE CreateFile(
    LPCTSTR lpFileName,                            	 //指向文件名的指针
    DWORD dwDesiredAccess,                        	 //读/写访问模式
    DWORD dwShareMode,                           	 //共享模式
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,  	 //指向安全属性的指针
    DWORD dwCreationDisposition,                     //文件存在标志
    DWORD dwFlagsAndAttributes,                      //文件属性
    DWORD hTemplateFile                              //指向访问模板文件的句柄
    ) ;
  • 参数说明
  1. lpFILEName:指向文件名的指针。
  2. dwDesiredAccess:指出访问文件的类型,可以是读访问、写访问、读 / 写访问或查询访问。该参数可以是表4-1中的组合。
  3. dwShareMode:指出文件共享模式。若dwShareMode的值为0,表示目标不能被共享。若要共享文件,可以使用表4-2中的组合。
  • 表4-1 不同值的描述

  • 描述
    0查询访问
    GENERIC_READ读访问,从文件中读出数据,且移动文件指针。当需要对文件进行读写时,该属性可以与GENERIC_WRITE组合使用
    GENERIC_WRITE写访问,将数据写入文件,且移动文件指针。当需要对文件进行读/写时,该属性可以与GENERIC_READ组合使用
  • 表4-2 dwShareMode的值

  • 描述
    FILE_SHARE_DELETE仅当删除访问时,对文件的打开操作才能成功
    FILE_SHARE_READ仅当读访问时,对文件的打开操作才能成功
    FILE_SHARE_WRITE仅当写访问时,对文件的打开操作才能成功
  1. lpSecurityAttributes:指向安全属性的指针。为NULL时,子进程可以继承该安全描述符。
  2. dwCreationDisposition:文件存在标志。指出当文件不存在时,可以对文件进行何种操作。可以取表4-3中的值。
  • 表4-3 dwCreationDisposition 的值
  • 描述
    CREAT_NEW创建新文件。若文件已存在,则该函数调用失败
    CREAT_ALWAYS创建新文件。若文件已存在,则该函数覆盖原文件的内容且清空现有属性
    OPEN_EXISTING打开已存在文件,若文件不存在,则该函数打开失败
    OPEN_ALWAYS若文件存在,则打开该文件,若文件不存在,则以CREAT_NEW方式创建文件
    TRUNCATE_EXISTING打开文件,并将文件的大小截取为0
  1. dwFlagsAndAttributes:指出文件属性和标志。除了FILE_ATTRIBUTE_NORMAL属性之外,参数dwFlagsAndAttributes可以取表4-4中任何属性的组合。参数dwFlagsAndAttributes还可以取表4-5中任何属性的组合。
  • 表4-4 属性描述

  • 属性描述
    FILE_ATTRIBUTE_ARCHIVE文件可以被存档
    FILE_ATTRIBUTE_HIDDEN文件可以被隐藏
    FILE_ATTRIBUTE_NORMAL文件没有其他属性,该属性仅当单独时使用才有效
    FILE_ATTRIBUTE_OFFLINE文件中的数据被脱机存储,文件中的数据不能立即有效
    FILE_ATTRIBUTE_READONLY文件只能读
    FILE_ATTRIBUTE_SYSTEM文件被系统使用
    FILE_ATTRIBUTE_TEMPORARY文件被临时存储
  • 表4-5 属性补充

  • 属性描述
    FILE_FLAG_WRITE_THROUGH系统对文件的任何写操作,当缓冲的内容改变时立即写回磁盘
    FILE_FLAG_OVERLAPPED异步读/写,使用该属性时,文件指针将不被保留
    FILE_FLAG_NO_BUFFERING文件不使用缓冲
    FILE_FLAG_RANDOM_ACCESS文件随机访问
    FILE_FLAG_SEQUENTIAL_SCAN文件被顺序访问
    FILE_FLAG_DELETE_ON_CLOSE当文件句柄关闭时,文件立即被删除
    FILE_FLAG_BACKUP_SEMANTICS文件用于备份或转储
    FILE_FLAG_POSIX_SEMANTICS文件访问遵循POSIX协议。
  1. hTemplateFile:指向访问模板文件的句柄,可以将其设置为空。
    返回值:文件创建成功,该函数返回文件句柄,否则返回INVALID_HANDLE_VALUE,可调用函数GetLastError( )查询失败的原因。
  • 用法举例
  • HANDLE handle;
    handle = CreateFile(“nobuffer.txt”,GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,
    NULL,NULL);
    //使用函数创建一个新文件nobuffer.txt,对该文件只能进行写操作
    

读文件

  • 函数Readfile( ) 从文件指针指示的位置开始读取文件中的数据。如果文件不是用FILE_FLAG_OVERLAPPED属性创建的,则文件指针移动到实际读出字节数所处的位置;如果文件是用FILE_FLAG_OVERLAPPED属性创建的,则文件指针由应用程序来调整其位置。
    函数原型:
 BOOL ReadFILE(
    HANDLE hFile,                              //要读的文件的句柄
    LPVOID lpBuffer,                           //指向文件缓冲区的指针
    DWORD nNumberOfBytesToRead,                //从文件中要读取的字节数
    LPDWORD lpNumberOfBytesRead,               //指向从文件中要读取的字节数的指针
    LPOVERLAPPED lpOverlapped                  //指向OVERLAPPED结构的指针
    ) ;

lpOverlapped:如果文件是用FILE_FLAG_OVERLAPPED属性创建的,则需要此结构;如果文件是用FILE_FLAG_OVERLAPPED属性打开的,则参数lpOverlapped不为NULL,它指向一个OVERLAPPED结构。如果文件是用FILE_FLAG_OVERLAPPED属性创建且参数lpOverlapped为NULL,则该函数不能正确报告读操作是否完成。

如果文件用FILE_FLAG_OVERLAPPED属性打开且参数lpOverlapped不为NULL,则读操作从OVERLAPPED结构中指定的位置开始,且ReadFile( )函数在读操作完成之前返回。此时ReadFile( )返回FALSE,并且GetLastError( )函数返回ERROR_IO_PENDING,即执行读文件操作的进程被挂起,当读操作完成时,进程才继续执行。在OVERLAPPED结构中指定的事件被设置为读操作完成的发送信号状态。

如果文件不用FILE_FLAG_OVERLAPPED属性打开且参数lpOverlapped不为NULL,则读操作从OVERLAPPED结构中指定的位置开始,操作完成时ReadFile( )函数返回。

返回值:

  1. 如果函数调用成功,则返回值为非0值。
  2. 如果返回非0值,且读出的字节数为0,则说明执行读操作时文件的指针出界,此时调用GetLastError( )函数,可得到返回值ERROR_HANDLE_EOF。但若文件用FILE_FLAG_OVERLAPPED属性打开且参数lpOverlapped不为NULL,则ReadFile( )函数返回为FALSE。
  3. 如果函数调用失败,则返回值为0。若要得到更多的错误信息,可调用函数GetLastError( )。

用法举例:

//下面的例子说明如何测试异步读操作的文件结尾。
//设置 OVERLAPPED结构
gOverLapped.Offset    =0;
gOverLapped.OffsetHigh=0;
gOverLapped.hEvent    =NULL;
//执行异步读操作
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, &gOverlapped);
//异步读不成功,进程挂起
if ( !bResult)
{  //处理错误码
	switch (dwError = GetLastError( ))
	{	
		case ERROR_HANDLE_EOF:
		{//文件出界处理
		}
		case ERROR_IO_PENDING:
		{
			//异步I/O进行中
			//做其他事……
			GoDoSomethingElse( );
        	//检查异步读结果
			bResult = GetOverlappedResult( hFile, &gOverlapped, 
            &nBytesRead, FALSE) ;
        	//若不成功
			if ( !bResult )
			{   //处理错误代码
				switch (dwError = GetLastError( ) )
				{
					case ERROR_HANDLE_EOF:
					{	//异步操作期间达到文件尾
					}
    		//处理其他错误码
				}
			}
		}// end case
             //处理其他错误码
	}//end switch
}// end if

写文件
函数WriteFile ( ) 将数据写入文件。函数在文件指针所指的位置完成写操作,写操作完成后,文件指针按实际写入的字节数来调整。

  • 函数原型
BOOL WriteFile(                           
    HANDLE hFile,                            // 要读的文件的句柄
    LPVOID lpBuffer,                         // 指向文件缓冲区的指针
    DWORD nNumberOfBytesToWrite,             // 从文件中要读取的字节数
    LPDWORD lpNumberOfBytesWritten,          // 指向从文件中要读取的字节数的指针
    LPOVERLAPPED lpOverlapped                // 指向 OVERLAPPED结构的指针
    ) ;

返回值:

  1. 如果函数调用成功,则返回值为非0值。
  2. 如果函数调用失败,则返回值为0.若要得到更多的错误信息,可调用函数GetLastError().

关闭文件句柄

函数CloseHandle()关闭与文件相关的句柄,其作用与释放动态申请的内存空间类似,这样可以释放系统资源,使进程安全运行。

函数原型:

BOOL CloseHandle(
HANDLE hObject
);

参数说明:

  • hObject:已打开对象的句柄

返回值:

  1. 如果函数调用成功,则返回值为非0值。
  2. 如果函数调用失败,则返回值为0.若要得到更多的错误信息,可调用函数GetLastError()。

三、实验内容

(一)实验内容

  1. 在File_NoBuffer目录下建立一个source.txt文件,并输入内容
  2. 在建立一个函数,使用该函数将源文件source.txt中的内容读出,再写到目标文件nobuffer.txt中去。
  3. 打开建立的nobuffer.txt文件查看与source.txt文件内容是否相同

(二)主要代码

// File_NoBuffer.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "File_NoBuffer.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
DWORD BufferSize=1024;
char buf[1024];
/////////////////////////////////////////////////////////////////////////////
// The one and only application object

CWinApp theApp;

using namespace std;

void FileReadWrite_NoBuffer(char*source,char*destination);

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;

	printf("Call FileReadWrite_NoBuffer!\n");
	FileReadWrite_NoBuffer("source.txt","nobuffer.txt");

	return nRetCode;
}
void FileReadWrite_NoBuffer(char*source,char*destination)
{
	HANDLE handle_src,handle_dst;
	DWORD NumberOfByteWrite;
	BOOL cycle;
	char*buffer;
	buffer=buf;
	//创建文件
	handle_src=CreateFile(source,
		GENERIC_READ,
		0,
	    NULL,
	    OPEN_EXISTING,
	    FILE_FLAG_NO_BUFFERING,
	    NULL);
	handle_dst=CreateFile(destination,
	    GENERIC_WRITE,
	    NULL,
	    NULL,
	    CREATE_ALWAYS,
  	    NULL,
	    NULL);
	if (handle_src==INVALID_HANDLE_VALUE ||
	    handle_dst==INVALID_HANDLE_VALUE)
	{
        printf("File Create Fail!\n");
	    exit(1);
	}
	cycle=TRUE;
	while(cycle)
	{
		DWORD NumberOfByteRead=BufferSize;
		//读文件
		if(!ReadFile(handle_src,buffer,NumberOfByteRead,&NumberOfByteRead,NULL))
		{
			printf("Read File Error!%d\n",GetLastError());
			exit(1);
		}
		if(NumberOfByteRead<BufferSize)cycle=FALSE;
		//写文件
		if(!WriteFile(handle_dst,buffer,NumberOfByteRead,&NumberOfByteWrite,NULL))
		{
			printf("Write File Error!%d\n",GetLastError());
			exit(1);
		}
	}
	//关闭文件句柄
	CloseHandle(handle_src);
	CloseHandle(handle_dst);
}

四、实验结果和总结

实验结果

  • 在File_NoBuffer目录下建立source.txt文件并输入内容
    image
  • 运行实验,将source.txt的文件内容复制到nobuffer.txt文件下,显示如下代码
    image
  • 运行后,File_NoBuffer目录下建立了nobuffer.txt文件,并显示如下内容
    image

总结

该实验完成无缓冲方式的文件读/写操作。先创建文件source.txt,然后反复从文件source.txt中读出数据块,并写到文件nobuffer.txt中去,直到文件尾为止。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值