c# - 对不同行长度的文件进行二分搜索

标签 c# binary-search

我有一些代码对文件进行二进制搜索,每行都有排序的十六进制值(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;
}

我的想法是从中间向前查找,直到找到换行符,然后向后查找同一点,这应该给我一个完整的行,我可以用“:”分隔符分隔它。然后,我比较分割字符串数组的第一部分,它应该只是 SHA1 哈希值。

我认为这仍然应该以正确的值为中心,但是我想知道是否有更简洁的方法来做到这一点?如果中点不是行尾字符之间的实际中点,是否应该在高值和低值之前调整它?

最佳答案

我认为这可能是一个可能更简单(更快)的解决方案,无需回溯到行的开头。我认为您可以只使用字节文件索引,而不是尝试使用完整的“记录/行”。因为中间索引并不总是位于行/记录的开头,所以“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;
}

关于c# - 对不同行长度的文件进行二分搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49035142/

相关文章:

c# - 为什么我们不能像使用 WCF 或 ASMX 一样在 Visual Studio 中将 Web API 添加为 "service reference"?

c++ - 为什么我们在二分查找中写 lo+(hi-lo)/2?

C++ std::lower_bound() 函数查找索引排序 vector 的插入点

c# - winforms中的软件还在开发中吗?

c# - 如何为 'float method' 返回 null - 错误处理

c++ - 使用二进制搜索查找数字第 N 次出现的索引

c - 使 C 比较函数对称

algorithm - 在不使用反馈的情况下查找数组中的偶数

c# - ASP.Net MVC 删除带有 FK 约束的记录

c# - 我怎样才能实现这个搜索