济南建设网站的公司,wordpress添加订阅教程,博罗网站设计公司,wordpress原图对比插件最近工作上遇到一个问题#xff0c;就是有将近200万个CSV文件#xff0c;每个CSV文件包含了成千上万条实验数据#xff0c;CSV以一个不连续的整数值作为文件名#xff0c;比如#xff1a;1.CSV、2.CSV、3.CSV、5.CSV等等。另外又有200万个XML文件#xff0c;每个XML文件的… 最近工作上遇到一个问题就是有将近200万个CSV文件每个CSV文件包含了成千上万条实验数据CSV以一个不连续的整数值作为文件名比如1.CSV、2.CSV、3.CSV、5.CSV等等。另外又有200万个XML文件每个XML文件的文件名与CSV的文件名一一对应在这些XML文件中定义了所对应的CSV实验数据文件的实验描述信息比如实验名称、实验类型等等也就是说每个XML包含的是它所对应的CSV文件的元数据。现在的一个需求是当软件中列出其中一部分比如几千个或者几万个CSV文件时需要在每个文件名边上显示对应的实验名称。咋一看这样的需求感觉比较简单当显示某个CSV文件时直接找到对应的XML文件解析XML得到名称就结束了。然而问题是这就需要软件本身自带这200万个XML文件文件数量太大如果压缩成ZIPZIP的尺寸也相对较大在程序请求实验名称时还需要解压性能极差解析XML本身需要损耗一定的性能如果要显示成百上千个CSV对应的实验名称那么需要对每个XML进行解析性能也很不理想在此我介绍一种方法通过预处理的方式将所需信息提取成结构化的数据结构Structured Data Structure然后通过索引进行快速定位。问题分析虽然XML文件数量比较大每个XML文件提供的信息也比较多但是我们所需要的信息仅仅就是XML文件中的实验名称因此一个思路就是首先对所有XML文件进行预处理然后提取实验名称并将其保存到另一个文件中。当需要根据CSV文件名获取实验名称时就查询这个实验名称数据文件然后显示对应的实验名称。这里的问题是使用哪种格式来产生实验名称数据文件呢我们又有几个选择使用JSON存储“CSV文件名—实验名称”的键值对这样性能也不会很好因为这样的键值对有200万个解析JSON文件本身的CPU和IO负载会很高使用桌面数据库比如SQLite这样做需要应用程序内建一个SQLite的引擎它本身存在CPU架构的问题x86,x64而且中间封了一层数据库访问操作性能也不见得特别高自定义存储结构这种做法比较灵活但是需要自己实现有一定的难度出问题的几率也相对较大综合分析我们还是打算选择第三个方案自己定义数据的存储结构。假设CSV文件名是连续的比如是从1.CSV、2.CSV一直到2000000.CSV那么我们可以将CSV的文件名数值作为索引值通过查表法找到对应的实验名称字符串即可。比如在内存中有以下字符串数组假设CSV文件名为1535.CSV那么我们只需要assayNames[1534]即可获得第1535个CSV也就是1535.CSV所对应的实验名称。这样做的效率是非常高的它直接利用了数组的索引。然而现实并不是那么美好我们不可能把200万条数据全部放在一个数组内存中这样做消耗内存会非常高原始CSV文件的文件名标号并不是连续的解决问题一的方式比较直白我们需要将数据放在磁盘中然后按需访问对于问题二我们需要引入数据库实现中的一个概念索引。解决问题假设每条实验名称数据被当成一条长度固定的记录存放在二进制文件中但由于文件名中数值标识并不连续因此无法简单地通过文件名来推断数据记录的位置也就是数组的下标值比如对于1.csv、2.csv尚有规律可寻实验名称数据记录在二进制文件中的位置就是文件名数值减1从4.csv开始后面的位置值就与文件名没什么关系了。此时我们需要有一个映射来定义文件名中的数值与数据记录位置之间的关系。为此我引入了另一个二进制文件其中定义了200万条记录每条记录仅占4个字节每条记录每4个字节保存的是以该记录的偏移值作为文件名数值的CSV文件所对应的实验名称数据记录在上述二进制文件中的记录位置。比如那么假设CSV文件的文件名为4.csv于是可以首先找到索引文件中偏移值为4也就是index3的记录位置值也就是2然后在二进制文件中定位到索引值为2的记录就是4.csv所对应的实验名称数据。代码实现我使用System.Runtime.InteropServices命名空间下的Marshal类和GCHandle类配合System.IO命名空间下的BinaryReader、BinaryWriter类来实现结构化二进制文件的读取和写入。封装代码如下public static class BinaryFileHelper{ public static T ReadStructT(BinaryReader binaryReader, int idx 0) where T : struct { var buff new byte[Marshal.SizeOfT()]; if (binaryReader.BaseStream.CanSeek) { binaryReader.BaseStream.Seek(idx * buff.Length, SeekOrigin.Begin); binaryReader.BaseStream.Read(buff, 0, buff.Length); } var gcHandle GCHandle.Alloc(buff, GCHandleType.Pinned); try { var result Marshal.PtrToStructureT(gcHandle.AddrOfPinnedObject()); return result; } finally { gcHandle.Free(); } } public static void WriteStructT(BinaryWriter binaryWriter, T item) where T : struct { var buff new byte[Marshal.SizeOfT()]; var gcHandle GCHandle.Alloc(buff, GCHandleType.Pinned); try { Marshal.StructureToPtrT(item, gcHandle.AddrOfPinnedObject(), false); binaryWriter.Write(buff, 0, buff.Length); } finally { gcHandle.Free(); } }}接下来再写一个测试程序来测试结构化二进制文件的读取性能[StructLayout(LayoutKind.Explicit)]public struct AssayNameStructuredIndex{ [FieldOffset(0)] [MarshalAs(UnmanagedType.U4, SizeConst 4)] public int Index;}[StructLayout(LayoutKind.Explicit)]public struct AssayNameStructuredRecord{ [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst 256)] public string Name;}static void Main(string[] args){ var stopwatch new Stopwatch(); using (var recordFileStream new FileStream(assayNames.bin, FileMode.Open, FileAccess.Read)) using (var indexFileStream new FileStream(assayNames.idx, FileMode.Open, FileAccess.Read)) using (var recordReader new BinaryReader(recordFileStream)) using (var indexReader new BinaryReader(indexFileStream)) { while (true) { Console.Write(请输入CSV文件名直接回车退出程序); var line Console.ReadLine(); if (string.IsNullOrEmpty(line)) break; if (!int.TryParse(Path.GetFileNameWithoutExtension(line), out var identifier)) continue; stopwatch.Restart(); var indexValue BinaryFileHelper.ReadStructAssayNameStructuredIndex(indexReader, identifier); if (indexValue.Index -1) { Console.WriteLine($数据文件中未包含{line}的记录。); Console.WriteLine(); continue; } var assayNameValue BinaryFileHelper.ReadStructAssayNameStructuredRecord(recordReader, indexValue.Index); stopwatch.Stop(); Console.WriteLine($耗时{stopwatch.ElapsedMilliseconds}毫秒实验名称{assayNameValue.Name}。); Console.WriteLine(); } }}执行结果如下可以看到无论CSV文件名中的数值是大还是小从近200万条数据中读取实验名称信息的速度都是非常快的基本上也就是零点几个毫秒达到了预期的目标。总结所谓之结构化的数据就是表示每条数据所占用的存储空间都是一致的也就是每条记录所占用的字节数是相等的这样才能非常容易地通过记录的索引值以及每条记录的大小来计算位置偏移量从而快速读取数据。这是一种空间换时间的方案一个明显的问题是需要根据实际数据来合理选择每条记录所占用的存储空间如果太大那么200多万条记录累积起来会占用大量存储空间造成空间浪费如果太小又会导致某些数据无法正确存储造成信息丢失。因此本文介绍的方案还是需要根据实际情况进行斟酌选择合理的记录存储结构。原文地址: http://sunnycoding.cn/2018/07/04/accessing-structural-binary-file-using-csharp/.NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com