在 C# 中向后读取大文件(从头到尾)

Reading large files backwards (from end to start) in C#

我有一个包含一系列价格数据的文本文件。 该问题可能与任何长期的历史数据相匹配,例如温度、空气湿度、价格、日志文件...

我的历史文件的头部如下所示:

如果我想读取和处理一个太大而无法占用内存的文件,我通常会选择以下代码:

using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
    string line;
    while ((line = sr.ReadLine()) != null)
    {
        // Process Data
    }
}

在我的例子中,每 1000 毫秒创建一条记录。最新数据位于文件末尾。 尝试处理最新数据时会出现问题。

示例:
我想生成过去 30 天的平均值。
从文件末尾开始并向开头移动直到达到 X 天阈值将是最有效的。 上面的示例代码将读取整个文件,这在这种情况下几乎不可用。 A worst-case 每次我需要更新最近的数据指标。 这个问题当然适用于任何你想处理最后 x 个元素的操作。

有没有从头到尾读取文件的功能?

您可以使用 Seek 到达文件末尾,但是您需要 "guess" 或计算距离末尾的距离...例如读取最后 1024 个字节:

    stream.Seek(-1024, SeekOrigin.End);

只需算出最后 30 行最多可以有多少字节,并在文件末尾之前查找到那么远,然后只读取文件的那部分

试试下面的代码。最后一行可以是空白。不确定处理最后一行空白的最佳方式。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace GetFileReverse
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.txt";
        static void Main(string[] args)
        {
            GetFileReverse getFileReverse = new GetFileReverse(FILENAME);
            string line = "";
            while ((line = getFileReverse.ReadLine()) != null)
            {
                Console.WriteLine(line);
            }
        }
    }
    public class GetFileReverse : IDisposable
    {
        const int BUFFER_SIZE = 1024;
        private FileStream stream { get; set; }
        private string data { get; set; }
        public Boolean SOF { get; set; }
        private long position { get; set; }
        public GetFileReverse(string filename)
        {
            stream = File.OpenRead(filename);
            if (stream != null)
            {
                position = stream.Seek(0, SeekOrigin.End);
                SOF = false;
                data = string.Empty;
            }
            else
            {
                SOF = true;
            }
        }
        private byte[] ReadStream()
        {
            byte[] bytes = null;
            int size = BUFFER_SIZE;
            if (position != 0)
            {
                bytes = new byte[BUFFER_SIZE];
                long oldPosition = position;
                if (position >= BUFFER_SIZE)
                {
                    position = stream.Seek(-1 * BUFFER_SIZE, SeekOrigin.Current);
                }
                else
                {
                    position = stream.Seek(-1 * position, SeekOrigin.Current);
                    size = (int)(oldPosition - position);
                    bytes = new byte[size];
                }
                stream.Read(bytes, 0, size);
                stream.Seek(-1 * size, SeekOrigin.Current);
            }
            return bytes;

        }
        public string ReadLine()
        {
            string line = "";
            while (!SOF && (!data.Contains("\r\n")))
            {
                byte[] bytes = ReadStream();
                if (bytes != null)
                {
                    string temp = Encoding.UTF8.GetString(bytes);
                    data = data.Insert(0, temp);
                }
                SOF = position == 0;
            }


            int lastReturn = data.LastIndexOf("\r\n");
            if (lastReturn == -1)
            {
                if (data.Length > 0)
                {
                    line = data;
                    data = string.Empty;
                }
                else
                {
                    line = null;
                }
            }
            else
            {
                line = data.Substring(lastReturn + 2);
                data = data.Remove(lastReturn);
            }

            return line;
        }
        public void Close()
        {
            stream.Close();
        }
        public void Dispose()
        {
            stream.Dispose();
            data = string.Empty;
            position = -1;
        }
    }
}