[英]Extract the hash value substring from each line in a TextBox
我正在嘗試制作一個可以一次驗證多個文件哈希的應用程序。 The way I've done it is like this: Hash source files from location 1 and output to a textbox filename, hash type and the hash itself. Hash 源文件從位置 2 和 output 到另一個文本框的第二個文件名,hash 類型和 Z0809452577283944C5433
問題是路徑不同,並且有幾個哈希值需要驗證。 我不知道如何拆分字符串以使其正常工作,以便僅檢查哈希與其他哈希,而無需文件名。
這就是我得到 hash 字符串的方式:
string[] files = filePicketTextBox.Text.Split('\n');
if (files.Length > 0)
{
foreach (var file in files).. //do hash
以下是 hash 的 output 的完成方式:
output = file + " MD5 Hash = " + sb.ToString() + "\n";
這是 output 文本框中的樣子:
C:\Users\jj\Downloads\hasher.zip MD5 Hash = 8B0A222D30CA4962AFE30511695C8BB3
C:\Users\jj\Downloads\OfficeSetup.exe MD5 Hash = 2E210D07A9AE9B93FAEB0852A0DAFF83
這是我用來驗證哈希的方法:
private void VerifyHashes(object sender, RoutedEventArgs e)
{
string output_one = outputTextBox.Text;
string output_two = output2TextBox.Text;
if (output_one == output_two)
{
Verification.Text = "HASHES VERIFIED";
}
else
{
Verification.Text = "NO MATCH";
}
誰能幫助我如何拆分它以刪除這些文件名並考慮到多個文件名將一次得到驗證?
完整代碼:
private void filePickerButton_Click(object sender, RoutedEventArgs e)
{
// Create the CommonOpenFIleDialog object
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = "C:\\Users";
dialog.IsFolderPicker = false;
dialog.Multiselect = true;
// Check to see if we have a result
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
filePicketTextBox.Text = string.Join("\n", dialog.FileNames);
}
else
{
outputTextBox.Text = "Operation cancelled." + "\n";
}
}
private async void runButton_Click(object sender, RoutedEventArgs e)
{
File.Delete(Path.Combine(Environment.CurrentDirectory, "log.txt"));
// Detect and handle the event of a void filename
if (filePicketTextBox.Text == "")
{
outputTextBox.Text = "No file selected." + "\n";
return;
}
string[] files = filePicketTextBox.Text.Split('\n');
if (files.Length > 0)
{
foreach (var file in files)
{
// Detect and handle the event of a non-valid filename
try
{
var stream = File.OpenRead(file);
stream.Close();
}
catch
{
outputTextBox.Text = "Invalid filename." + "\n";
return;
}
// Detect event of no options selected
if (!(bool)md5CheckBox.IsChecked && !(bool)sha1CheckBox.IsChecked && !(bool)sha256CheckBox.IsChecked && !(bool)sha512CheckBox.IsChecked)
{
outputTextBox.Text = "No hash algorithm selected.";
}
// MD5 Calculation and Display
if ((bool)md5CheckBox.IsChecked)
{
await Task.Run(() =>
{
var result = checkMD5(file).Result;
WriteIntoTxtBox(result);
});
await Task.Delay(1000);
progressbar.Value = 0;
}
MD5 計算代碼:
public async Task<string> checkMD5(string file)
{
string output;
using (var md5 = MD5.Create())
{
AddValueLoadingBar(20, true);
using (var stream = File.OpenRead(file))
{
byte[] hash = md5.ComputeHash(stream);
AddValueLoadingBar(60, true);
await Task.Delay(1000);
StringBuilder sb = new StringBuilder(hash.Length);
int toAdd = 30 / hash.Length;
foreach (byte b in hash)
{
sb.AppendFormat("{0:X2}", b);
AddValueLoadingBar(toAdd);
}
output = file + " MD5 Hash = " + sb.ToString() + "\n";
AddValueLoadingBar(100, true);
}
}
return output;
}
這工作正常:
string[] Files = strFileBuf.Split('\n');
string[] MyDelim = { " MD5 Hash = " };
foreach (string OneRow in Files)
{
string[] TwoParts = OneRow.Split(MyDelim,StringSplitOptions.None);
string FileNameOnly = Path.GetFileName(TwoParts[0]);
string MyHash = TwoParts[1];
TextBox1.Text += ("File = " + FileNameOnly + " ->Hash = " + MyHash + "\n");
}
Output:
因此,您可以在每一行上拆分 (\n)
然后你可以假設分隔符是中間的字符串部分。
因此,我們拆分為文件名和 hash。
然后我只顯示文件名(沒有路徑)和 hash 代碼。
***編輯 **************************************
好的,用戶希望 output 排序。 他們應該張貼一些標記。
在這里,我們將做什么:
我們假設兩個輸入字符串 - 從文件,“到”文件。
如前所述,我真的必須假設某種列表或比兩個字符串更好的東西是這兩個文件集的源。
然而,下面的代碼可以很好地適應——並且可以直接從原始的兩個文件“列表”中運行。
這兩個字符串 - 來自文本框,來自海底 - 不在乎。
所以,我們假設頁面上有這個標記:
<asp:Button ID="Button1" runat="server" Height="31px" Text="Button" Width="110px" />
<br />
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
<br />
一個簡單的按鈕,一個簡單的 gridView。
我們會將結果推送/發送到網格視圖 - 排序。
上述按鈕后面的代碼可能/可能如下所示:
protected void Button1_Click(object sender, EventArgs e)
{
string strFileBuf1 = ""; ' set this from/input files string
string strFileBuf2 = ""; ' set this to files string
SortedDictionary<string, string> Loc1Files;
SortedDictionary<string, string> Loc2Files;
Loc1Files = GetFiles(strFileBuf1);
Loc2Files = GetFiles(strFileBuf2);
DataTable MyTable = new DataTable();
MyTable.Columns.Add("FromFile", typeof(string));
MyTable.Columns.Add("ToFile", typeof(string));
MyTable.Columns.Add("Match", typeof(string));
foreach (KeyValuePair<string, string> OneFile in Loc1Files)
{
DataRow OneRow;
OneRow = MyTable.Rows.Add;
OneRow("FromFile") = OneFile.Key;
if (Loc2Files.ContainsKey(OneFile.Key))
{
OneRow("ToFile") = OneFile.Key;
// compare hash codes
if (OneFile.Value == Loc2Files[OneFile.Key])
OneRow("Match") = "Yes";
else
OneRow("Match") = "No";
}
else
{
OneRow("ToFile") = "missing";
OneRow("Match") = "missing";
}
}
GridView1.DataSource = MyTable;
GridView1.DataBind();
}
Output:
拆分數據並返回排序后的字典列表的函數/子是這樣的——我們假設這“兩組”文件作為那些字符串。
public SortedDictionary<string, string> GetFiles(string str)
{
string[] Files = str.Split('\n');
SortedDictionary<string, string> MyList = new SortedDictionary<string, string>();
foreach (string OneRow in Files)
{
string[] MyDelim = { " MD5 Hash = " };
string[] TwoParts = OneRow.Split(MyDelim,StringSplitOptions.None);
MyList.Add(Path.GetFileName(TwoParts[0]), TwoParts[1]);
}
return MyList;
}
您應該格式化您的 output 使用除空格以外的一些分隔符,因為文件名中可以有空格,否則您需要考慮這一點。
您的解決方案非常尷尬,當然可以重新設計以獲得更好的解決方案。
但要直接回答您的問題,您可以將 output 加入Dictionary<string,string>
中,其中鍵是文件名,值是 hash
var d1 = outputTextBox.Text.Split(Environment.NewLine.ToCharArray())
.Select(x => x.Split(' '))
.ToDictionary(
// use the file name without the directory as the key.
x => System.IO.Path.GetFileName(string.Join(" ", x.Take(x.Length - 4))),
// the has will be the last sequence of characters following the last space
x => x[x.Length - 1]
);
var d2 = output2TextBox.Text.Split(Environment.NewLine.ToCharArray())
.Select(x => x.Split(' '))
.ToDictionary(
// use the file name without the directory as the key.
x => System.IO.Path.GetFileName(string.Join(" ", x.Take(x.Length - 4))),
// the has will be the last sequence of characters following the last space
x => x[x.Length - 1]
);
在您需要使用文件名作為鍵比較兩個字典的內容之后。
這可以按如下方式完成
///compare the the keys of the two dictionaries match.
var areEqual = d1.Keys
.OrderBy(x=> x) // sort the first dictionaries keys
// comare the sorted keys to the sorted keys of the second dictionary
.SequenceEqual(d2.Keys.OrderBy(x=>x));
if (areEqual)
{
// if the keys match, then compare the hashes
areEqual = d1.Select(x => x.Value == d2[x.Key])
.All(x => x == true);
}
現在,如果您只關心兩個目錄中的文件是否匹配,您可以只收集哈希值並進行比較。
var outputTextBox = new TextBox();
var output2TextBox = new TextBox();
var hashes_left = outputTextBox.Text
.Split(Environment.NewLine.ToCharArray())
.Select(x => x.Substring(x.LastIndexOf(' ') + 1))
.OrderBy(x => x);
var hashes_right = output2TextBox.Text
.Split(Environment.NewLine.ToCharArray())
.Select(x => x.Substring(x.LastIndexOf(' ') + 1))
.OrderBy(x => x);
var areEqual = hashes_left.SequenceEqual(hashes_right);
然而,即使有這些直接的答案,正確的做法是重新設計您的代碼以獲得更有效的解決方案,例如使用自定義 class,您可以在其中比較 memory 中的強類型項目。
澄清一下,使用自定義類正確重新設計解決方案看起來像這樣:
public class FileCollectionComparer
{
private HashAlgo algo;
private string[] x;
private string[] y;
public FileCollectionComparer(HashAlgo hashAlgo, string[] x, string[] y)
{
this.algo = hashAlgo;
this.x = x.OrderBy(z=> Path.GetFileName(z)).ToArray();
this.y = y.OrderBy(z => Path.GetFileName(z)).ToArray();
}
public bool AreEqual()
{
if (x.Length != y.Length)
return false;
else if (!x.Select(z=> Path.GetFileName(z))
.SequenceEqual(y.Select(z=> Path.GetFileName(z))))
return false;
else
{
var a = x.GetEnumerator();
var b = y.GetEnumerator();
while (a.MoveNext())
{
b.MoveNext()
var xHash = Hasher.GetHash((string)a.Current, algo);
var yHash = Hasher.GetHash((string)b.Current, algo);
if (xHash != yHash)
return false;
}
}
return true;
}
}
我們可以將HashAlgo
枚舉和Hasher
定義為:
public enum HashAlgo
{
MD5,
SHA1,
SHA256,
SHA512
}
public class Hasher
{
public static string GetHash(string filePath, HashAlgo algo)
{
HashAlgorithm hasher = null;
switch (algo)
{
case HashAlgo.MD5:
hasher = MD5.Create();
break;
case HashAlgo.SHA1:
hasher = SHA1.Create();
break;
case HashAlgo.SHA256:
hasher = SHA256.Create();
break;
case HashAlgo.SHA512:
hasher = SHA512.Create();
break;
default:
throw new InvalidEnumArgumentException(nameof(algo), (int)algo, typeof(HashAlgo));
}
using (var fs = File.OpenRead(filePath))
return string.Join("", hasher.ComputeHash(fs).Select(x => x.ToString("X2")));
}
}
我已經做了很多工作來將您的代碼減少到計算散列的基本要素,而不依賴於 UI 。 然后我得到了需要分離 UI 的代碼,這樣它就與計算 hashes 無關。
首先,我創建了一個散列 function,您可以使用它來計算任何 hash。
public Task<string> ComputeHash(string file, Func<HashAlgorithm> create)
{
return Task.Run(() =>
{
using (var crypto = create())
{
using (var stream = File.OpenRead(file))
{
return String.Concat(
crypto.ComputeHash(stream).Select(x => x.ToString("X2")));
}
}
});
}
我現在創建了兩個字段來存儲文件列表和生成的哈希值。
private string[] _fileNames = null;
private string[] _hashes = null;
稍后我將展示設置_fileNames
數組的代碼,但現在這是您可以調用來計算散列的代碼:
private async Task<String> Run(bool md5, bool sha1, bool sha256, bool sha512)
{
File.Delete(Path.Combine(Environment.CurrentDirectory, "log.txt"));
Func<HashAlgorithm> hashAlgorithm = null;
if (md5) hashAlgorithm = () => MD5.Create();
else if (sha1) hashAlgorithm = () => SHA1.Create();
else if (sha256) hashAlgorithm = () => SHA256.Create();
else if (sha512) hashAlgorithm = () => SHA512.Create();
if (hashAlgorithm == null)
{
return "No hash algorithm selected.";
}
else if (_fileNames == null || _fileNames.Length == 0)
{
return "No file selected." + "\n";
}
else if (_fileNames.Any(f => !File.Exists(f)))
{
return "Invalid filename." + "\n";
}
else
{
var tasks = _fileNames.Select(f => ComputeHash(f, hashAlgorithm)).ToArray();
_hashes = await Task.WhenAll(tasks);
return "Success";
}
}
在我看來,這段代碼最難的部分是使用Func<HashAlgorithm> hashAlgorithm
來存儲散列算法。 如果您對Func<>
不熟悉,那么值得查找,但可以將其視為將 function 存儲在變量中的一種方式,以便您稍后可以調用 function。
現在,最后,我們有了與 UI 交互的兩段代碼:
private void filePickerButton_Click(object sender, RoutedEventArgs e)
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = "C:\\Users";
dialog.IsFolderPicker = false;
dialog.Multiselect = true;
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
_fileNames = dialog.FileNames.ToArray();
filePicketTextBox.Text = string.Join("\n", dialog.FileNames);
}
else
{
outputTextBox.Text = "Operation cancelled." + "\n";
}
}
private async void runButton_Click(object sender, RoutedEventArgs e)
{
string result = await Run(
md5CheckBox.IsChecked,
sha1CheckBox.IsChecked,
sha256CheckBox.IsChecked,
sha512CheckBox.IsChecked);
outputTextBox.Text = result;
}
最終結果是您有兩個 arrays、 _fileNames
和_hashes
可用於為每個文件名獲取 hash。 無需拆分文本。
好的,就像在 hash 方法中添加這一行一樣簡單。
file = Path.GetFileName(file);
這是方法內部。
public async Task<string> checkMD5(string file)
{
string output;
using (var md5 = MD5.Create())
{
AddValueLoadingBar(20, true);
using (var stream = File.OpenRead(file))
{
byte[] hash = md5.ComputeHash(stream);
AddValueLoadingBar(60, true);
await Task.Delay(1000);
StringBuilder sb = new StringBuilder(hash.Length);
int toAdd = 30 / hash.Length;
foreach (byte b in hash)
{
sb.AppendFormat("{0:X2}", b);
AddValueLoadingBar(toAdd);
}
file = Path.GetFileName(file);
output = file + " MD5 Hash = " + sb.ToString() + "\n";
AddValueLoadingBar(100, true);
}
}
return output;
我在 output 之前添加了這個,導致 output 不包含完整路徑名,這是假設哈希相同的文本框之間唯一發生變化的事情。 文件名是一樣的,如果hash也是,那就驗證了。
這引起了很多工作,但答案很簡單。 不過,我的代碼肯定需要重新編寫:) 感謝所有回答這個問題的人。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.