【问题标题】:C# linq expression. How to get different offsets and 'any' offset?C# linq 表达式。如何获得不同的偏移量和“任何”偏移量?
【发布时间】:2021-11-16 10:09:03
【问题描述】:

我想读出文件的标题并将其与给定的签名进行比较。签名的开头可以有一个偏移量。

这是我目前的功能:

    public FileTypeVerifyResult Verify(Stream stream)
    {
        stream.Position = 0;
        var reader = new BinaryReader(stream);
        var headerBytes = reader.ReadBytes(SignatureLength + this.OffSet);

        return new FileTypeVerifyResult
        {
            Name = Name,
            Description = Description,
            IsVerified = Signatures.Any(signature =>
                headerBytes.Skip(this.OffSet).Take(signature.Length)
                    .SequenceEqual(signature)
            )
        };
    }

这目前适用于一个偏移量,但存在可以具有多个偏移量的扩展。所以我的第一个想法是使OffSet 属性成为具有所有偏移量的int[],但是我不知道这是否可以轻松地构建到这个 linq 表达式中。

此外,文件可以具有偏移量any(或任何其他表示文件中任何位置的值,例如-1)。怎么会有这样的东西?

【问题讨论】:

  • 您需要处理掉您的阅读器和流。
  • 属性不应该被键入为可变数组(例如int[]),因为不清楚属性是否总是返回相同的数组 - 或者在每次调用时返回一个新数组 - 并且也不表示数组是否可变。相反,为什么不在Verify 方法中添加一个params int[] offsets 参数?
  • 你不应该盲目地设置stream.Position = 0,因为不是每个Stream都是可搜索的(例如NetworkStream是不可搜索的)。您需要先检查if( stream.CanSeek )

标签: c# linq


【解决方案1】:

为了能够处理更多的偏移,这是我的解决方案:

    protected List<int> OffSets { get; set; }

    public int OffSetLength => OffSets.Max(m => m);

    public FileTypeVerifyResult Verify(Stream stream)
    {
        stream.Position = 0;
        var reader = new BinaryReader(stream);
        var headerBytes = reader.ReadBytes(SignatureLength + OffSetLength);
        reader.Close();

        return new FileTypeVerifyResult
        {
            Name = Name,
            Description = Description,
            IsVerified = Signatures.Any(signature =>
                OffSets.Any(offSet => 
                    headerBytes.Skip(offSet).Take(signature.Length)
                        .SequenceEqual(signature))
            )
        };
    }

如果不重写所有内容,我认为我无法在此函数中获得 any/-1 偏移量。

【讨论】:

  • 您的代码的运行时复杂度为 O(n *m),您可以显着改进它。
【解决方案2】:

我认为这是瑞士刀不是你想要的情况,Linq 对很多事情都有好处,但它确实没有为手头的问题增加任何价值,恕我直言,因为编写这样的查询会让人眼前一亮流血,但这是我对问题本身的看法,如果有的话,很少使用 LINQ :)

我允许自己扣除未提供的缺失项目并合理命名。

public class FileType
{
    public class FileTypeVerifyResult
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public bool IsVerified { get; set; }
    }

    public class Signature
    {
        /// <summary>
        /// Actual signature
        /// </summary>
        public byte[] Payload { get; set; }
        public int Length { get => Payload.Length; }
        public List<int> OffSets { get; set; }
    }
    
    public string Name { get; set; }
    public string Description { get; set; }

    public List<Signature> Signatures { get; set; }

    public FileTypeVerifyResult Verify(Stream stream)
    {
        if (stream == null) throw new ArgumentException(nameof(stream));
        if(stream.CanSeek)
            stream.Position = 0;
        else
            //TODO: Consider if we need this: throw new ArgumentException("Stream does not support seek", nameof(stream));
        
        var bytes = new byte[stream.Length];
        int wasRead = stream.Read(bytes, 0, (int)stream.Length);
        if (wasRead == 0) throw new ArgumentException("Unable to read from stream");
        
        foreach (var signature in Signatures)
        {
            foreach(var offset in signature.OffSets)
            {
                if(offset == -1)
                {
                    //TODO: read signature length from each position first 0, then 1 until what is left is less than length of signature and if it fits
                    return createResultObject(true);
                }

                var candidateBytes = new byte[signature.Length];
                candidateBytes = bytes.Skip(offset).Take(signature.Length).ToArray();
                //  I'd not use linq, instead  Array.Copy(bytes, offset, candidateBytes, 0, signature.Length);
                if(signature.Payload.Equals(candidateBytes))                    
                {
                    return createResultObject(true);    
                }
            }                
        }
        return createResultObject();

        FileTypeVerifyResult createResultObject(bool isValid = false)
        {
            return new FileTypeVerifyResult
            {
                Name = Name,
                Description = Description,
                IsVerified = isValid
            };
        }
    }
}

【讨论】:

  • 为什么这些类是可变的?
  • 这是解决问题的一个小技巧,不是一个完整的解决方案,我相信它可以被优化为不可变的,用于非常高强度的处理,很好的建议
  • 不变性与性能无关(这只是一个很好的副作用),它实际上是关于能够推理程序的数据流:没有不变性,对象的属性可以在实例化后任意修改,并且它会抛出有关其 ctor 对对象状态所做的所有保证(例如,如果您的 class Signature 是由忘记设置 Payload 的人实例化的,那么 Length 属性的 getter 将扔一个NullReferenceException - 一个令人讨厌的惊喜。
  • 我个人并不反对,但这完全与我们不知道的问题无关,即使课程看起来像我猜的一样,请参阅上面的代码。社区成员想看看是否可以这样使用 Linq,我的回应是为他的问题提供解决方案,但不要使用超级复杂的 linq。显然,类型定义不是每个设计都应该改变的东西,所以我们需要防止它,这是一种错误的使用模式,也许但可能不是。用户将新建类型定义并处理很多文件或仅处理一个。
猜你喜欢
  • 2010-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-31
  • 1970-01-01
  • 1970-01-01
  • 2019-09-08
相关资源
最近更新 更多