Solution 檔案
WinForm Layout
Form1.cs
using System.IO;
namespace WalkthroughBackgroundWorker
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// 屬性設定
// 使用 ReportProgress() 必須把 WorkerReportsProgress 設為 true
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
// 事件註冊
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted;
backgroundWorker1.ProgressChanged += BackgroundWorker1_ProgressChanged;
backgroundWorker1.DoWork += BackgroundWorker1_DoWork;
txtSourceFile.Text = @"D:\Sample.txt";
txtCompareString.Text = "21934";
CounterReset();
ButtonControl(true);
}
private void StartThread()
{
CounterReset();
Words WC = new Words();
WC.SourceFile = txtSourceFile.Text.Trim();
WC.CompareString = txtCompareString.Text.Trim();
// RunWorkerAsync() 會觸發 DoWork Event
backgroundWorker1.RunWorkerAsync(WC);
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// This event handler is where the actual work is done.
// This method runs on the background thread.
BackgroundWorker worker = (BackgroundWorker)sender;
Words WC = (Words)e.Argument;
WC.CountWords(worker, e);
}
private void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// This method runs on the main thread.
// 更新統計資訊
Words.CurrentState state = (Words.CurrentState)e.UserState;
txtLinesCounted.Text = state.LinesCounted.ToString();
txtWordsCounted.Text = state.WordsMatched.ToString();
}
private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// This event handler is called when the background thread finishes.
// This method runs on the main thread.
if (e.Error != null)
MessageBox.Show("錯誤訊息:" + e.Error.Message);
else if (e.Cancelled)
MessageBox.Show("字數統計取消");
else
MessageBox.Show("完成字數統計");
ButtonControl(true);
}
private void btnStart_Click(object sender, EventArgs e)
{
if (File.Exists(txtSourceFile.Text.Trim()) == false)
{
MessageBox.Show("該檔案路徑不存在,無法進行解析", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
ButtonControl(false);
StartThread();
}
private void btnCancel_Click(object sender, EventArgs e)
{
// 取消非同步操作
backgroundWorker1.CancelAsync();
CounterReset();
ButtonControl(true);
}
private void ButtonControl(bool state)
{
btnStart.Enabled = state;
btnCancel.Enabled = !state;
}
private void CounterReset()
{
txtWordsCounted.Text = "0";
txtLinesCounted.Text = "0";
}
private void btnFileSelect_Click(object sender, EventArgs e)
{
OpenFileDialog fd = new OpenFileDialog();
fd.Filter = "txt 檔案 (*.*)|*.txt";
fd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (fd.ShowDialog() == DialogResult.Cancel) return;
txtSourceFile.Text = fd.FileName;
}
}
}
Words.csusing System.Text.RegularExpressions;
using System.ComponentModel;
using System.IO;
using System.Threading;
namespace WalkthroughBackgroundWorker
{
public class Words
{
// Object to store the current state, for passing to the caller.
public class CurrentState
{
public int LinesCounted { get; set; }
public int WordsMatched { get; set; }
}
public string SourceFile { get; set; }
public string CompareString { get; set; }
private int WordCount { get; set; }
private int LinesCounted { get; set; }
public void CountWords(BackgroundWorker worker, DoWorkEventArgs e)
{
if (string.IsNullOrEmpty(CompareString))
throw new Exception("沒有指定搜尋字串");
// 變數初始化
CurrentState state = new CurrentState();
string line = string.Empty;
// 利用 StreamReader 讀取 txt 檔案
using (StreamReader sr = new StreamReader(SourceFile))
{
// Process lines while there are lines remaining in the file.
while (!sr.EndOfStream)
{
// 使用者取消後,就不繼續讀取
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
line = sr.ReadLine();
WordCount += CountInString(line, CompareString);
// 紀錄行數
LinesCounted++;
state.LinesCounted = LinesCounted;
state.WordsMatched = WordCount;
worker.ReportProgress(0, state);
// 處理每一行都停止 50 毫秒,方便觀察執行結果
Thread.Sleep(50);
}
// Report the final count values.
state.LinesCounted = LinesCounted;
state.WordsMatched = WordCount;
worker.ReportProgress(100, state);
}
}
private int CountInString(string SourceString,string CompareString)
{
// This function counts the number of times
// a word is found in a line.
if (SourceString == null)
{
return 0;
}
// Regex.Escape:以逸出碼取代 (\、*、+、?、|、{、[、(、)、^、$、.、# 和泛空白字元) 等字元。 這樣會指示規則運算式引擎將這些字元解譯為常值,而非解譯為中繼字元。
string EscapedCompareString = Regex.Escape(CompareString);
// To count all occurrences of the string, even within words, remove both instances of @"\b" from the following line.
// 下述兩種寫法都可以使用
string pattern = @"\b" + EscapedCompareString + @"\b";
// string pattern = $"\\b{EscapedCompareString}\\b";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
MatchCollection matches = regex.Matches(SourceString);
return matches.Count;
}
}
}
下面這段 Code,用途應該是每 20 毫秒才透過 ReportProgress() 觸發 ProgressChanged Event,避免觸發太過頻繁,不過個人感覺用 Thread.Sleep 觀察執行結果,效果比較好,所以就把它拿掉int compare = DateTime.Compare(
DateTime.Now, lastReportDateTime.AddMilliseconds(elapsedTime));
if (compare > 0)
{
// ...................................
}
統計完成執行過程中,取消統計
- 參考資料
- Walkthrough: Multithreading with the BackgroundWorker Component (C#)
- BackgroundWorker 類別
- 如何:在背景執行作業
- BackgroundWorker.ReportProgress
- BackgroundWorker.ProgressChanged 事件
- ProgressChangedEventArgs 類別
- BackgroundWorker.RunWorkerAsync
- BackgroundWorker.DoWork
- DoWorkEventArgs 類別
- DateTime.AddMilliseconds
- StreamReader.ReadLine 方法 ()
- StreamReader.EndOfStream 屬性
- Regex.Escape 方法
- Regex.Match 方法
沒有留言:
張貼留言