文件有 1.5G ,目前采用的读取方法
using (var reader = new BinaryReader(new FileStream(FileStorePathTable, FileMode.Open), Encoding.ASCII))
{
    // ...             
}
请问有没有性能更好的方法?
大文件处理最快的据说是内存映射文件,没怎么具体测试过。
你这样一次读取全部的字节, 估计会很慢吧,是不是考虑 分段读取 ,一次读10M 这种
FileStream~~理解Stream和File.ReadAll的区别。
FileStream的操作方式已经透明了Buffer的实现过程。
如果只执行读和保存,上述Reader和Writer默认参数方式肯定不会达到10M。
若题主并不会具体操作具体的byte,也不想跳过文件系统,可以换个Reader,调整Buffer参数(如跟文件系统的块成倍数关系)。
剥离中间过程,如跳过文件系统,直接读取扇区。
若题主并不会具体操作具体的byte,也不想跳过文件系统,可以换个Reader,调整Buffer参数(如跟文件系统的块成倍数关系,Buffer.Size 的倍数n大于等于1倍文件系统的块大小)。
public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
是附带了Buffer这个参数的,你可以根据自己的文件系统块大小调整。
如果是Win,直接win32读肯定也要快一些(算是跳过.net的过程,比如上面有安全句柄),自行Buffer,然后操作读写;
内存映射
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Text;
namespace ConsoleDemo
{
class Program
{
private const string TXT_FILE_PATH = @"E:\开源学习\超大文本文件读取\File\a.txt";
private const string SPLIT_VARCHAR = "囧";
private const char SPLIT_CHAR = '囧';
private static long FILE_SIZE = 0;
static void Main(string[] args)
{
//long ttargetRowNum = 39999999;
long ttargetRowNum = 10000000;
DateTime beginTime = DateTime.Now;
string line = CreateMemoryMapFile(ttargetRowNum);
double totalSeconds = DateTime.Now.Subtract(beginTime).TotalSeconds;
Console.WriteLine(line);
Console.WriteLine(string.Format("查找第{0}行,共耗时:{1}s", ttargetRowNum, totalSeconds));
Console.ReadLine();
}
    /// <summary>
    /// 创建内存映射文件
    /// </summary>
    private static string CreateMemoryMapFile(long ttargetRowNum)
    {
        string line = string.Empty;
        using (FileStream fs = new FileStream(TXT_FILE_PATH, FileMode.Open, FileAccess.ReadWrite))
        {
            long targetRowNum = ttargetRowNum + 1;//目标行
            long curRowNum = 1;//当前行
            FILE_SIZE = fs.Length;
            using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fs, "test", fs.Length, MemoryMappedFileAccess.ReadWrite, null, HandleInheritability.None, false))
            {
                long offset = 0;
                //int limit = 250;
                int limit = 200;
                try
                {
                    StringBuilder sbDefineRowLine = new StringBuilder();
                    do
                    {
                        long remaining = fs.Length - offset;
                        using (MemoryMappedViewStream mmStream = mmf.CreateViewStream(offset, remaining > limit ? limit : remaining))
                        {
                            offset += limit;
                            using (StreamReader sr = new StreamReader(mmStream))
                            {
                                string ss = sr.ReadToEnd().ToString().Replace("\n", SPLIT_VARCHAR).Replace(Environment.NewLine, SPLIT_VARCHAR);
                                if (curRowNum <= targetRowNum)
                                {
                                    if (curRowNum < targetRowNum)
                                    {
                                        string s = sbDefineRowLine.ToString();
                                        int pos = s.LastIndexOf(SPLIT_CHAR);
                                        if (pos > 0)
                                            sbDefineRowLine.Remove(0, pos);
                                    }
                                    else
                                    {
                                        line = sbDefineRowLine.ToString();
                                        return line;
                                    }
                                    if (ss.Contains(SPLIT_VARCHAR))
                                    {
                                        curRowNum += GetNewLineNumsOfStr(ss);
                                        sbDefineRowLine.Append(ss);
                                    }
                                    else
                                    {
                                        sbDefineRowLine.Append(ss);
                                    }
                                }
                                sr.Dispose();
                            }
                            mmStream.Dispose();
                        }
                    } while (offset < fs.Length);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                return line;
            }
        }
    }
    private static long GetNewLineNumsOfStr(string s)
    {
        string[] _lst = s.Split(SPLIT_CHAR);
        return _lst.Length - 1;
    }
}
}
用磁盘阵列存储文件,比如RAID Level 0,理论上读取速度是放在一块磁盘上的n倍(n是磁盘数量)。或者通过代码把大文件拆分后分别保存到不同的磁盘上,读取的时候几个磁盘一起读,然后再合并。
参考资料:How to read a binary file quickly in c#? (ReadOnlySpan vs MemoryStream)
– dudu 5年前