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

фрагмент кода, который будет считывать последние "n строк" из файла журнала. Я придумал следующий код из сети. Я немного новичок в C sharp. Поскольку файл журнала может быть довольно большим, я хочу избежать дополнительных затрат на чтение всего файла. Может кто-нибудь предложить какое-либо улучшение производительности. яне очень хочется читать каждый символ и изменить положение.

   var reader = new StreamReader(filePath, Encoding.ASCII);
            reader.BaseStream.Seek(0, SeekOrigin.End);
            var count = 0;
            while (count <= tailCount)
            {
                if (reader.BaseStream.Position <= 0) break;
                reader.BaseStream.Position--;
                int c = reader.Read();
                if (reader.BaseStream.Position <= 0) break;
                reader.BaseStream.Position--;
                if (c == '\n')
                {
                    ++count;
                }
            }

            var str = reader.ReadToEnd();
 Russ Cam06 янв. 2011 г., 21:57
Взгляни наstackoverflow.com/questions/1271225/..., Вы могли бы тогда использовать расширение LINQ.Last() на IEnumerable, чтобы получить последние N строк
 SLaks06 янв. 2011 г., 21:56
Вы не можете использовать StreamReader таким образом.
 SLaks06 янв. 2011 г., 21:58
@Russ: Нет, ты не можешь. LINQ не может дать вам последнееn линий.
 CodesInChaos06 янв. 2011 г., 22:06
 Russ Cam06 янв. 2011 г., 22:01
@ Слакс - ой! Я думал, что была перегружена, чтобы получить последние N предметов ... был долгий день! Теперь, когда я думаю об этом, потребуется один раз вернуться в конце, чтобы получить N элементов.

Ответы на вопрос(7)

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

List<string> mostRecentLines = File.ReadLines(filePath)
    // .Where(....)
    // .Distinct()
    .Reverse()
    .Take(10)
    .ToList()

то вы можете рассчитать среднюю длину линии, а затем сделать следующее:

искать end_of_file - lines_needed * avg_line_length (previous_point)прочитайте все до концаесли вы схватили достаточно строк, это нормально. Если нет, переходите к предыдущей точке - lines_needed * avg_line_lengthчитать все до предыдущей точкиперейти к 3

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

Решение Вопроса

поскольку вы не разрешаете кэшированию.
Кроме того, это не будет работатьвообще для Юникода.

Я написал следующую реализацию:

///<summary>Returns the end of a text reader.</summary>
///<param name="reader">The reader to read from.</param>
///<param name="lineCount">The number of lines to return.</param>
///<returns>The last lneCount lines from the reader.</returns>
public static string[] Tail(this TextReader reader, int lineCount) {
    var buffer = new List<string>(lineCount);
    string line;
    for (int i = 0; i < lineCount; i++) {
        line = reader.ReadLine();
        if (line == null) return buffer.ToArray();
        buffer.Add(line);
    }

    int lastLine = lineCount - 1;           //The index of the last line read from the buffer.  Everything > this index was read earlier than everything <= this indes

    while (null != (line = reader.ReadLine())) {
        lastLine++;
        if (lastLine == lineCount) lastLine = 0;
        buffer[lastLine] = line;
    }

    if (lastLine == lineCount - 1) return buffer.ToArray();
    var retVal = new string[lineCount];
    buffer.CopyTo(lastLine + 1, retVal, 0, lineCount - lastLine - 1);
    buffer.CopyTo(0, retVal, lineCount - lastLine - 1, lastLine + 1);
    return retVal;
}
 frictionlesspulley06 янв. 2011 г., 22:35
очень понравилась идея смены буфера. Но не эффективно это прочитать весь файл журнала. Есть ли эффективный способ «поиска» в начале n-й строки и выполнения readLine () оттуда. Это может быть моим глупым сомнением !!
 SLaks06 янв. 2011 г., 23:06
@frictionlesspulley: попробуйтеstackoverflow.com/questions/398378/&he,llip;

а, возможно, в него что-то записывается, поэтому лучше убедиться, что вы его не блокируете.

