C++ 从内存映射的格式化文件中读取整数

C++ 从内存映射的格式化文件中读取整数,c++,windows,mmap,streambuf,memory-mapping,C++,Windows,Mmap,Streambuf,Memory Mapping,我已将内存映射为一个大型格式化(文本)文件,每行包含一个整数,如下所示: 123 345 34324 3232 ... 因此,在第一个字节有一个指向内存的指针,在最后一个字节也有一个指向内存的指针。我试图尽可能快地将所有这些整数读入数组。最初,我创建了一个专门的std::streambuf类来使用std::istream从内存中读取数据,但它似乎相对较慢 对于如何有效地将字符串“1231232\r\n123123\r\n123\r\n1231\r\n1231\r\n2387897…”解析到数组

我已将内存映射为一个大型格式化(文本)文件,每行包含一个整数,如下所示:

123
345
34324
3232
...
因此,在第一个字节有一个指向内存的指针,在最后一个字节也有一个指向内存的指针。我试图尽可能快地将所有这些整数读入数组。最初,我创建了一个专门的std::streambuf类来使用std::istream从内存中读取数据,但它似乎相对较慢

对于如何有效地将字符串“1231232\r\n123123\r\n123\r\n1231\r\n1231\r\n2387897…”解析到数组{123123212312312387897,…}中,您有什么建议吗


文件中的整数数事先未知。

注意:此答案已编辑过几次

逐行读取内存(基于和)

类行
{
std::字符串数据;
公众:
friend std::istream&operator>>(std::istream&is,line&l)
{
std::getline(is,l.data);
回报是;
}
运算符std::string(){返回数据;}
};
标准::streambuf osrb;
setg(ptr、ptr、ptrs+尺寸-1);
标准::istream istr(&osrb);
std::向量整数;
std::istream_迭代器begin(istr);
std::istream_迭代器端;
std::transform(开始、结束、std::返回插入器(ints)和boost::词法转换);

因为这是内存映射,所以将字符简单地复制到堆栈数组,将atoi复制到另一个内存映射文件上的另一个整数数组将是非常有效的。这样,分页文件根本不用于这些大缓冲区

open memory mapped file to output int buffer

declare small stack buffer of 20 chars
while not end of char array
  while current char not  line feed
    copy chars to stack buffer
    null terminate the buffer two chars back
    copy results of int buffer output buffer
    increment the output buffer pointer
  end while  
end while
虽然不使用a库,但它的优点是最大限度地减少了内存映射文件的内存使用,因此临时缓冲区仅限于堆栈缓冲区和atoi内部使用的缓冲区。输出缓冲区可以根据需要丢弃或保存到文件中。

std::vector array;
std::vector<int> array;
char * p = ...; // start of memory mapped block
while ( not end of memory block )
{
    array.push_back(static_cast<int>(strtol(p, &p, 10)));
    while (not end of memory block && !isdigit(*p))
        ++p;
}
char*p=…;//内存映射块的开始 while(不是内存块的末尾) { array.push_back(静态_cast(strtol(p,&p,10)); while(不是内存块的末尾&&!isdigit(*p)) ++p; }

这段代码有点不安全,因为不能保证
strtol
会在内存映射块的末尾停止,但这是一个开始。这应该是非常快的,即使有额外的检查。

这对我来说是一个非常有趣的任务,我可以学到更多关于C++的内容。 承认,代码相当大,并且有很多错误检查,但这只表明在解析过程中有多少不同的事情会出错

#include <ctype.h>
#include <limits.h>
#include <stdio.h>

#include <iterator>
#include <vector>
#include <string>

static void
die(const char *reason)
{
  fprintf(stderr, "aborted (%s)\n", reason);
  exit(EXIT_FAILURE);
}

template <class BytePtr>
static bool
read_uint(BytePtr *begin_ref, BytePtr end, unsigned int *out)
{
  const unsigned int MAX_DIV = UINT_MAX / 10;
  const unsigned int MAX_MOD = UINT_MAX % 10;

  BytePtr begin = *begin_ref;
  unsigned int n = 0;

  while (begin != end && '0' <= *begin && *begin <= '9') {
    unsigned digit = *begin - '0';
    if (n > MAX_DIV || (n == MAX_DIV && digit > MAX_MOD))
      die("unsigned overflow");
    n = 10 * n + digit;
    begin++;
  }

  if (begin == *begin_ref)
    return false;

  *begin_ref = begin;
  *out = n;
  return true;
}

template <class BytePtr, class IntConsumer>
void
parse_ints(BytePtr begin, BytePtr end, IntConsumer out)
{
  while (true) {
    while (begin != end && *begin == (unsigned char) *begin && isspace(*begin))
      begin++;
    if (begin == end)
      return;

    bool negative = *begin == '-';
    if (negative) {
      begin++;
      if (begin == end)
        die("minus at end of input");
    }

    unsigned int un;
    if (!read_uint(&begin, end, &un))
      die("no number found");

    if (!negative && un > INT_MAX)
      die("too large positive");
    if (negative && un > -((unsigned int)INT_MIN))
      die("too small negative");

    int n = negative ? -un : un;
    *out++ = n;
  }
}

