代码之家  ›  专栏  ›  技术社区  ›  Camilo Martin

如何设计包装并提供对单个文件访问的类?

  •  1
  • Camilo Martin  · 技术社区  · 14 年前

    MyClass 就是提供对单个文件的访问。它必须 CheckHeader() , ReadSomeData() , UpdateHeader(WithInfo) 等。

    但是由于这个类所代表的文件非常复杂,所以需要特别的设计考虑。

    该文件包含一个潜在的巨大文件夹,如具有各种节点类型的树结构,并且基于块/单元更好地处理碎片。大小通常小于20 MB。 这不是我设计的。

    你打算怎么设计这样一个班级?

    • 将~20MB的数据流读取到内存中?
    • 在temp dir上放置一个副本并将其路径保留为属性?

    • 在内存中保留一个大对象的副本,并将其作为只读属性公开?
    • GetThings() 从文件中抛出异常代码?

    这个类最初只由我使用,但如果它结束得足够好,我可能会打开它的源代码。

    (这是一个关于设计的问题,但是平台是.NET,类是关于XP的脱机注册表访问)

    2 回复  |  直到 14 年前
        1
  •  1
  •   Alois Kraus    14 年前

    注册表访问非常复杂。你基本上是在读一个大的二叉树。类设计应该很大程度上依赖于存储的数据结构。只有这样,您才能选择合适的类设计。为了保持灵活性,您应该为原语建模,如reg_sz、reg_expand_sz、dword、subkey等。DonSyme在他的书《专家F》中有一个关于二进制组合器的二进制解析的很好的章节。基本的思想是,您的对象自己知道如何从二进制表示中反序列化。当你有这样的字节流时

    <页眉与页眉; NoDE1/GT;
    NoDE2 & GT; <目录1>
    和/或NoDE2 & GT; L&/Health≫

    从BinaryReader开始,逐字节读取二进制对象。因为您知道第一件事必须是头,所以可以将其传递给头对象

    public class Header
    {
       static Header Deserialize(BinaryReader reader)
       {
          Header header = new Header();
    
          int magic = reader.ReadByte();
          if( magic == 0xf4 ) // we have a node entry
             header.Insert(Node.Read( reader );
          else if( magic == 0xf3 ) // directory entry
             header.Insert(DirectoryEntry.Read(reader))
          else 
             throw NotSupportedException("Invalid data");
    
          return header;
       }
    }
    

    为了保持性能,您可以将分析数据的时间延迟到稍后实际访问此实例或该实例的特定属性时。

    由于Windows中的注册表可能会变得很大,因此无法立即将其完全读取到内存中。你需要把它切成块。Windows应用的一个解决方案是将整个文件分配到分页池内存中,分页池内存可以跨越几GB,但只有实际访问的部分从磁盘交换到内存中。这使得Windows能够高效地处理非常大的注册表文件。你的读者也需要类似的东西。懒惰的解析是一个方面,在文件中跳跃而不需要读取其中的数据的能力是保持性能的关键。

    有关页面池和注册表的更多信息,请访问: http://blogs.technet.com/b/markrussinovich/archive/2009/03/26/3211216.aspx

    您的API设计将取决于您如何读取数据以保持效率(例如,使用 memory mapped file 从不同的映射区域读取)。有了.NET 4,一个内存映射文件实现已经到来,现在已经相当不错了,但是围绕OS API的包装器也存在。

    你的, 阿洛伊斯克劳斯

    为了支持从内存映射文件延迟加载,不需要将字节数组读取到对象中,稍后再对其进行分析,而是先执行一步,然后只存储内存映射文件中内存块的偏移量和长度。稍后,当实际访问对象时,您可以读取和反序列化数据。这样,您就可以遍历整个文件并构建一个对象树,其中只包含偏移量和对内存映射文件的引用。这样可以节省大量的内存。

        2
  •  3
  •   lfalin    14 年前

    这取决于您需要如何处理这些数据。如果只需要线性地处理一次,那么只需要占用内存中一个大文件的性能,可能会更快。

    但是,如果您需要对文件进行各种各样的操作,而不仅仅是一个线性解析,那么我将把数据解析到一个轻量级数据库(如sqlite)中,然后对其进行操作。这样可以保留文件的所有结构,并加快对该文件的所有后续操作。