C# 不同行长文件的二进制搜索

C# 不同行长文件的二进制搜索,c#,binary-search,C#,Binary Search,我有一些代码,它对每行带有排序十六进制值(SHA1哈希)的文件进行二进制搜索。这用于搜索数据库。最新版本包含找到每个密码哈希的次数计数,因此某些行的末尾有额外的字符,格式为“:###” 这张附加支票的长度不是固定的,也不总是固定的。这会导致缓冲区读取不正确的值,并且无法找到实际存在的值 当前代码: static bool Check(string asHex, string filename) { const int LINELENGTH = 40; //SHA1 hash lengt

我有一些代码,它对每行带有排序十六进制值(SHA1哈希)的文件进行二进制搜索。这用于搜索数据库。最新版本包含找到每个密码哈希的次数计数,因此某些行的末尾有额外的字符,格式为“:###”

这张附加支票的长度不是固定的,也不总是固定的。这会导致缓冲区读取不正确的值,并且无法找到实际存在的值

当前代码:

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;
}
静态布尔检查(字符串asHex,字符串文件名)
{
const int LINELENGTH=40;//SHA1哈希长度
var buffer=新字节[LINELENGTH];
使用(var sr=File.OpenRead(文件名))
{
//行数
var高=(sr.Length/(LINELENGTH+2))-1;
var低=0升;

虽然(低我认为这可能是一个更简单(更快)的解决方案,不需要回溯到行的开头。我认为您可以只使用字节文件索引,而不是尝试使用完整的“记录/行”。因为中间索引并不总是在行/记录的开头,即“读取行”“可以返回部分行/记录。如果您立即执行第二个“readline”,您将得到完整的行/记录。这不是很理想,因为您实际上会在中间索引之前进行比较

我下载了pWNED PASSWORDES 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;
}
const int HASHLENGTH=40;
静态布尔检查(字符串asHex、字符串文件名)
{
使用(var fs=File.OpenRead(文件名))
{
var低=0升;
//我们不需要从最后开始
var high=fs.Length-(HASHLENGTH-1);//EOF-1 HASHLENGTH
StreamReader sr=新的StreamReader(fs);
而(低0)和(!sr.EndOfStream))readLine=sr.readLine();
string[]parts=readLine.Split(“:”);
字符串哈希=部分[0];
//默认情况下,字符串比较是否进行区域性敏感比较我们可能不是我们想要的?
//进行顺序比较(0-90)
{
低=中+1;
}
其他的
{
返回true;
}
}
}
返回false;
}

我解决问题的方法是创建一个只包含散列的新二进制文件。16字节/散列和更快的二进制搜索(我不需要50次重复才能进行注释)

听起来可能更容易规范化(通过填充每行/条目)或者,您也可以生成一个单独的索引文件,其中包含起始位置(搜索偏移量)我曾考虑过只检查文件并删除所有散列计数,但理想情况下,我希望它能够处理提供的文件,而不是我自己的版本。索引应该可以工作。为它指定相同的文件名和.index后缀,并且仅在密码文件较新时生成。使用二进制将使每个entry相同大小(文件位置为长)并且应该使读取速度更快。您在索引上运行二进制搜索,查找当前密码哈希,并从密码文件中查找和读取。因此,您建议创建另一个文件,但在某些行末尾没有计数?文件有多大?我认为这不起作用,但结果是有一个版本是错误的按计数而不是has值进行orted,导致搜索失败。我刚刚通过读取每个哈希并使用您的函数在文件中找到它来测试它,它对每个值都有效!谢谢!我的建议是在查找之前丢弃BufferedData,而不是在查找之后。在t之后完成丢弃时,我看到了奇怪的调试结果seek。具体来说,seek清除EndOfStream,但它可以通过DiscardBufferedData重新设置(即使seek不是文件的结尾)。这会导致读取失败(返回空值)。对我来说,这似乎是一个错误,但解决方法很简单。对于“双读”方法,存在两种特殊情况。1)文件的开头(此处处理);2)文件结尾(此处未完全处理)。如果拆分行时sr.EndOfStream为true,则必须检查部分[0]长度。如果短于散列长度,则必须将其处理为与“高”读取相同(高=中+1并继续)。添加此检查后,无需在开始时将文件长度减少HASHLENGTH。这是一件好事,因为HASHLENGTH不是行的长度,只是行的一部分,因此其效果不可靠。