Получить последние 10 строк очень большого текстового файла> 10 ГБ

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

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

 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); } } 

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

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

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

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

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

 Get-Content file.txt | Select-Object -last 10 

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

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

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

 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(); 

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

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

 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'); } 

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

Одним из полезных методов является FileInfo.Length . Он задает размер файла в байтах.

Какова структура вашего файла? Вы уверены, что последние 10 строк будут ближе к концу файла? Если у вас есть файл с 12 строками текста и 10 ГБ 0, то смотреть на конец на самом деле не так быстро. Опять же, вам, возможно, придется просмотреть весь файл.

Если вы уверены, что файл содержит множество коротких строк на каждой новой строке, попробуйте до конца, а затем проверьте, пока вы не подсчитали 11 строк. Затем вы можете читать вперед для следующих 10 строк.

Я думаю, что другие плакаты все показали, что нет реального ярлыка.

Вы можете использовать такой инструмент, как tail (или powershell), или вы можете написать какой-то немой код, который ищет конец файла, а затем оглядывается назад на n строк новой строки.

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

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

Если у вас есть файл с четным форматом на строку (например, система daq), вы просто используете streamreader для получения длины файла, а затем возьмите одну из строк ( readline() ).

Разделите общую длину на длину строки. Теперь у вас есть общее длинное число, чтобы представить количество строк в файле.

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

 StreamReader leader = new StreamReader(GetReadFile); leader.BaseStream.Position = 0; StreamReader follower = new StreamReader(GetReadFile); int count = 0; string tmper = null; while (count <= 12) { tmper = leader.ReadLine(); count++; } long total = follower.BaseStream.Length; // get total length of file long step = tmper.Length; // get length of 1 line long size = total / step; // divide to get number of lines long go = step * (size - 12); // get the bit location long cut = follower.BaseStream.Seek(go, SeekOrigin.Begin); // Go to that location follower.BaseStream.Position = go; string led = null; string[] lead = null ; List samples = new List(); follower.ReadLine(); while (!follower.EndOfStream) { led = follower.ReadLine(); lead = Tokenize(led); samples.Add(lead); } 

Используя ответ Sisutil в качестве отправной точки, вы можете прочитать файл по строкам и загрузить их в Queue . Он читает файл с самого начала, но имеет силу не пытаться прочитать файл назад. Это может быть очень сложно, если у вас есть файл с кодировкой ширины переменной ширины, такой как UTF-8, как указал Джон Скит. Он также не делает никаких предположений о длине строки.

