2010-11-16 1 views
2

У меня есть память отображается большой отформатированный файл (текст), содержащий одно целое число в каждой строке, как так:Чтение числа из памяти, отображенных файл в формате

123 
345 
34324 
3232 
... 

Итак, у меня есть указатель на память первого байта а также указатель на память в последнем байте. Я пытаюсь как можно быстрее прочитать все эти целые числа в массиве. Первоначально я создал специализированный класс std :: streambuf для работы с std :: istream для чтения из этой памяти, но, похоже, он относительно медленный.

Есть ли у вас предложения по эффективному анализу строки типа «1231232 \ r \ n123123 \ r \ n123 \ r \ n1231 \ r \ n2387897 ...» в массив {1231232,123123,1231,231 , 2387897, ...}?

Число целых чисел в файле не известно заранее.

ответ

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 остановится в конце памяти отображается блок, но это начало. Должно идти очень быстро, даже с добавлением дополнительной проверки.

+0

Одной из оптимизаций может быть замена isdigit на (* p & 0xF0). – ronag

+0

Вы забыли 'array.reserve (...)'. Снижение производительности может быть серьезным. –

+0

@ HardCoder1986, std :: vector вырастет массив экспоненциально, так что штраф за производительность будет только log (n). В вопросе явно сказано, что число целых чисел неизвестно. Я согласен с тем, что разумная догадка может помочь. –

0

ПРИМЕЧАНИЕ: Этот ответ был отредактирован несколько раз.

Читает память по строкам (на основе link и link).

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>); 
+1

Кажется, это своего рода поражение, если он собирается скопировать файл с памятью в память ... –

+0

Действительно, он побеждает точку. – ronag

+0

Ну, я думаю, отредактированный ответ в порядке ... это была честная попытка, по крайней мере. – ronag

0

Поскольку это память отображается простое копирование на символы в массив стека и 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 

Хотя это не использует библиотеку это имеет преимущество, минимизируя использование памяти файлы, отображенные на память, поэтому временные буферы ограничены в стек одной и той, которая используется atoi внутри. Выходной буфер можно выбросить или оставить сохраненным в файле по мере необходимости.

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 на неизвестном типе данных.

+0

+1 для усилий –

Смежные вопросы