我想像GNU tail一样用"-f“参数连续读取文件。我需要它来实时读取日志文件。什么才是正确的方法呢?
发布于 2010-09-25 05:28:16
您希望以二进制模式打开FileStream
。定期查找到文件末尾减去1024字节(或其他任何字节),然后读取到末尾并输出。这就是tail -f
的工作原理。
您的问题的答案:
二进制,因为如果您将其作为文本读取,则很难随机访问该文件。您必须自己完成二进制到文本的转换,但这并不困难。(见下文)
1024字节,因为这是一个非常方便的数字,应该可以处理10到15行文本。通常是这样。
以下是打开文件、读取最后1024字节并将其转换为文本的示例:
static void ReadTail(string filename)
{
using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// Seek 1024 bytes from the end of the file
fs.Seek(-1024, SeekOrigin.End);
// read 1024 bytes
byte[] bytes = new byte[1024];
fs.Read(bytes, 0, 1024);
// Convert bytes to string
string s = Encoding.Default.GetString(bytes);
// or string s = Encoding.UTF8.GetString(bytes);
// and output to console
Console.WriteLine(s);
}
}
请注意,您必须使用FileShare.ReadWrite
打开,因为您正在尝试读取当前由另一个进程打开以供写入的文件。
还要注意,我使用的是Encoding.Default
,在美国/英语和大多数西欧语言中,它都是8位字符编码。如果文件是以其他编码(如UTF-8或其他Unicode编码)编写的,则字节可能无法正确转换为字符。如果您认为这将是一个问题,那么您必须通过确定编码来处理它。有关确定文件的文本编码的信息,请搜索堆栈溢出。
如果您希望定期执行此操作(例如,每15秒),则可以设置一个计时器,该计时器可以根据需要随时调用ReadTail
方法。你可以通过在程序开始时只打开文件一次来优化它。那随你的便。
发布于 2014-07-28 18:57:53
使用FileSystemWatcher
的更自然的方法
var wh = new AutoResetEvent(false);
var fsw = new FileSystemWatcher(".");
fsw.Filter = "file-to-read";
fsw.EnableRaisingEvents = true;
fsw.Changed += (s,e) => wh.Set();
var fs = new FileStream("file-to-read", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using (var sr = new StreamReader(fs))
{
var s = "";
while (true)
{
s = sr.ReadLine();
if (s != null)
Console.WriteLine(s);
else
wh.WaitOne(1000);
}
}
wh.Close();
在这里,主读周期停止等待传入的数据,FileSystemWatcher
只是用来唤醒主读周期。
发布于 2017-02-28 07:03:33
要连续监控文件的尾部,您只需记住文件之前的长度。
public static void MonitorTailOfFile(string filePath)
{
var initialFileSize = new FileInfo(filePath).Length;
var lastReadLength = initialFileSize - 1024;
if (lastReadLength < 0) lastReadLength = 0;
while (true)
{
try
{
var fileSize = new FileInfo(filePath).Length;
if (fileSize > lastReadLength)
{
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
fs.Seek(lastReadLength, SeekOrigin.Begin);
var buffer = new byte[1024];
while (true)
{
var bytesRead = fs.Read(buffer, 0, buffer.Length);
lastReadLength += bytesRead;
if (bytesRead == 0)
break;
var text = ASCIIEncoding.ASCII.GetString(buffer, 0, bytesRead);
Console.Write(text);
}
}
}
}
catch { }
Thread.Sleep(1000);
}
}
我不得不使用ASCIIEncoding,因为这段代码不够智能,无法在缓冲区边界上处理可变字符长度的UTF8。
备注:您可以将Thread.Sleep部件更改为不同的计时,也可以将其与文件守护程序和阻塞模式监视器相链接。输入/等待/脉冲。对我来说,计时器就足够了,如果文件没有变化,它最多只能每秒检查一次文件长度。
https://stackoverflow.com/questions/3791103
复制相似问题