头文件:
//MemTextFile.h
#pragma once

class CMemTextFile
{
public:
 CMemTextFile(void);
 ~CMemTextFile(void);
public:
 BOOL OpenFile(IN LPCTSTR lpPath);
 BOOL ReadString(IN OUT LPTSTR& lpBufferString);
 void CloseFile();
 DWORD GetFileSize();
private:
 HANDLE m_hFile;
 HANDLE m_hFileMap;
 PBYTE  m_lpbFile;
 DWORD m_dwFileSize;
 DWORD m_dwLastPos;

 TCHAR *m_pBuffer;
 DWORD m_dwBufferSize;
 BOOL m_bIsMalloc;
};


源文件:
//MemTextFile.cpp
#include "StdAfx.h"
#include <Shlwapi.h>
#pragma comment(lib,"Shlwapi.lib")
#include "MemTextFile.h"

#define DEF_MEM_INC 100

CMemTextFile::CMemTextFile(void)
{
}

CMemTextFile::~CMemTextFile(void)
{
 CloseFile();
}

BOOL CMemTextFile::OpenFile( IN LPCTSTR lpPath )
{
 m_hFile=INVALID_HANDLE_VALUE;
 m_hFileMap=NULL;
 m_lpbFile=NULL;
 m_dwFileSize=0;
 m_dwLastPos=0;
 m_dwBufferSize=DEF_MEM_INC;
 m_pBuffer=NULL;
 m_bIsMalloc=FALSE;

 if (!PathFileExists(lpPath))
 {
  return FALSE;
 }
 m_hFile=CreateFile(
  lpPath,//路径
  GENERIC_READ,//读
  FILE_SHARE_READ,//共享类型只读
  NULL,//文件的安全特性
  OPEN_EXISTING,//文件必须已经存在
  FILE_FLAG_SEQUENTIAL_SCAN,//针对连续访问对文件缓冲进行优化
  NULL);//如果不为零,则指定一个文件句柄

 if (INVALID_HANDLE_VALUE==m_hFile)
 {
  return FALSE;
 }

 m_dwFileSize=::GetFileSize(m_hFile,NULL);

 m_hFileMap = CreateFileMapping(m_hFile,
  NULL, //安全对象
  PAGE_READONLY,//以只读方式打开映射
  0,
  0, //用磁盘文件的实际长度
  NULL);//指定文件映射对象的名字
 if (NULL==m_hFileMap)
 {
  return FALSE;
 }

 m_lpbFile = (PBYTE) MapViewOfFile(
  m_hFileMap,
  FILE_MAP_READ,
  0,
  0, //映射整个文件映射对象
  0);//零表示允许windows寻找地址
 if (NULL==m_lpbFile)
 {
  return FALSE;
 }

 return TRUE;
}

void CMemTextFile::CloseFile()
{
 if (m_lpbFile!=NULL)
 {
  UnmapViewOfFile(m_lpbFile);
 }
 if (m_hFileMap!=NULL)
 {
  CloseHandle(m_hFileMap);
 }
 if (m_hFile!=INVALID_HANDLE_VALUE)
 {
  CloseHandle(m_hFile);
 }
 m_hFile=INVALID_HANDLE_VALUE;
 m_hFileMap=NULL;
 m_lpbFile=NULL;
 m_dwFileSize=0;
 m_dwLastPos=0;
 m_bIsMalloc=FALSE;
 if (m_pBuffer!=NULL)
 {
  free(m_pBuffer);
  m_pBuffer=NULL;
 }
 m_dwBufferSize=0;
}

BOOL CMemTextFile::ReadString(IN OUT LPTSTR& lpBufferString)
{
 if (NULL==m_lpbFile)
 {
  return FALSE;
 }

 if (m_dwLastPos>=(m_dwFileSize-1))
 {
  return FALSE;
 }

 if (!m_bIsMalloc)
 {
  m_pBuffer=(TCHAR*)malloc(m_dwBufferSize*sizeof(TCHAR));
  if (m_pBuffer==NULL)
  {
   return FALSE;
  }
  m_bIsMalloc=TRUE;
 }
 memset(m_pBuffer,0,m_dwBufferSize*sizeof(TCHAR));

 DWORD dwCount=0;
 while(m_dwLastPos<(m_dwFileSize-1))
 {
  if (m_lpbFile[m_dwLastPos]=='\r')//回车CR
  {
   ++m_dwLastPos;
   if (m_dwLastPos<(m_dwFileSize-1))
   {
    if (m_lpbFile[m_dwLastPos]=='\n')//回车后紧接着是换行CR LF
    {
     ++m_dwLastPos;
    }
   }
   break;
  }
  else if (m_lpbFile[m_dwLastPos]=='\n')//换行LF
  {
   ++m_dwLastPos;
   break;
  }

  if (dwCount>=m_dwBufferSize)
  {
   m_pBuffer=(TCHAR*)realloc(m_pBuffer,(m_dwBufferSize+DEF_MEM_INC)*sizeof(TCHAR));
   if (m_pBuffer==NULL)
   {
    return FALSE;
   }
   memset(m_pBuffer+m_dwBufferSize,0,DEF_MEM_INC*sizeof(TCHAR));
   m_dwBufferSize=m_dwBufferSize+DEF_MEM_INC;
  }

  m_pBuffer[dwCount++]=(TCHAR)(m_lpbFile[m_dwLastPos]);
  ++m_dwLastPos;
 }

 m_pBuffer[dwCount++]='\n';
 m_pBuffer[dwCount++]='\r';
 lpBufferString=m_pBuffer;

 return TRUE;
}

DWORD CMemTextFile::GetFileSize()
{
 return m_dwFileSize;
}


使用方法:
 
CMemTextFile txt;
 if (!txt.OpenFile(_T("Bigtest2.nc")))
 {
  AfxMessageBox(_T("打开文件失败!!"));
 } 

 TCHAR *szLine=NULL;
 while(txt.ReadString(szLine))
 {
  //TRACE("szLine=%s\n",szLine);
 }
 txt.CloseFile();


性能比较:
硬件环境为E5200CPU,2G内存。
测试文件为40M,200万行的文本文件。

使用CMemTextFile,用时653ms。
使用CStdioFile,用时4120ms。
 

时间用指令RDTSC测得:
__asm
{
   _emit 0x0F;
   _emit 0x31;
}


本文链接地址: 用内存映射读取大文本文件的类
http://qingfengju.com/index.asp?id=4

分类:Win32/C++ 查看次数:12787 发布时间:2009-4-18 20:00:15