Как мне прочитать заданное количество строк из конца файла, используя потоки в C ++? - PullRequest
3 голосов
/ 17 января 2010

Для моей реализации команды tail shell в Linux мне нужно прочитать определенное количество строк / байтов из конца файла, используя потоковый ввод / вывод. У кого-нибудь есть предложения как это сделать? Я подозреваю, что мне нужно открыть файл и передать какой-то параметр конструктору ifstream, но я не знаю, что именно. Погуглив ничего не нашел.

Ответы [ 5 ]

4 голосов
/ 17 января 2010

Поскольку tail должен работать с конвейерами, которые вы не можете перематывать, вам придется сохранять вращающийся буфер последних n прочитанных строк, которые вы будете выгружать в EOF.

3 голосов
/ 17 января 2010

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

2 голосов
/ 17 января 2010

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

HTH

2 голосов
/ 17 января 2010
#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main()
{
  ifstream is("file.txt", ios::binary);
  if (!is) {
    cout << "Failed to open file" << endl;
    return 1;
  }

  is.seekg(0, ios::end);
  int len = is.tellg();
  char c;
  int n = 0;
  ostringstream line;
  int lines = 0;

  for (int i = len - 1; i >= 0; --i) {
    is.seekg(i, ios::beg);
    is.get(c);
    if (c == '\n' || i == 0) {
      if (i < len - 1) {
        if (i == 0) {
          line << c;
        }
        string s = line.str();
        cout << lines << ": " << string(s.rend() - n, s.rend()) << endl;
        ++lines;
        n = 0;
        line.seekp(0, ios::beg);
      }
    } else {
      line << c;
      ++n;
    }
  }

  is.close();

  return 0;
}
0 голосов
/ 18 января 2010

это показывает, как вы будете делать это в c ++ ... читать последовательные фрагменты из конца файла, а затем сканировать фрагменты на новые строки. если символ новой строки не найден, часть фрагмента следует сохранить и объединить со следующим фрагментом, прочитанным в ...

//
// USAGE: lastln COUNT [FILE]
//
// Print at most COUNT lines from the end of FILE or standard input.
// If COUNT is -1, all lines are printed.
//

#include <errno.h>
#include <libgen.h>
#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main(int argc, char **argv)
{
  int ret = 0, maxLines = -1, len, count = 0, sz = 4096, lines = 0, rd;
  istream *is;
  ifstream ifs;
  stringstream ss;
  char *buf = NULL;
  const char *prog = (argc > 0 && argv[0] ? basename(argv[0]) : "");
  string line;

  if (argc > 1) {
    if ((maxLines = atoi(argv[1])) == 0) {
      goto end;
    }
  }

  if (argc > 2 && !(argv[2] && argv[2][0] == '-' && argv[2][1] == '\0')) {
    ifs.open(argv[2], ios::in | ios::binary);
    if (!ifs) {
      ret = 1;
      cerr << prog << ": " << argv[2] << ": " << strerror(errno) << endl;
      goto end;
    }
    is = &ifs;
  } else {
    ss << cin.rdbuf();
    if (!ss) {
      ret = 1;
      cerr << prog << ": failed to read input" << endl;
      goto end;
    }
    is = &ss;
  }

  is->seekg(0, ios::end);
  len = is->tellg();
  buf = new char[sz + 1];

  while (rd = min(len - count, sz)) {
    is->seekg(0 - count - rd, ios::end);
    is->read(buf, rd);
    count += rd;
    char *p = buf + rd, *q;
    *p = '\0';

    for (;;) {
      q = (char *)memrchr(buf, '\n', p - buf);
      if (q || count == len) {
        if (q) *q = '\0';
        if (lines || p - q - 1 > 0 || !q) {
          ++lines;
          cout << lines << ": " << (q ? q + 1 : buf) << line << endl;
          line.clear();
          if (lines >= maxLines && maxLines != -1) break;
        }
        if (q) p = q; else break;
      } else {
        line = string(buf, p - buf) + line;
        break;
      }
    }
  }

  end:

  if (buf) delete[] buf;
  return ret;
}
...