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

时间:2010-11-16 20:03:48

标签: c++ windows mmap streambuf memory-mapping

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

123
345
34324
3232
...

所以,我有一个指向第一个字节的内存的指针,还有一个指向最后一个字节的内存的指针。我试图尽快将所有这些整数读入数组。最初我创建了一个专门的std :: streambuf类来使用std :: istream来读取该内存,但它看起来相对较慢。

对于如何有效地将字符串如“1231232 \ r \ n \ n123123 \ r \ n123 \ r \ n \ n1212 \ r \ n \ n \ n \ n \ n \ n \ n \ n \创建数组...”解析为数组{1231232,123123,1231,231, 2387897,...}?

事先不知道文件中的整数。

4 个答案:

答案 0 :(得分:1)

这对我来说是一个非常有趣的任务,可以学习更多关于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;
}

我努力不要调用任何类型的未定义行为,这在将无符号数转换为有符号数或在未知数据类型上调用isspace时会非常棘手。

答案 1 :(得分:0)

注意:此答案已被编辑几次。

逐行读取内存(基于linklink)。

class line 
{
   std::string data;
public:
   friend std::istream &operator>>(std::istream &is, line &l) 
   {
      std::getline(is, l.data);
      return is;
   }
   operator std::string() { return data; }    
};

std::streambuf osrb;
setg(ptr, ptr, ptrs + size-1);
std::istream istr(&osrb);

std::vector<int> ints;

std::istream_iterator<line> begin(istr);
std::istream_iterator<line> end;
std::transform(begin, end, std::back_inserter(ints), &boost::lexical_cast<int, std::string>);

答案 2 :(得分:0)

由于这是内存映射的一个简单的副本,chars到一个堆栈数组,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库具有最小化内存映射文件的内存使用的优点,因此临时缓冲区仅限于堆栈1和atoi内部使用的缓冲区。输出缓冲区可以根据需要丢弃或保存到文件中。

答案 3 :(得分:0)

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;
}

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