Эффективное средство определения положения конца линии - PullRequest
0 голосов
/ 08 марта 2012

Я работаю с C ++, ifstream и текстовыми файлами. Я ищу положение конца каждой строки, потому что мне нужно прочитать n символов с конца строки.

В настоящее время я читаю каждый байт и проверяю, соответствует ли он символу новой строки Unix (LF).

К сожалению, ввод обычно длинный текст, и мой метод не быстрый.

Есть ли более быстрый способ?

Ответы [ 6 ]

6 голосов
/ 08 марта 2012

Если вы ищете сырую скорость, я бы отобразил файл в памяти и использовал что-то вроде strchr, чтобы найти новую строку;

p = strchr(line_start, '\n');

затем, пока p не NULL или первый символ в области памяти, вы можете просто использовать p[-1], чтобы прочитать символ перед новой строкой.

ПРИМЕЧАНИЕ: , если файл может содержать '\0' символов, вам следует использовать memchr. На самом деле, это может быть желательно независимо от того, что позволяет указать размер буфера (область памяти).

2 голосов
/ 08 марта 2012

Я работаю с C ++, ifstream и текстовыми файлами.Я ищу положение конца каждой строки, потому что мне нужно прочитать n символов с конца строки .

Я сосредоточусь на вашем требовании, читая'n' символов в конце строки, а не на ваш вопрос:

// Untested.
std::string s;
while(std::getline(std::cin, s)) {
    if(s.size() > n) s.erase(s.begin(), s.end()-n);
    // s is the last 'n' chars of the line
    std::cout << "Last N chars: " << s << "\n";
}
1 голос
/ 08 марта 2012

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

Создайте массив символов размером n и используйте его в качестве кольцевого буфера: когда вы доберетесь до конца массива, просто обведите его обратно. Сохраните символ в следующей позиции вашего кругового буфера.

Когда вы обнаружите '\n', ваш буфер содержит n предшествующие символы, только немного не в порядке: префикс начинается с вашего указателя буфера и заканчивается в конце буфера, а суффикс начинается с нуля и идет на указатель вашего буфера минус один.

Вот пример того, как вы можете заставить его работать (при условии n == 20):

int main()
{
    ifstream fs("c:\\temp\\a.txt");
    char buf[20];
    int bp = 0;
    bool circular = false;
    while (fs.good()) {
        char ch = fs.get();
        if (ch != '\n') {
            buf[bp] = ch;
            bp = (bp+1) % 20;
            circular |= !bp;
        } else {
            string s;
            if (circular) {
                s = string(buf+bp, buf+20) + string(buf, buf+bp);
            } else {
                s = string(buf, buf+bp);
            }
            cerr << s << endl;
            circular = false;
            bp = 0;
        }
    }
    return 0;
}
1 голос
/ 08 марта 2012

Вы можете взглянуть на функцию getline в std::string. Попробуйте прочитать всю строку за раз, а затем прочитать символы с конца строки.

Как обычно в случае проблем с производительностью, реальная хитрость заключается в том, чтобы запустить ваш код через профилировщик и посмотреть, на что он тратит свое время. Часто между «Fastest» и «Fast Enough» существует очень реальная разница.

0 голосов
/ 08 марта 2012

Что бы вы ни делали, вам все равно придется искать линейно в файле. Вы можете искать быстрее, но это все равно будет линейный поиск.

Реальным решением является изменение формата файла, чтобы индексы «интересных» символов записывались в начале файла. Когда придет время его прочитать, вы можете полностью пропустить «неинтересные» части файла.

Если это невозможно, вы можете создать отдельный файл «индекса». Это не избавит вас от необходимости выполнять линейный поиск один раз, но избавит вас от необходимости повторять один и тот же файл. Это, конечно, имеет значение, только если вы собираетесь обрабатывать один и тот же файл более одного раза.

Кстати, даже линейное сканирование должно быть довольно быстрым. Вы должны быть связаны с вводом / выводом больше всего на свете. Насколько велики ваши файлы и что вы подразумеваете под "мой метод не быстрый"?

0 голосов
/ 08 марта 2012

Быстрый и грязный способ - что-то вроде этого:

ifs.seekg( 0, std::ifstream::end );
std::string buffer( ifs.tellg(), '\0' );
ifs.seekg( 0, std::ifstream::beg );
ifs.read( &buffer[0], buffer.size() );

Тогда вместо этого работайте с буфером.Это, вероятно, принесет вам всю необходимую вам скорость (много-много порядков в моем опыте).Если вы хотите иметь возможность обрабатывать произвольно большие файлы, вам нужно немного изменить логику (вместо этого ищите по частям).

...