我需要知道如何读取文本文件的最后一行。我需要找到该行,然后将其处理到 SQL 数据库中……
我一直在阅读并在网上搜索,但我正在努力寻找正确的方法来做到这一点。IE:
- 查找文件的最后一行。
- 处理文件的最后一行。
有两种方法:简单而低效,或者极其复杂但高效。复杂的版本假定一个理智的编码。
除非您的文件太大以至于您真的无法阅读全部内容,否则我只会使用:
var lastLine = File.ReadLines("file.txt").Last();
请注意,这使用File.ReadLines
,而不是 File.ReadAllLines
。如果您使用的是 .NET 3.5 或更早版本,则需要使用File.ReadAllLines
或编写自己的代码 -一次性ReadAllLines
将整个文件读入内存,同时ReadLines
将其流式传输。
否则,复杂的方法是使用类似于this的代码。它尝试从文件末尾向后读取,处理诸如 UTF-8 多字节字符之类的脏乱。这并不令人愉快。
第一部分:
File.ReadAllLines(@"c:\some\path\file.txt").Last();
或者
File.ReadLines(@"c:\some\path\file.txt").Last();
ReadLines 是首选。
我会简单地结合File.ReadLines(path)
和Enumerable.Last
:
String last = File.ReadLines(@"C:\file.txt").Last();
它流式传输行并且不会将所有行加载到内存中File.ReadAllLines
。
string m = "";
StreamReader r = new StreamReader("file_path");
while (r.EndOfStream == false)
{
m = r.ReadLine();
}
Console.WriteLine("{0}\n", m);
r.Close();
注意:所有这些代码都假定为 UTF-8。如果您需要支持使用像 Unicode 这样的双宽字符的代码页,那么您需要在换行符之前和/或之后对字符添加额外的检查,以确保它确实是换行符。
这个问题的主要用例之一是抓取日志文件的末尾。不幸的是,当日志文件达到兆字节时,其他答案就惨死了。想象一下,在一个微小的单核 VPS 上每次调用都运行每一行......哎呀。
UTF-8 的好处在于,当您点击 '\n' 字符时,您不必担心任何相关字节,因为在 UTF8-8 中任何高位清除的字节都只是一个 ASCII 字符。很方便!
您可以使用“如何使用 C# 中的迭代器反向读取文本文件”中的解决方案,但请注意代码相当复杂。如果您只需要一个简单的 UTF-8 行尾,那么此解决方案将非常有效,即使在大型日志文件上也能表现出色。
如果您一次监视大量文件并在 C# 中使用 FileSystemWatcher 之类的东西,那么这种性能提升将非常重要。我在廉价的单 CPU Linux VPS 上使用非常相似的代码来监控登录失败,并将 ip 地址放入我的 MIT 许可项目https://github.com/DigitalRuby/IPBan的防火墙中,使用https://github.com/ DigitalRuby/IPBan/blob/master/IPBanCore/Core/Utility/LogFileScanner.cs(一次处理多个新行)。
当您的 SSH 端口面向公众时,您会惊讶于 auth.log 的大小。如果你经常阅读几十个甚至几百个文件,你会很高兴你没有使用File.ReadAllLines().Last();
由于这只是一页代码,因此在简单和非常快速之间取得了很好的平衡。
C#代码...
/// <summary>
/// Utility class to read last line from a utf-8 text file in a performance sensitive way. The code does not handle a case where more than one line is written at once.
/// </summary>
public static class UTF8FileUtilities
{
/// <summary>
/// Read the last line from the file. This method assumes that each write to the file will be terminated with a new line char ('\n')
/// </summary>
/// <param name="path">Path of the file to read</param>
/// <returns>The last line or null if a line could not be read (empty file or partial line write in progress)</returns>
/// <exception cref="Exception">Opening or reading from file fails</exception>
public static string ReadLastLine(string path)
{
// open read only, we don't want any chance of writing data
using (System.IO.Stream fs = System.IO.File.OpenRead(path))
{
// check for empty file
if (fs.Length == 0)
{
return null;
}
// start at end of file
fs.Position = fs.Length - 1;
// the file must end with a '\n' char, if not a partial line write is in progress
int byteFromFile = fs.ReadByte();
if (byteFromFile != '\n')
{
// partial line write in progress, do not return the line yet
return null;
}
// move back to the new line byte - the loop will decrement position again to get to the byte before it
fs.Position--;
// while we have not yet reached start of file, read bytes backwards until '\n' byte is hit
while (fs.Position > 0)
{
fs.Position--;
byteFromFile = fs.ReadByte();
if (byteFromFile < 0)
{
// the only way this should happen is if someone truncates the file out from underneath us while we are reading backwards
throw new System.IO.IOException("Error reading from file at " + path);
}
else if (byteFromFile == '\n')
{
// we found the new line, break out, fs.Position is one after the '\n' char
break;
}
fs.Position--;
}
// fs.Position will be right after the '\n' char or position 0 if no '\n' char
byte[] bytes = new System.IO.BinaryReader(fs).ReadBytes((int)(fs.Length - fs.Position));
return System.Text.Encoding.UTF8.GetString(bytes);
}
}
}
string last = File.ReadLines(@"C:\file.txt").Last();
string lastsymbol = last[last.Count - 1];