我有一些代码在每行上对带有排序十六进制值(SHA1哈希值)的文件进行二进制搜索。这用于搜索HaveIBeenPwned数据库。最新版本包含每个密码哈希的查找次数,因此某些行末尾有额外的字符,格式为':###'
此附加检查的长度未修复,并且始终不存在。这会导致缓冲区读取不正确的值,并且无法找到实际存在的值。
当前代码:
static bool Check(string asHex, string filename)
{
const int LINELENGTH = 40; //SHA1 hash length
var buffer = new byte[LINELENGTH];
using (var sr = File.OpenRead(filename))
{
//Number of lines
var high = (sr.Length / (LINELENGTH + 2)) - 1;
var low = 0L;
while (low <= high)
{
var middle = (low + high + 1) / 2;
sr.Seek((LINELENGTH + 2) * ((long)middle), SeekOrigin.Begin);
sr.Read(buffer, 0, LINELENGTH);
var readLine = Encoding.ASCII.GetString(buffer);
switch (readLine.CompareTo(asHex))
{
case 0:
return true;
case 1:
high = middle - 1;
break;
case -1:
low = middle + 1;
break;
default:
break;
}
}
}
return false;
}
我的想法是从中间向前寻找,直到找到换行符,然后向后寻找相同的点,这应该给我一个完整的线,我可以通过&#39;:&#39;分隔符。然后我比较分裂字符串数组的第一部分,它应该只是一个SHA1哈希值。
我认为这仍然应该以正确的值为中心,但是我想知道是否有更简洁的方法来做到这一点?如果中点不是行尾字符之间的实际中点,是否应该在高值和低值之前进行调整?
答案 0 :(得分:2)
我认为这可能是一种可能更简单(更快)的解决方案,而不需要回溯到行的开头。我认为你可以使用字节文件索引,而不是尝试使用完整的“记录/行。因为中间索引不会总是在行/记录的开头,”readline“可以返回部分行/记录如果你要立即做第二个“readline”,你会得到一个完整的行/记录。这不是最佳的,因为你实际上会比中间索引稍微好一点。
我下载了pwned-passwords-update-1并在开始,结束和中间拉出了大约30条记录,它似乎找到了所有记录。你觉得怎么样?
const int HASHLENGTH = 40;
static bool Check(string asHex, string filename)
{
using (var fs = File.OpenRead(filename))
{
var low = 0L;
// We don't need to start at the very end
var high = fs.Length - (HASHLENGTH - 1); // EOF - 1 HASHLENGTH
StreamReader sr = new StreamReader(fs);
while (low <= high)
{
var middle = (low + high + 1) / 2;
fs.Seek(middle, SeekOrigin.Begin);
// Resync with base stream after seek
sr.DiscardBufferedData();
var readLine = sr.ReadLine();
// 1) If we are NOT at the beginning of the file, we may have only read a partial line so
// Read again to make sure we get a full line.
// 2) No sense reading again if we are at the EOF
if ((middle > 0) && (!sr.EndOfStream)) readLine = sr.ReadLine() ?? "";
string[] parts = readLine.Split(':');
string hash = parts[0];
// By default string compare does a culture-sensitive comparison we may not be what we want?
// Do an ordinal compare (0-9 < A-Z < a-z)
int compare = String.Compare(asHex, hash, StringComparison.Ordinal);
if (compare < 0)
{
high = middle - 1;
}
else if (compare > 0)
{
low = middle + 1;
}
else
{
return true;
}
}
}
return false;
}
答案 1 :(得分:0)
我解决您问题的方法是创建一个仅包含哈希值的新二进制文件。 16 字节/哈希和更快的二进制搜索(我不需要 50 个代表只需要评论)