Чтение последней строки файла - PullRequest
4 голосов
/ 27 ноября 2011

У меня большой файл, и мне нужно получить только последнюю строку из него (\n только разделитель строк).
Мне нужно, чтобы это было сделано на устройстве iOS, поэтому это не может занять много памяти или процессорного времени (например, чтение всего файла).
Как я могу сделать это в Objective-C, C ++ или C ++ 11?

Ответы [ 4 ]

5 голосов
/ 27 ноября 2011

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

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

3 голосов
/ 27 ноября 2011

У меня есть особенность в моем рабочем коде. Идея состоит в том, чтобы попытаться прочитать последнюю строку путем поиска и чтения. Посмотрите, пожалуйста.

bool readLastLine(std::string const& filename, std::string& lastLine)
{
    std::ifstream in(filename.c_str(),std::ifstream::binary);
    if(!in) return false;
    in.seekg(0, std::ifstream::end);
    const std::streamoff len = in.tellg();
    //empty file
    if(len == 0)
    {
        lastLine = "";
        return true;
    }
    int buf_size = 128;
    std::vector<char> buf;
    while(in)
    {   
        if(buf_size > len)
        {
            buf_size = len;
        }
        buf.resize(buf_size);
        in.seekg(0 - buf_size, std::ifstream::end);
        in.read(&buf[0],buf_size);
        //all content is in the buffer or we already have the complete last line
        if(len == buf_size || std::count(buf.begin(), buf.end(), '\n') > 1)
        {
            break;
        }
        //try enlarge the buffer
        buf_size *= 2;
    }
    //find the second line seperator from the end if any
    auto i = std::find(++buf.rbegin(),buf.rend(), '\n');
    lastLine.assign(i == buf.rend() ?  buf.begin() : buf.begin() + std::distance(i, buf.rend()), buf.begin() + buf_size);
    return true;
}
2 голосов
/ 27 ноября 2011

@ Ответ Nerdtron кажется мне наиболее подходящим, если у вас нет контроля над форматом файла, но ...

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

0 голосов
/ 04 апреля 2013

Я придумал это, пытаясь улучшить работу Брюса, преимущество в том, что буфер не нуждается в изменении размера, просто продолжайте читать куски символов одинакового размера еще дальше от EOF:

std::string lastLine(std::ifstream &file)
{
    if (!file.good()) throw exception("Bad stream on input");

    const size_t bufSize = 80; // because why not? tweak if need to
    char buf[bufSize];
    string line;

    int seek, nloff;
    // iterate over multiples of bufSize while file ok
    for (size_t n = 1; file; ++n)
    {
        // next seek position will be a multiple of bufSize
        seek = -static_cast<int>(n * bufSize);
        file.seekg(seek, file.end);
        // read "bufSize" bytes into buffer
        file.read(buf, bufSize);

        // in case no newline found, seek past eof
        nloff = -seek;
        // find offset of last newline in buffer
        for (size_t i = 0; i < bufSize; ++i)
        {
            if (buf[i] == '\n') nloff = i;
        }
        seek += nloff + 1; // new seek position is one character after found newline
        if (seek >= 0) continue; // just kidding about the "past eof" part ;)

        // seek to after found newline and get line
        file.seekg(seek, file.end);
        getline(file, line);
        if (!line.empty()) break; // have result, break and return
    }

    if (file.good()) return line;
    else return string();
}
...