[VC]使用内存映射技术对文件进行倒序

原创 2013年12月05日 17:34:53

代码解释

对文件内容进行倒序的最容易的方法是调用C运行期函数_ strrev。与所有C字符串一样,字符串的最后一个字符必须是个0结束符。由于文本文件不以0为结束符,因此FileReverse必须给文件附加一个0。若要进行这样的附加操作,首先要调用GetFileSize函数:
// Get the size of the file (I assume the whole file can be mapped).
	DWORD dwFileSize = GetFileSize(hFile, NULL);
现在已经得到了文件的长度,你能够通过调用CreateFileMapping函数创建文件映射对象。创建的文件映射对象的长度是dwFileSize加一个宽字符的大小(对于0字符来说)。当文件映射对象创建后,该对象的视图就被映射到FileReverse的地址空间。变量pvFile包含了MapViewOfFile函数的返回值,并指向文本文件的第一个字节。
下一步是在文件的结尾处写一个0,并对字符串进行倒序:
		// 文件末尾添加 0,作为字符串的结束符
		PSTR pchANSI = (PSTR) pvFile;
		pchANSI[dwFileSize / sizeof(CHAR)] = 0;
在文本文件中,每一行的结尾都是一个回车符(‘\ r’)后随一个换行符(‘\ n’)。但是,当调用_strrev对文件进行倒序时,这些字符也会被倒序。如果将已经倒序的文本文件加载到文本编辑器,那么出现的每一对“ \ n \ r”字符都必须重新改为它的原始顺序。这个倒序操作是由下面的循环代码进行的:
while (pchANSI != NULL) {
			// We have found an occurrence....
			*pchANSI++ = '\r';   // Change '\n' to '\r'.
			*pchANSI++ = '\n';   // Change '\r' to '\n'.
			pchANSI = strstr(pchANSI, "\n\r"); // Find the next occurrence.
		}
当你观察这样一个简单的代码时,可能很容易忘记你实际上是在对磁盘驱动器上的文件内容进行操作(这显示出内存映射文件的功能是多么大)。
在文件被倒序后, FileReverse便进行清除操作,撤消文件映射对象的视图映象,关闭所有的内核对象句柄。此外,FileReverse必须删除附加给文件结尾处的0字符(记住_strrev并不对结尾的0字符进行倒序)。如果没有删除0字符,那么倒序的文件将会多出一个字符,如果再次调用FileRev函数,将无法使文件还原成它的原始样子。若要删除文件结尾处的0字符,必须后退一步,使用文件管理函数,而不是通过内存映射对文件进行操作。
如果要强制已经倒序的文件在某个位置上结束,就需要将文件指针定位在指定的位置(原始文件的结尾处)并调用SetEndOfFile函数:
// Remove trailing zero character added earlier.
	SetFilePointer(hFile, dwFileSize, NULL, FILE_BEGIN);
	SetEndOfFile(hFile);
注意:SetEndOfFile函数必须在撤消视图的映象并且关闭文件映射对象之后调用,否则,该函数将返回FALSE,GetLastError则返回ERRORUSERMAPPE DFILE。这个
错误表示不能在与文件映射对象相关联的文件上执行文件未尾的操作。

核心代码

BOOL CFileRevDlg::FileReverse()
{
	bool bIsTextUnicode = FALSE;  // Assume text is Unicode
	// Open the file for reading and writing.
	HANDLE hFile = CreateFile(m_DirectoryPath + FILENAME , GENERIC_WRITE | GENERIC_READ ,0,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFile == INVALID_HANDLE_VALUE){
		MessageBox(_T("File could not be opened."));
		return FALSE;
	}

	// Get the size of the file (I assume the whole file can be mapped).
	DWORD dwFileSize = GetFileSize(hFile, NULL);

	// Create the file-mapping object. The file-mapping object is 1 character 
	// bigger than the file size so that a zero character can be placed at the 
	// end of the file to terminate the string (file). Because I don't yet know
	// if the file contains ANSI or Unicode characters, I assume worst case
	// and add the size of a WCHAR instead of CHAR.
	HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 
		0, dwFileSize + sizeof(WCHAR), NULL);

	if (hFileMap == NULL) {
		MessageBox(_T("File map could not be opened."));
		CloseHandle(hFile);
		return FALSE;
	}

	// Get the address where the first byte of the file is mapped into memory.
	PVOID pvFile = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0);

	if (pvFile == NULL) {
		MessageBox(_T("Could not map view of file."));
		CloseHandle(hFileMap);
		CloseHandle(hFile);
		return FALSE;
	}

	// Does the buffer contain ANSI or Unicode?
	int iUnicodeTestFlags = -1;   // Try all tests
	bIsTextUnicode = IsTextUnicode(pvFile, dwFileSize, &iUnicodeTestFlags);
	SetDlgItemText(IDC_TEXTTYPE,bIsTextUnicode ? TEXT("文件编码:Unicode") : TEXT("文件编码:ANSI"));
	if (!bIsTextUnicode) {
		// For all the file manipulations below, we explicitly use ANSI 
		// functions because we are processing an ANSI file.

		// 文件末尾添加 0,作为字符串的结束符
		PSTR pchANSI = (PSTR) pvFile;
		pchANSI[dwFileSize / sizeof(CHAR)] = 0;

		// Reverse the contents of the file.
		_strrev(pchANSI);

		// Convert all "\n\r" combinations back to "\r\n" to 
		// preserve the normal end-of-line sequence.
		pchANSI = strstr(pchANSI, "\n\r"); // Find first "\r\n".

		while (pchANSI != NULL) {
			// We have found an occurrence....
			*pchANSI++ = '\r';   // Change '\n' to '\r'.
			*pchANSI++ = '\n';   // Change '\r' to '\n'.
			pchANSI = strstr(pchANSI, "\n\r"); // Find the next occurrence.
		}

	} else {
		// For all the file manipulations below, we explicitly use Unicode
		// functions because we are processing a Unicode file.

		// Put a zero character at the very end of the file.
		PWSTR pchUnicode = (PWSTR) pvFile;
		pchUnicode[dwFileSize / sizeof(WCHAR)] = 0;

		if ((iUnicodeTestFlags & IS_TEXT_UNICODE_SIGNATURE) != 0) {
			// If the first character is the Unicode BOM (byte-order-mark),
			// 0xFEFF, keep this character at the beginning of the file.
			pchUnicode++;
		}

		// Reverse the contents of the file.
		_wcsrev(pchUnicode);

		// Convert all "\n\r" combinations back to "\r\n" to 
		// preserve the normal end-of-line sequence.
		pchUnicode = wcsstr(pchUnicode, L"\n\r"); // Find first '\n\r'.

		while (pchUnicode != NULL) {
			// We have found an occurrence....
			*pchUnicode++ = L'\r';   // Change '\n' to '\r'.
			*pchUnicode++ = L'\n';   // Change '\r' to '\n'.
			pchUnicode = wcsstr(pchUnicode, L"\n\r"); // Find the next occurrence.
		}
	}	
	// Clean up everything before exiting.
	UnmapViewOfFile(pvFile);
	CloseHandle(hFileMap);

	// Remove trailing zero character added earlier.
	SetFilePointer(hFile, dwFileSize, NULL, FILE_BEGIN);
	SetEndOfFile(hFile);
	CloseHandle(hFile);	
	return TRUE;
}

