[英]c# continuously read file
我想像帶有“ -f”參數的GNU尾部一樣連續讀取文件。 我需要它來實時讀取日志文件。 正確的做法是什么?
您要以二進制模式打開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
調用ReadTail
方法。 您可以通過在程序啟動時僅打開一次文件來稍微優化一下。 隨你(由你決定。
使用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
僅用於喚醒主讀取周期。
要連續監視文件的尾部,您只需要記住文件的長度即可。
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部分更改為不同的時間,還可以將其與文件監視程序和阻止模式鏈接-Monitor.Enter / Wait / Pulse。 對我來說,計時器就足夠了,如果文件沒有更改,它最多只能每秒檢查一次文件長度。
這是我的解決方案
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);
}
}
}
因此,在您的代碼中,您可以執行
foreach (string line in TailFrom(file))
{
Console.WriteLine($"line read= {line}");
}
您可以使用FileSystemWatcher類,該類可以發送有關文件系統上發生的不同事件(例如文件更改)的通知。
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);
}
如果您正在尋找執行此操作的工具,請查看免費版本的Bare tail
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.