Вы идете до конца. Начните читать назад, пока не дойдете до n строк. Тогда прочитайте все оттуда.

        int n = 5; //or any arbitrary number
        int count = 0;
        string content;
        byte[] buffer = new byte[1];

        using (FileStream fs = new FileStream("text.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            // read to the end.
            fs.Seek(0, SeekOrigin.End);

            // read backwards 'n' lines
            while (count < n)
            {
                fs.Seek(-1, SeekOrigin.Current);
                fs.Read(buffer, 0, 1);
                if (buffer[0] == '\n')
                {
                    count++;
                }

                fs.Seek(-1, SeekOrigin.Current); // fs.Read(...) advances the position, so we need to go back again
            }
            fs.Seek(1, SeekOrigin.Current); // go past the last '\n'

            // read the last n lines
            using (StreamReader sr = new StreamReader(fs))
            {
                content = sr.ReadToEnd();
            }
        }

Мой друг используетЭтот способ (BackwardReader может быть найденВот):

public static IList<string> GetLogTail(string logname, string numrows)
{
    int lineCnt = 1;
    List<string> lines = new List<string>();
    int maxLines;

    if (!int.TryParse(numrows, out maxLines))
    {
        maxLines = 100;
    }

    string logFile = HttpContext.Current.Server.MapPath("~/" + logname);

    BackwardReader br = new BackwardReader(logFile);
    while (!br.SOF)
    {
        string line = br.Readline();
        lines.Add(line + System.Environment.NewLine);
        if (lineCnt == maxLines) break;
        lineCnt++;
    }
    lines.Reverse();
    return lines;
}
 Jesse C. Slicer06 янв. 2011 г., 22:13
Я буду честен, SLaks, я не могу найти ничего в посте моего приятеля в блоге, который объясняет почему. Я вижу, что это по сути метод WCF, вызываемый из JavaScript, но я не уверен, что это адекватно объясняет это.
 Maarten20 авг. 2015 г., 11:14
Ссылка на BackwardReader больше не доступна.
 BrunoLM06 янв. 2011 г., 22:11
Тот же вопрос, что и SLaks, но +1 дляBackwardReader, Я не знал об этом.
 SLaks06 янв. 2011 г., 22:10
ПОЧЕМУ являетсяnumrows строка?
 SLaks06 янв. 2011 г., 23:15
Эта реализация BackwardReader является медленной (так как она не буферизируется) и не может поддерживать Unicode.

Вот мой ответ:

    private string StatisticsFile = @"c:\yourfilename.txt";

    // Read last lines of a file....
    public IList<string> ReadLastLines(int nFromLine, int nNoLines, out bool bMore)
    {
        // Initialise more
        bMore = false;
        try
        {
            char[] buffer = null;
            //lock (strMessages)  Lock something if you need to....
            {
                if (File.Exists(StatisticsFile))
                {
                    // Open file
                    using (StreamReader sr = new StreamReader(StatisticsFile))
                    {
                        long FileLength = sr.BaseStream.Length;

                        int c, linescount = 0;
                        long pos = FileLength - 1;
                        long PreviousReturn = FileLength;
                        // Process file
                        while (pos >= 0 && linescount < nFromLine + nNoLines) // Until found correct place
                        {
                            // Read a character from the end
                            c = BufferedGetCharBackwards(sr, pos);
                            if (c == Convert.ToInt32('\n'))
                            {
                                // Found return character
                                if (++linescount == nFromLine)
                                    // Found last place
                                    PreviousReturn = pos + 1; // Read to here
                            }
                            // Previous char
                            pos--;
                        }
                        pos++;
                        // Create buffer
                        buffer = new char[PreviousReturn - pos];
                        sr.DiscardBufferedData();
                        // Read all our chars
                        sr.BaseStream.Seek(pos, SeekOrigin.Begin);
                        sr.Read(buffer, (int)0, (int)(PreviousReturn - pos));
                        sr.Close();
                        // Store if more lines available
                        if (pos > 0)
                            // Is there more?
                            bMore = true;
                    }
                    if (buffer != null)
                    {
                        // Get data
                        string strResult = new string(buffer);
                        strResult = strResult.Replace("\r", "");

                        // Store in List
                        List<string> strSort = new List<string>(strResult.Split('\n'));
                        // Reverse order
                        strSort.Reverse();

                        return strSort;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine("ReadLastLines Exception:" + ex.ToString());
        }
        // Lets return a list with no entries
        return new List<string>();
    }

    const int CACHE_BUFFER_SIZE = 1024;
    private long ncachestartbuffer = -1;
    private char[] cachebuffer = null;
    // Cache the file....
    private int BufferedGetCharBackwards(StreamReader sr, long iPosFromBegin)
    {
        // Check for error
        if (iPosFromBegin < 0 || iPosFromBegin >= sr.BaseStream.Length)
            return -1;
        // See if we have the character already
        if (ncachestartbuffer >= 0 && ncachestartbuffer <= iPosFromBegin && ncachestartbuffer + cachebuffer.Length > iPosFromBegin)
        {
            return cachebuffer[iPosFromBegin - ncachestartbuffer];
        }
        // Load into cache
        ncachestartbuffer = (int)Math.Max(0, iPosFromBegin - CACHE_BUFFER_SIZE + 1);
        int nLength = (int)Math.Min(CACHE_BUFFER_SIZE, sr.BaseStream.Length - ncachestartbuffer);
        cachebuffer = new char[nLength];
        sr.DiscardBufferedData();
        sr.BaseStream.Seek(ncachestartbuffer, SeekOrigin.Begin);
        sr.Read(cachebuffer, (int)0, (int)nLength);

        return BufferedGetCharBackwards(sr, iPosFromBegin);
    }

Запись:-

Вызовите ReadLastLines с nLineFrom, начинающимся с 0 для последней строки, и nNoLines в качестве количества строк для чтения обратно.Он переворачивает список, поэтому первая строка - последняя строка в файле.bMore возвращает true, если есть еще строки для чтения.Он кэширует данные в 1024 чанках - поэтому это быстро, вы можете увеличить этот размер для очень больших файлов.

Наслаждайтесь!

что вы можете теперь очень легко сделать в C # 4.0 (и с небольшим усилием в более ранних версиях), это использовать файлы с отображением памяти для этого типа операций. Он идеально подходит для больших файлов, потому что вы можете отобразить только часть файла, а затем обращаться к нему как к виртуальной памяти.

Существуетхороший пример здесь.

 AaA28 нояб. 2014 г., 07:39
Это хорошая идея, однако, насколько я понимаю, она не позволяет читать файлы по строкам (тексту), когда задается вопрос.

Ваш ответ на вопрос