效果截图


参考资料

《Windows核心编程》第17章:内存管理

Demo下载

点击下载

版权

原文地址:曾是土木人

[转][VC/MFC]关于内存映射文件技术处理大文件的读写

Windows对文件的读写提供了很丰富的操作手段,如:1. FILE *fp, fstearm...; (C/C++)2. CFile, CStdioFile...; (MFC)3. CreateFi...
  • believefym
  • believefym
  • 2006年09月02日 21:46
  • 9395

VC/MFC 封装好的文件内存映射类

一、创建过程 1) // 第一步:创建文件 HANDLE hFile = CreateFileForMapping(_T("MyMemFile.dat"), GENERIC_READ | GENE...
  • mitesi
  • mitesi
  • 2014年04月03日 09:00
  • 1170

利用内存映射文件处理大文件(转载)(修改程序版)

 ----原来作者的程序有些问题,现在文章中的程序已经修改正确---VC中用内存映射文件处理大文件 摘要:本文通过内存映射文件的使用来对大尺寸文件进行访问操作,同时也对内存映射文件的相关概念和一般编程...
  • metasearch
  • metasearch
  • 2008年03月05日 10:08
  • 2675

关于内存映射文件技术处理大文件的读写

Windows对文件的读写提供了很丰富的操作手段,如:1. FILE *fp, fstearm...; (C/C++)2. CFile, CStdioFile...; (MFC)3. CreateFi...
  • seu07201213
  • seu07201213
  • 2006年09月02日 11:57
  • 6128

在linux中使用内存映射(mmap)操作文件的方法

在使用内存映射操作文件之前,我们先按照常规的方式来读写文件,这种方式操作如下: 1,打开或创建文件,得到文件描述符, 2,将内存中的数据以一定的格式和顺序写入文件,或者将文件中的数据以一定的格式和...
  • bzhxuexi
  • bzhxuexi
  • 2015年06月04日 15:22
  • 683

vc++使用内存映射

文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MF...
  • songshu1007
  • songshu1007
  • 2014年05月09日 11:38
  • 884

内存映射文件技术

内存映射文件技术1. 用途和基本操作用于不同进程之间的内存共享操作, 可以将一个物理文件映射到内存当中然后直接利用分配到的或者打开的命名共享内存的地址空间实现资源共享访问2. 相关流程1) 新建命名共...
  • ydfok
  • ydfok
  • 2007年01月16日 11:02
  • 4146

对数组进行重新排序

很多时候,我们从数据库中取出我们要的数据后,并不是就完成了工作,更多的我们需要对数据进行遍历处理等,这时候我们从数据库中获取数据时的排序可能就被打乱了, 需要对数组重新排序,下面就写写汇总到的对一维和...
  • Kit_G
  • Kit_G
  • 2017年08月15日 01:26
  • 239

IPC之Posix内存映射文件详解

1.什么是内存映射文件 内存映射文件,就是把磁盘上的物理文件映射至进程地址空间中,使用内存映射文件的特性是,所有的I/O都是在内核掩盖下完成,我们只需编写存取内存映射区中各个值的代码,也就是不需要...
  • daiyudong2020
  • daiyudong2020
  • 2016年01月10日 20:44
  • 947

VC读取大文件之创建文件映射及文件写入效率测试

文件太大,没法一次读取到内存进行操作?Windows提供了内存映射API来读取大文件,与普通文件读取相比,内存映射效率比较高。 从代码层面上看,从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并...
  • mfcing
  • mfcing
  • 2015年02月11日 15:35
  • 4395
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[VC]使用内存映射技术对文件进行倒序
举报原因:
原因补充:

(最多只允许输入30个字)