VB.NET在.NET Framework 4.8下无需读取全文件获取文件末尾行的方案
反向读取文件末尾指定行数实现方案(.NET Framework 4.8 兼容)
核心原理
直接通过FileStream将文件指针定位到文件尾部,从后往前扫描换行符,累计匹配到指定数量的换行符后,再读取对应区间的内容转成文本即可,全程不需要加载整个文件到内存,读取速度不受文件整体大小影响。
注意事项
- 兼容Windows风格
\r\n和Unix风格\n两种换行格式,避免出现行截断 - 支持自定义文本编码,可根据实际日志文件的编码调整参数
- 自动处理边界情况:如果文件实际行数小于需要读取的行数,直接返回全部文件内容
VB 实现代码
Imports System.IO Imports System.Text ' 方法参数:文件路径、要读取的末尾行数、文本编码(默认UTF-8,可按需调整) Public Function ReadLastLines(filePath As String, linesCount As Integer, Optional encoding As Encoding = Nothing) As String If encoding Is Nothing Then encoding = Encoding.UTF8 If linesCount <= 0 Then Return String.Empty If Not File.Exists(filePath) Then Return String.Empty Const bufferSize As Integer = 4096 ' 单次读取缓冲区大小,可按需调整 Dim lineTerminator As Char = ControlChars.Lf ' 以换行符作为行判定基准 Dim foundLines As Integer = 0 Dim resultBuilder As New StringBuilder() Using fs As New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) ' 处理空文件情况 If fs.Length = 0 Then Return String.Empty Dim filePosition As Long = fs.Length - 1 Dim buffer As Byte() = New Byte(bufferSize - 1) {} Dim isFirstBlock As Boolean = True While filePosition >= 0 AndAlso foundLines < linesCount Dim readSize As Integer = CInt(Math.Min(bufferSize, filePosition + 1)) filePosition -= readSize fs.Seek(filePosition, SeekOrigin.Begin) fs.Read(buffer, 0, readSize) ' 从后往前扫描当前缓冲区的换行符 For i As Integer = readSize - 1 To 0 Step -1 If buffer(i) = AscW(lineTerminator) Then foundLines += 1 If foundLines = linesCount Then ' 已经找够行数,截取当前位置之后的内容 Dim segmentLength As Integer = readSize - i - 1 If segmentLength > 0 Then resultBuilder.Insert(0, encoding.GetString(buffer, i + 1, segmentLength)) End If Exit For End If End If Next ' 还没找够行数,把整个缓冲区内容拼到结果前面 If foundLines < linesCount Then resultBuilder.Insert(0, encoding.GetString(buffer, 0, readSize)) End If ' 处理最后一行末尾没有换行符的情况 If isFirstBlock Then isFirstBlock = False If buffer(readSize - 1) <> AscW(lineTerminator) Then foundLines += 1 End If End If End While End Using Return resultBuilder.ToString().TrimEnd(ControlChars.Cr, ControlChars.Lf) End Function ' 调用示例 Sub Main() Dim filePath = "C:\file.xy" Dim linesCount = 5 ' 若日志是系统默认编码用Encoding.Default,UTF-8的话可以不传第三个参数 Dim result = ReadLastLines(filePath, linesCount, Encoding.Default) Console.WriteLine(result) End Sub
性能对比
你原有实现中File.ReadAllLines会把整个文件的内容全部加载到内存拆分出行数组,当日志文件达到GB级别时,会占用大量内存且读取耗时极长。
本实现仅会读取末尾需要的行数附近的少量内容,即使是10GB以上的日志文件,读取最后5行的耗时也能稳定在毫秒级。
内容的提问来源于stack exchange,提问作者TheDomis4
相关产品推荐
相关产品推荐