static void
print(int x)
{
  printf("%d\n", x);
}

int
main()
{
  std::vector<int> result;
  std::string input("2147483647 -2147483648 0 00000 1 2 32767 4 -17 6");

  parse_ints(input.begin(), input.end(), back_inserter(result));

  std::for_each(result.begin(), result.end(), print);
  return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
静态空隙
死亡(常量字符*原因)
{
fprintf(stderr,“中止(%s)\n”,原因);
退出(退出失败);
}
模板
静态布尔
读取单元(BytePtr*begin\u ref,BytePtr end,unsigned int*out)
{
const unsigned int MAX_DIV=UINT_MAX/10;
常量无符号整数最大值=整数最大值%10;
BytePtr begin=*begin\u ref;
无符号整数n=0;
while(begin!=end&“0”MAX_MOD))
die(“未签名溢出”);
n=10*n+位;
begin++;
}
如果(开始==*开始参考)
返回false;
*begin\u ref=开始;
*out=n;
返回true;
}
模板
无效的
parse_int(BytePtr begin、BytePtr end、IntConsumer out)
{
while(true){
while(begin!=end&&*begin==(无符号字符)*begin&&isspace(*begin))
begin++;
如果(开始==结束)
返回;
布尔负=*开始='-';
如果(否定){
begin++;
如果(开始==结束)
模具(“输入端负”);
}
无符号整数;
如果(!读取单元(&开始、结束和取消))
模具(“未找到编号”);
如果(!negative&&un>INT\u MAX)
模具(“太大的正极”);
如果(负和反>-((无符号整数)整数分钟))
死(“太小负片”);
int n=负?-un:un;
*out++=n;
}
}
静态空隙
打印(int x)
{
printf(“%d\n”,x);
}
int
main()
{
std::向量结果;
字符串输入(“2147483647-2147483648 0 000001 2 32767 4-17 6”);
解析输入(input.begin(),input.end(),back_inserter(result));
std::for_each(result.begin()、result.end()、print);
返回0;
}

我尽量不调用任何类型的未定义行为,当将无符号数转换为有符号数或对未知数据类型调用
isspace
时,这可能会变得非常棘手。

如果他要将内存映射文件复制到内存中,似乎有点违背了这一点……我认为编辑后的答案是可以的。。。这至少是一次诚实的尝试。这几乎和我使用专门的streambuf和istream时使用的解决方案相同,后者相对较慢。不过这是一个优雅的代码。一个优化可能是用(*p&0xF0)替换isdigit。您忘记了
数组。reserve(…)
。这可能会造成严重的性能损失。@HardCoder1986,std::vector将使数组按指数增长,因此性能损失仅为log(n)。问题明确地说整数的数目是未知的。我同意做出合理的猜测可能会有所帮助。@ronag,你的表达方式不完全一样。我怀疑
isdigit
是否会成为瓶颈。此外,使用
EOF
以外的
isdigit
字符调用
isdigit
会调用未定义的行为。
#include <ctype.h>
#include <limits.h>
#include <stdio.h>

#include <iterator>
#include <vector>
#include <string>

static void
die(const char *reason)
{
  fprintf(stderr, "aborted (%s)\n", reason);
  exit(EXIT_FAILURE);
}

template <class BytePtr>
static bool
read_uint(BytePtr *begin_ref, BytePtr end, unsigned int *out)
{
  const unsigned int MAX_DIV = UINT_MAX / 10;
  const unsigned int MAX_MOD = UINT_MAX % 10;

  BytePtr begin = *begin_ref;
  unsigned int n = 0;

  while (begin != end && '0' <= *begin && *begin <= '9') {
    unsigned digit = *begin - '0';
    if (n > MAX_DIV || (n == MAX_DIV && digit > MAX_MOD))
      die("unsigned overflow");
    n = 10 * n + digit;
    begin++;
  }

  if (begin == *begin_ref)
    return false;

  *begin_ref = begin;
  *out = n;
  return true;
}

template <class BytePtr, class IntConsumer>
void
parse_ints(BytePtr begin, BytePtr end, IntConsumer out)
{
  while (true) {
    while (begin != end && *begin == (unsigned char) *begin && isspace(*begin))
      begin++;
    if (begin == end)
      return;

    bool negative = *begin == '-';
    if (negative) {
      begin++;
      if (begin == end)
        die("minus at end of input");
    }

    unsigned int un;
    if (!read_uint(&begin, end, &un))
      die("no number found");

    if (!negative && un > INT_MAX)
      die("too large positive");
    if (negative && un > -((unsigned int)INT_MIN))
      die("too small negative");

    int n = negative ? -un : un;
    *out++ = n;
  }
}

static void
print(int x)
{
  printf("%d\n", x);
}

int
main()
{
  std::vector<int> result;
  std::string input("2147483647 -2147483648 0 00000 1 2 32767 4 -17 6");

  parse_ints(input.begin(), input.end(), back_inserter(result));

  std::for_each(result.begin(), result.end(), print);
  return 0;
}