Проблема с чтением последней строки кода алгоритма - c ++ - PullRequest
0 голосов
/ 18 февраля 2020

Я нашел этот код в Stackoverflow:

    if (file.is_open())
    {
        file.seekg(-1, std::ios_base::end);
        if (file.peek() == '\n')
        {
            //Start searching for \n occurrences
            file.seekg(-1, std::ios_base::cur);
            int i = file.tellg();
            for (i;i > 0; i--)
            {
                if (file.peek() == '\n')
                {
                    //Found
                    file.get();
                    break;
                }
                //Move one character back
                file.seekg(i, std::ios_base::beg);
            }
                string line;
                getline(file,line);
        }

Он сохраняет последнюю строку файла в переменной 'line' и работает нормально, но если последняя строка является первой строкой, это не так читать первый символ строки (остальное читается). Когда последняя строка, например, третья строка, все работает. В чем проблема?

Ответы [ 3 ]

0 голосов
/ 18 февраля 2020

Здесь следует решение. Он использует другой метод, который требует, чтобы весь файл был прочитан в строку. Но, похоже, это решит вашу проблему. Эта функция вернет последнюю строку текстового файла.

std::string get_last_line(const std::string& s_file_name)
{
    std::ifstream file(s_file_name);

    std::string str((std::istreambuf_iterator<char>(file)),
                     std::istreambuf_iterator<char>());

    std::size_t p = str.find_last_of("\n");

    if (p != std::string::npos)
        return str.substr(p+1, str.length() - p);

    return str;
}

Хотя она использует больше памяти, я считаю, что этот код будет работать быстрее, так как не требует нескольких обращений к файловой системе ОС.

0 голосов
/ 18 февраля 2020

Есть несколько условий, которые необходимо учитывать при резервном копировании поиска последней строки в текстовом файле.

  1. Вам не важно, какой последний символ в файле. Файл может иметь (или не иметь) POSIX конец файла ('\n' в качестве последнего символа последней строки),
  2. , что имеет значение при поиске конец файла, вы тоже не в начале (empty-file); тогда вы можете
  3. начать цикл, просматривая '\n' перед последней строкой в ​​файле (сохранить счетчик смещения от конца и, когда смещение не равно единице до конца, и file.peek() == '\n' , затем прервите цикл и, наконец,
  4. проверьте, что вы не в начале или если 1 st char равен '\n', если так, продвиньте позицию файла на единицу, и
  5. прочитайте последнюю строку.

Вы можете закодировать логи c несколькими различными способами, но если вы делаете это в функции, вы либо передадите ссылку на открыть std::ifstream или передать имя файла для открытия в функции. Если вы передаете ссылку на открытый файловый поток, вы хотите, чтобы тип возвращаемой функции был такой же ссылкой на открытый файловый поток, что поток состояние может быть проверено для обнаружения ошибки потока (вы также проверите string.length() != 0 при возврате, чтобы убедиться, что файл не был пустым)

В целом, вы можете написать файл get last line используя ваши файловые операции simi Lar to:

#include <iostream>
#include <fstream>
#include <string>

std::ifstream& getlastline (std::ifstream& f, std::string& last)
{
    long offsetend = 0;     /* initial offset from end */

    /* if file not empty */
    if (f.seekg(offsetend, std::ios_base::end) && f.tellg())
        /* loop continually seeking 1 before current offsetend */
        while (f.seekg(offsetend - 1, std::ios_base::end)) {
            /* if at beginning or offsetend and '\n' */
            if (!f.tellg() || (offsetend < 0 && f.peek() == '\n'))
                break;
            offsetend--;        /* decrement offsetend */
        }
    /* if not at beginning, or 1st char is '\n' advance by 1 */
    if (f.tellg() || f.peek() == '\n')
        f.seekg(offsetend, std::ios_base::end);

    if (!getline (f, last)) /* read last line */
        std::cerr << "error: getline() read of last line.\n";

    return f;       /* return stream state */
}

Добавляя короткий пример, который позволяет вам указать имя файла для чтения в качестве первого аргумента программы, вы можете сделать:

int main (int argc, char **argv) {

    if (argc < 2) {
        std::cerr << "error: filename required as 1st argument.\n";
        return 1;
    }

    std::string last {};
    std::ifstream f (argv[1]);

    if (!getlastline (f, last) || last.length() == 0) {
        std::cerr << "error: stream error or file was empty.\n";
        return 1;
    }

    std::cout << last << '\n';
}

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

Конечно, если все файлы, с которыми вы имеете дело, имеют несколько сотен строк или меньше, вы можете полностью отказаться от смещения от конца и просто прочитать все строки, сохранив последние - это создает драматизм c упрощение, например

#include <iostream>
#include <fstream>
#include <string>

int main (int argc, char **argv) {

    if (argc < 2) {
        std::cerr << "error: filename required as 1st argument.\n";
        return 1;
    }

    std::string current {}, last {};
    std::ifstream f (argv[1]);

    while (getline (f, current))
        last = current;

    /* (add any length validations here) */
    std::cout << last << '\n';
}

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 18 февраля 2020

Проблема в том, что весь ваш код выполняется только тогда, когда последний символ равен '\n'.

Если это не так, то ваша программа ничего не делает.

Вам необходимо переосмыслить if (file.peek() == '\n') часть.

...