Я протестировал это против 1,7-гигабайтного файла (у него не было 10 ГБ), и это заняло около 14 секунд. Разумеется, обычные обычаи применяются при сравнении времени загрузки и чтения между компьютерами.

 int numberOfLines = 10; string fullFilePath = @"C:\Your\Large\File\BigFile.txt"; var queue = new Queue(numberOfLines); using (FileStream fs = File.Open(fullFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) using (BufferedStream bs = new BufferedStream(fs)) // May not make much difference. using (StreamReader sr = new StreamReader(bs)) { while (!sr.EndOfStream) { if (queue.Count == numberOfLines) { queue.Dequeue(); } queue.Enqueue(sr.ReadLine()); } } // The queue now has our set of lines. So print to console, save to another file, etc. do { Console.WriteLine(queue.Dequeue()); } while (queue.Count > 0); 

У меня была одна и та же проблема – огромный файл журнала, к которому нужно получить доступ через интерфейс REST. Конечно, загружать его в любую память и отправлять его через http не было решением.

Как отметил Джон, у этого решения есть очень специфическая утилита. В моем случае, я точно знаю (и проверяю), что кодировка является utf-8 (с BOM!) И, таким образом, может извлечь выгоду из всех благ UTF. Это, конечно, не решение общей цели.

Вот то, что работало для меня очень хорошо и быстро (я забыл закрыть stream – теперь исправлено):

  private string tail(StreamReader streamReader, long numberOfBytesFromEnd) { Stream stream = streamReader.BaseStream; long length = streamReader.BaseStream.Length; if (length < numberOfBytesFromEnd) numberOfBytesFromEnd = length; stream.Seek(numberOfBytesFromEnd * -1, SeekOrigin.End); int LF = '\n'; int CR = '\r'; bool found = false; while (!found) { int c = stream.ReadByte(); if (c == LF) found = true; } string readToEnd = streamReader.ReadToEnd(); streamReader.Close(); return readToEnd; } 

Сначала мы пытаемся найти что-то ближе к концу с помощью BaseStream, и когда у нас будет правильный stream positon, прочитайте его до конца с помощью обычного StreamReader.

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

Если мы используем этот код, это не помешает нити записи:

  FileStream fileStream = new FileStream( filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); StreamReader streamReader = new StreamReader(fileStream); 

Если вам нужно прочитать любое количество строк в обратном направлении от текстового файла, используйте LINQ-совместимый class, который вы можете использовать. Он фокусируется на производительности и поддержке больших файлов. Вы можете прочитать несколько строк и вызвать Reverse (), чтобы получить последние несколько строк в прямом порядке:

Использование :

 var reader = new ReverseTextReader(@"C:\Temp\ReverseTest.txt"); while (!reader.EndOfStream) Console.WriteLine(reader.ReadLine()); 

Класс ReverseTextReader :

 ///  /// Reads a text file backwards, line-by-line. ///  /// This class uses file seeking to read a text file of any size in reverse order. This /// is useful for needs such as reading a log file newest-entries first. public sealed class ReverseTextReader : IEnumerable { private const int BufferSize = 16384; // The number of bytes read from the uderlying stream. private readonly Stream _stream; // Stores the stream feeding data into this reader private readonly Encoding _encoding; // Stores the encoding used to process the file private byte[] _leftoverBuffer; // Stores the leftover partial line after processing a buffer private readonly Queue _lines; // Stores the lines parsed from the buffer #region Constructors ///  /// Creates a reader for the specified file. ///  ///  public ReverseTextReader(string filePath) : this(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.Default) { } ///  /// Creates a reader using the specified stream. ///  ///  public ReverseTextReader(Stream stream) : this(stream, Encoding.Default) { } ///  /// Creates a reader using the specified path and encoding. ///  ///  ///  public ReverseTextReader(string filePath, Encoding encoding) : this(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read), encoding) { } ///  /// Creates a reader using the specified stream and encoding. ///  ///  ///  public ReverseTextReader(Stream stream, Encoding encoding) { _stream = stream; _encoding = encoding; _lines = new Queue(128); // The stream needs to support seeking for this to work if(!_stream.CanSeek) throw new InvalidOperationException("The specified stream needs to support seeking to be read backwards."); if (!_stream.CanRead) throw new InvalidOperationException("The specified stream needs to support reading to be read backwards."); // Set the current position to the end of the file _stream.Position = _stream.Length; _leftoverBuffer = new byte[0]; } #endregion #region Overrides ///  /// Reads the next previous line from the underlying stream. ///  ///  public string ReadLine() { // Are there lines left to read? If so, return the next one if (_lines.Count != 0) return _lines.Dequeue(); // Are we at the beginning of the stream? If so, we're done if (_stream.Position == 0) return null; #region Read and Process the Next Chunk // Remember the current position var currentPosition = _stream.Position; var newPosition = currentPosition - BufferSize; // Are we before the beginning of the stream? if (newPosition < 0) newPosition = 0; // Calculate the buffer size to read var count = (int)(currentPosition - newPosition); // Set the new position _stream.Position = newPosition; // Make a new buffer but append the previous leftovers var buffer = new byte[count + _leftoverBuffer.Length]; // Read the next buffer _stream.Read(buffer, 0, count); // Move the position of the stream back _stream.Position = newPosition; // And copy in the leftovers from the last buffer if (_leftoverBuffer.Length != 0) Array.Copy(_leftoverBuffer, 0, buffer, count, _leftoverBuffer.Length); // Look for CrLf delimiters var end = buffer.Length - 1; var start = buffer.Length - 2; // Search backwards for a line feed while (start >= 0) { // Is it a line feed? if (buffer[start] == 10) { // Yes. Extract a line and queue it (but exclude the \r\n) _lines.Enqueue(_encoding.GetString(buffer, start + 1, end - start - 2)); // And reset the end end = start; } // Move to the previous character start--; } // What's left over is a portion of a line. Save it for later. _leftoverBuffer = new byte[end + 1]; Array.Copy(buffer, 0, _leftoverBuffer, 0, end + 1); // Are we at the beginning of the stream? if (_stream.Position == 0) // Yes. Add the last line. _lines.Enqueue(_encoding.GetString(_leftoverBuffer, 0, end - 1)); #endregion // If we have something in the queue, return it return _lines.Count == 0 ? null : _lines.Dequeue(); } #endregion #region IEnumerator Interface public IEnumerator GetEnumerator() { string line; // So long as the next line isn't null... while ((line = ReadLine()) != null) // Read and return it. yield return line; } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } #endregion } 

Почему бы не использовать file.readalllines, который возвращает строку []?

Затем вы можете получить последние 10 строк (или членов массива), которые будут тривиальной задачей.

Этот подход не учитывает какие-либо проблемы с кодировкой, и я не уверен в точности эффективности этого подхода (время, затраченное на полный метод и т. Д.).

  • Подчеркивать текст в UIlabel
  • IE 9 не использует подпиксельное сглаживание при определенных условиях
  • jQuery, если div содержит этот текст, замените ту часть текста
  • Запись текста на изображение в c #
  • Как искать и заменять текст в файле с помощью Python?
  • автоматически resize текста (размер шрифта) при изменении размера windows?
  • элемент поиска jquery по тексту
  • Приложение для Android в Eclipse: редактирование текста, не отображаемого на графическом макете
  • Предлагается ли использовать h: outputText для всего?
  • Загрузка и отображение больших текстовых файлов
  • Что представляет собой современное состояние для текстового рендеринга в OpenGL с версии 4.1?
  • Interesting Posts

    Создание classа LinkedList с нуля

    Неустранимая ошибка: неверный макет java.lang.String по значению

    Найти элемент большинства в массиве

    Зачем использовать Integer вместо Long?

    Как динамически создавать столбцы в DataGrid WPF?

    Как сбросить индикатор прогресса между задачами в JavaFX2?

    Нужно ли форматировать / разделять диск назначения ddrescue?

    Ошибка гибернации: org.hibernate.NonUniqueObjectException: другой объект с тем же значением идентификатора уже был связан с сеансом

    Как я могу исключить папку Outlook для Windows Search Index в Windows 7?

    Могу ли я сделать этот макрос более эффективным или быстрым?

    Почему .NET добавляет дополнительную косую черту к уже существующим косым чертам в пути?

    Каким детерминированным является неточность с плавающей запятой?

    Сообщение насоса в службе .NET Windows

    Оптимизировать диапазон запросов времени печати Postgres

    Является ли «структурное взлома» технически неопределенным поведением?

    Давайте будем гением компьютера.