zoukankan      html  css  js  c++  java
  • 关于文件读写

    Windows对文件的读写提供了很丰富的操作手段,如:
    1. FILE *fp, fstearm...; (C/C++)
    2. CFile, CStdioFile...; (MFC)
    3. CreateFile, ReadFile...;(API)
    ...

    在处理一般的文件(文本/非文本),这些足够了。然而在处理比较大的文件如
    几十M, 几百M, 甚至上G的文件, 这时再用一般手段处理,系统就显的力不从心了

    要把文件读出,再写进,耗费的是CPU利用率与内存以及IO的频繁操作。这显然是
    令用户难以忍受的

    为了解决这个吃内存,占CPU,以及IO瓶颈,windows核心编程提供了内存映射文件技术
    (Maping File)

    至于Maping File是什么原理,我不多说了,网上转载资源一箩筐,我只想从应用层
    来考虑,怎样用这个技术,实现日常项目中的应用
    举例来说:
    可能项目中,会经常用到一些大量的常量,而这些大量常量用宏来替代写再源文件中
    显然不可取,一般是写在文件中,给常量一些编号,通过编号来索引

    一般文件比较小时候,常用做法也是先预读到内存中,毕竟从内存中读比从文件中读要快(IO操作的瓶颈)
    比较好的做法,读到STL MAP 中去:
    例如一个索引文件:
    SEU07201213=汪洋中的一片叶子
    JIANGSHENG=蒋晟
    SEU07201214=CSDN
    ............
    打开文件,解析=号,在解析方面有CString操作,strtok,strstr, boost 正则表达式匹配等等,但我比较喜欢
    sscanf(szIndex, "%[^=]=%[^=]", sName, sValue);
    sscanf(szIndex, "%[^=]=%s", sName, sValue);
    fscanf(stream, "%[^=]=%[^=]", sName, sValue);
    之类,
    然后再定义一个map:
    map<string, string> m_Map;
    m_Map[sName] = sValue;

    但是文件比较大的时候,笔者做过测试,用上面方法处理一个15M, 25万行的文本文件,占用内存非常
    的高,达70多M,处理的速度也非常的慢,这还不包括回写到文件
    这时,Maping File就派上用场了,这里处理大文件就抛弃了map的应用(因为容器占用很多内存)
    而是直接利用字符指针来操作,不用其他封装,不多说了,请看示例:

    #pragma warning(disable: 4786) 
    #include <windows.h>
    #include <stdio.h>
    #include <iostream>
    #include <string>

    using namespace std;

    string GetValue(const TCHAR *, const TCHAR *); //根据name得value
    void main(int argc, char* argv[])
    {
      // 创建文件对象(C: est.tsr)
      HANDLE hFile = CreateFile("C:\test.tsr", GENERIC_READ | GENERIC_WRITE,
        0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
      if (hFile == INVALID_HANDLE_VALUE)
      {
        printf("创建文件对象失败,错误代码:%d ", GetLastError());
        return;
      }
      // 创建文件映射对象
      HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
      if (hFileMap == NULL)
      {
        printf("创建文件映射对象失败,错误代码:%d ", GetLastError());
        return;
      }
      // 得到系统分配粒度
      SYSTEM_INFO SysInfo;
      GetSystemInfo(&SysInfo);
      DWORD dwGran = SysInfo.dwAllocationGranularity;
      // 得到文件尺寸
      DWORD dwFileSizeHigh;
      __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
      qwFileSize |= (((__int64)dwFileSizeHigh) << 32);
      // 关闭文件对象
      CloseHandle(hFile);
      // 偏移地址
      __int64 qwFileOffset = 0;
      // 块大小
      DWORD dwBlockBytes = 1000 * dwGran;
      if (qwFileSize < 1000 * dwGran)
      dwBlockBytes = (DWORD)qwFileSize;
      if (qwFileOffset >= 0)
      {
      // 映射视图
      TCHAR *lpbMapAddress = (TCHAR *)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS, 0, 0,dwBlockBytes);
      if (lpbMapAddress == NULL)
      {
        printf("映射文件映射失败,错误代码:%d ", GetLastError());
        return;
      }

      //-----------------------访问数据开始-------------------------
      cout<<GetValue(lpbMapAddress,"SEU07201213")<<endl;
      getchar();
      //-----------------------访问数据结束-------------------------

      // 撤销文件映像
      UnmapViewOfFile(lpbMapAddress);
      }
      // 关闭文件映射对象句柄
      CloseHandle(hFileMap);
    }
    string GetValue(const TCHAR *lpbMapAddress, const TCHAR *sName)
    {
      string sValue; // 存放 = 后面的value值
      TCHAR *p1 = NULL, *p2 = NULL; // 字符指针
      if((p1 = strstr(lpbMapAddress,sName)) != NULL) // 查找sName出现位置
      {
        if(p2 = strstr(p1,"\r\n")) *p2 = ‘\0‘; // 查找"\r\n"(换行)出现位置
        sValue = p1+strlen(sName)+strlen("="); // 指针移动"sName"+"="之后
        *p2 = ‘\r‘; // 还原*p2值,因为不还原会改变原文件结构
      }
      return sValue;
    }

    以上实现了根据索引name匹配value的简单过程,经测试,同样25W行文件,匹配耗费1秒不到,且
    不占本进程内存。
    以上修改lpbMapAddress任意处值,也不需要重新回写到文件,真正是大大提高了文件读与写的效率



  • 相关阅读:
    Delphi公用函数单元
    Delphi XE5 for Android (十一)
    Delphi XE5 for Android (十)
    Delphi XE5 for Android (九)
    Delphi XE5 for Android (八)
    Delphi XE5 for Android (七)
    Delphi XE5 for Android (五)
    Delphi XE5 for Android (四)
    Delphi XE5 for Android (三)
    Delphi XE5 for Android (二)
  • 原文地址:https://www.cnblogs.com/flysnail/p/2421055.html
Copyright © 2011-2022 走看看