C# 二进制文件中的二进制搜索包

C# 二进制文件中的二进制搜索包,c#,search,serialization,binary-search,C#,Search,Serialization,Binary Search,我有由许多24字节数据包组成的二进制文件,每个数据包中的前8个字节表示DateTime类型的序列化时间戳。数据包都是按时间戳升序排列的。我想开发一个二进制搜索算法,它选择前8个字节,反序列化时间戳,并将其与所需的时间戳进行比较 目标是在二进制文件中找到表示与所需时间戳匹配的序列化时间戳的起始位置的位置 编辑 数据位于二进制文件中,而不是数据结构中,因此List.BinarySearch对我不起作用。但是有可能在带有CustomComparer的流上使用BinarySearch吗 该文件包含许多数

我有由许多24字节数据包组成的二进制文件,每个数据包中的前8个字节表示DateTime类型的序列化时间戳。数据包都是按时间戳升序排列的。我想开发一个二进制搜索算法,它选择前8个字节,反序列化时间戳,并将其与所需的时间戳进行比较

目标是在二进制文件中找到表示与所需时间戳匹配的序列化时间戳的起始位置的位置

编辑 数据位于二进制文件中,而不是数据结构中,因此List.BinarySearch对我不起作用。但是有可能在带有CustomComparer的流上使用BinarySearch吗


该文件包含许多数以千万计的此类数据包,因此对该文件进行简单的迭代将非常低效。我考虑了二进制搜索方法。

还没有测试过,但是重点是在文件中间读取8个字节,而不是移动到右边或左边中间,并重复,这取决于读取时间戳。不是最干净的代码。复杂性将是LogN

public class BinaryFinder
{
    private readonly long _packagesCount;
    private readonly FileStream _reader;

    public BinaryFinder(FileStream reader, int packageSize)
    {
        _reader = reader;
        _packagesCount = reader.Length / packageSize;
    }


    public long Find(DateTime dateToSearch)
    {
        return Find(0, _packagesCount, dateToSearch);
    }

    private long Find(long minPosition, long maxPosition, DateTime dateToSearch)
    {
        while (minPosition<=maxPosition) {
            var newPosition = (minPosition + maxPosition) / 2;
            var readDate = ReadDateAt(newPosition);

            if (readDate == dateToSearch) {
                return newPosition;
            }

            if (dateToSearch < readDate){
                maxPosition = newPosition-1;
            }
            else {
                minPosition = newPosition+1;
            }
        }

        return -1;
    }

    private DateTime ReadDateAt(long middlePosition)
    {
        var buffer = new byte[8];

        _reader.Seek(middlePosition, SeekOrigin.Begin);
        _reader.Read(buffer, 0, buffer.Length);

        var currentDate = ConvertBytesToDate(buffer);
        return currentDate;
    }

    private static DateTime ConvertBytesToDate(byte[] dateBytes)
    {
        throw new NotImplementedException();
    }
}

好的,这是代码中的疯狂想法,检查它,它将返回您正在寻找的时间戳的结构索引

只需实例化一个FileStructListfileName,然后执行list.BinarySearchIndexOftheTimeStamp

您甚至可以将其传递给自己的比较器:

这包括对代码的二进制搜索,但由于它是IList,因此可以使用集合可用的任何搜索方法

public class FileStructList : IList<long>
{

    Stream baseStream;
    BinaryReader reader;
    int length;
    int headerSize;

    public FileStructList(string FileName, int HeaderSize)
    {
        baseStream = File.OpenRead(FileName);
        reader = new BinaryReader(baseStream);
        length = (int)((baseStream.Length - HeaderSize) / 24);
        headerSize = HeaderSize;
    }

    public long this[int index]
    {
        get
        {
            baseStream.Seek(24 * index + headerSize, SeekOrigin.Begin);
            return reader.ReadInt64();
        }

        set
        {
            throw new NotImplementedException();
        }
    }

    public int Count
    {
        get
        {
            return length;
        }
    }

    public bool IsReadOnly
    {
        get
        {
            return true;
        }
    }

    public void Add(long item)
    {
        throw new NotImplementedException();
    }

    public void Clear()
    {
        throw new NotImplementedException();
    }

    public bool Contains(long item)
    {
        return BinarySearchIndexOf(item) != -1;
    }

    public void CopyTo(long[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public IEnumerator<long> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public int IndexOf(long item)
    {
        return BinarySearchIndexOf(item);
    }

    public void Insert(int index, long item)
    {
        throw new NotImplementedException();
    }

    public bool Remove(long item)
    {
        throw new NotImplementedException();
    }

    public void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public Int32 BinarySearchIndexOf(long value, IComparer<long> comparer = null)
    {
        comparer = comparer ?? Comparer<long>.Default;

        Int32 lower = 0;
        Int32 upper = length - 1;

        while (lower <= upper)
        {
            Int32 middle = lower + (upper - lower) / 2;
            Int32 comparisonResult = comparer.Compare(value, this[middle]);
            if (comparisonResult == 0)
                return middle;
            else if (comparisonResult < 0)
                upper = middle - 1;
            else
                lower = middle + 1;
        }

        return -1;
    }
}

使用结构列表的问题是,您必须在搜索之前读取所有数据,可能只是读取流,直到您找到您想要的更多efficient@Codor,可能会多一点颜色?是的,它是按时间排序的,我认为有必要自定义二进制搜索的实现;您可能需要一个可以重新定位读取光标的流,如下所示;也许一个所谓的函数也能有所帮助。是的,这就是我所想的,在二进制读取器的基流中来回移动位置。我只是希望CustomComparer能帮我保存二进制搜索的实现部分。这是递归的,文件中的用户可以有数百万个条目,在最坏的情况下不会导致堆栈溢出吗?@Gusman也可以不用递归。好的观点是的,我知道这可以不用递归,只需要一个循环就可以完成,只是说明了问题,以防用户尝试。@Gusman不,这不会导致StackOverflow异常。二进制搜索的时间复杂度为OLog n,日志1000000为6,这意味着最多6次调用,您可以找到您的项目!这真是太神奇了,不是吗?@M.kazemAkhgary是6,10垒。在样本中,上述基数为2。因此,它将是大约20个步骤的最大值。我将如何解释补偿?我将头信息存储在前10000个字节中。让我修改代码,很简单,将偏移量传递给构造函数,并将其求和到seek偏移量。这是一个非常简洁的解决方案。非常感谢。很高兴你喜欢它:。您甚至可以扩展它来修改实现集合的时间戳,或者实现GetEnumerator以允许元素上的foreach。我知道添加foreach支持等对于您的最终目标来说不是很有效,但是如果您想操作文件上的数据,这是一个很好的功能。是的,这就是我喜欢的解决方案,它非常可扩展。我这样说是因为我也希望实现数据包插入,但需要检查是否可以调整开放流的大小,或者如果我想插入可以扩大文件大小的元素,是否需要创建一个新文件