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

代码解释

对文件内容进行倒序的最容易的方法是调用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;
}

效果截图


[VC]使用内存映射技术对文件进行倒序_第1张图片

参考资料

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

Demo下载

点击下载

版权

原文地址: 曾是土木人

你可能感兴趣的:([VC]使用内存映射技术对文件进行倒序)