Получить последние 10 строк очень большого текстового файла> 10 ГБ - PullRequest
62 голосов
/ 29 декабря 2008

Какой самый эффективный способ отобразить последние 10 строк очень большого текстового файла (этот конкретный файл превышает 10 ГБ). Я думал просто написать простое приложение на C #, но я не уверен, как это сделать эффективно.

Ответы [ 20 ]

74 голосов
/ 29 декабря 2008

Прочитайте до конца файла, затем ищите назад, пока не найдете десять новых строк, а затем читайте до конца, учитывая различные кодировки. Обязательно обработайте случаи, когда количество строк в файле меньше десяти. Ниже приведена реализация (в C #, как вы отметили это), обобщенная для поиска последних numberOfTokens в файле, расположенном в path, закодированном в encoding, где разделитель токенов представлен tokenSeparator; результат возвращается как string (это можно улучшить, возвращая IEnumerable<string>, который перечисляет токены).

public static string ReadEndTokens(string path, Int64 numberOfTokens, Encoding encoding, string tokenSeparator) {

    int sizeOfChar = encoding.GetByteCount("\n");
    byte[] buffer = encoding.GetBytes(tokenSeparator);


    using (FileStream fs = new FileStream(path, FileMode.Open)) {
        Int64 tokenCount = 0;
        Int64 endPosition = fs.Length / sizeOfChar;

        for (Int64 position = sizeOfChar; position < endPosition; position += sizeOfChar) {
            fs.Seek(-position, SeekOrigin.End);
            fs.Read(buffer, 0, buffer.Length);

            if (encoding.GetString(buffer) == tokenSeparator) {
                tokenCount++;
                if (tokenCount == numberOfTokens) {
                    byte[] returnBuffer = new byte[fs.Length - fs.Position];
                    fs.Read(returnBuffer, 0, returnBuffer.Length);
                    return encoding.GetString(returnBuffer);
                }
            }
        }

        // handle case where number of tokens in file is less than numberOfTokens
        fs.Seek(0, SeekOrigin.Begin);
        buffer = new byte[fs.Length];
        fs.Read(buffer, 0, buffer.Length);
        return encoding.GetString(buffer);
    }
}
22 голосов
/ 29 декабря 2008

Скорее всего, я просто открою его как двоичный поток, добьюсь конца, а затем вернусь назад в поисках разрывов строк. Сделайте резервную копию 10 (или 11 в зависимости от этой последней строки), чтобы найти свои 10 строк, затем просто прочитайте до конца и используйте Encoding.GetString в том, что вы прочитали, чтобы получить его в строковом формате. Разделить по желанию.

17 голосов
/ 29 декабря 2008

Как и предлагали другие, вы можете перейти к концу файла и эффективно читать в обратном направлении. Однако это немного сложно - особенно потому, что если у вас есть кодировка переменной длины (например, UTF-8), вам нужно быть хитрым, чтобы убедиться, что вы получаете «целые» символы.

17 голосов
/ 29 декабря 2008

Хвост? Tail - это команда unix, которая будет отображать последние несколько строк файла. В наборе ресурсов Windows 2003 Server имеется версия для Windows .

6 голосов
/ 29 декабря 2008

Вы должны иметь возможность использовать FileStream.Seek () , чтобы перейти к концу файла, а затем вернуться назад, ища \ n, пока у вас не будет достаточно строк.

6 голосов
/ 29 декабря 2008

Я не уверен, насколько это будет эффективно, но в Windows PowerShell получить последние десять строк файла так же просто, как и

Get-Content file.txt | Select-Object -last 10
4 голосов
/ 29 декабря 2008

Это то, что делает хвостовая команда Unix. Смотри http://en.wikipedia.org/wiki/Tail_(Unix)

В Интернете существует множество реализаций с открытым исходным кодом, и вот одна для win32: Хвост для WIn32

4 голосов
/ 30 декабря 2008

Я думаю, что следующий код решит проблему с небольшими изменениями, обновив кодировку

StreamReader reader = new StreamReader(@"c:\test.txt"); //pick appropriate Encoding
reader.BaseStream.Seek(0, SeekOrigin.End);
int count = 0;
while ((count < 10) && (reader.BaseStream.Position > 0))
{
    reader.BaseStream.Position--;
    int c = reader.BaseStream.ReadByte();
    if (reader.BaseStream.Position > 0)
        reader.BaseStream.Position--;
    if (c == Convert.ToInt32('\n'))
    {
        ++count;
    }
}
string str = reader.ReadToEnd();
string[] arr = str.Replace("\r", "").Split('\n');
reader.Close();
2 голосов
/ 04 марта 2012

вот моя версия. НТН

using (StreamReader sr = new StreamReader(path))
{
  sr.BaseStream.Seek(0, SeekOrigin.End);

  int c;
  int count = 0;
  long pos = -1;

  while(count < 10)
  {
    sr.BaseStream.Seek(pos, SeekOrigin.End);
    c = sr.Read();
    sr.DiscardBufferedData();

    if(c == Convert.ToInt32('\n'))
      ++count;
    --pos;
  }

  sr.BaseStream.Seek(pos, SeekOrigin.End);
  string str = sr.ReadToEnd();
  string[] arr = str.Split('\n');
}
2 голосов
/ 29 декабря 2008

Вы можете использовать версию tail для Windows и просто скопировать ее вывод в текстовый файл с символом> или просмотреть его на экране в зависимости от ваших потребностей.

...