我想像GNU tail一样连续读取文件“-f”param。我需要它来实时读取日志文件。 什么是正确的方法?
答案 0 :(得分:25)
您想要以二进制模式打开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
方法的计时器。您可以通过在程序开始时只打开一次文件来优化一些事情。这取决于你。
答案 1 :(得分:24)
使用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
仅用于唤醒主读取周期。
答案 2 :(得分:3)
要持续监控文件的尾部,您只需记住之前文件的长度。
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部分更改为不同的时间,也可以将其与filewatcher和阻塞模式链接 - Monitor.Enter / Wait / Pulse。对我来说,计时器足够了,如果文件没有改变,它最多只会每秒检查一次文件长度。
答案 3 :(得分:2)
您可以使用FileSystemWatcher类,该类可以发送文件系统上发生的不同事件的通知,例如文件更改。
答案 4 :(得分:2)
这是我的解决方案
<h1>Enter Name</h1>
<form method="get">
<p>
<label for="requestName">Enter Your Name:</label>
<input type="search" name="nameEntered" id="nameEntered">
<input type="submit" name="submit" id="submit" value="Submit">
</p>
</form>
<script type="text/javascript">
n=document.location.search.substring(0);
document.write("<h3>")
n1=document.location.search.substring(1);
n2=new Array();
n2=n1.split('&');
var x = n2[0].split('=');
document.write("You entered the name " + x[1]);
document.write("</h3>")
</script>
所以,在你的代码中你可以做到
static IEnumerable<string> TailFrom(string file)
{
using (var reader = File.OpenText(file))
{
while (true)
{
string line = reader.ReadLine();
if (reader.BaseStream.Length < reader.BaseStream.Position)
reader.BaseStream.Seek(0, SeekOrigin.Begin);
if (line != null) yield return line;
else Thread.Sleep(500);
}
}
}
答案 5 :(得分:0)
private void button1_Click(object sender, EventArgs e)
{
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
path = folderBrowserDialog.SelectedPath;
fileSystemWatcher.Path = path;
string[] str = Directory.GetFiles(path);
string line;
fs = new FileStream(str[0], FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
tr = new StreamReader(fs);
while ((line = tr.ReadLine()) != null)
{
listBox.Items.Add(line);
}
}
}
private void fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
string line;
line = tr.ReadLine();
listBox.Items.Add(line);
}
答案 6 :(得分:-4)
如果您只是想找一个工具来执行此操作,请查看免费版Bare tail