在2个文件的字节数组中求交集和并集
本文关键字:数组 文件 2个 字节 字节数 | 更新日期: 2023-09-27 18:24:44
我有两个文件。1是源文件,2是目标文件。
下面是我使用字节数组的交集和并集两个文件的代码。
FileStream frsrc = new FileStream("Src.bin", FileMode.Open);
FileStream frdes = new FileStream("Des.bin", FileMode.Open);
int length = 24; // get file length
byte[] src = new byte[length];
byte[] des = new byte[length]; // create buffer
int Counter = 0; // actual number of bytes read
int subcount = 0;
while (frsrc.Read(src, 0, length) > 0)
{
try
{
Counter = 0;
frdes.Position = subcount * length;
while (frdes.Read(des, 0, length) > 0)
{
var data = src.Intersect(des);
var data1 = src.Union(des);
Counter++;
}
subcount++;
Console.WriteLine(subcount.ToString());
}
}
catch (Exception ex)
{
}
}
它以最快的速度运行良好。但现在的问题是,我想要计算它,当我使用下面的代码时,它会变得非常慢。
var data = src.Intersect(des).Count();
var data1 = src.Union(des).Count();
那么,有什么解决方案吗?如果是,请尽快告诉我。感谢
Intersect
和Union
不是最快的操作。你看到它很快的原因是你从来没有真正列举过结果!
两者都返回可枚举值,而不是操作的实际结果。你应该遍历并枚举可枚举的,否则什么都不会发生——这被称为"延迟执行"。现在,当你执行Count
时,你实际上枚举了可枚举的,并产生了Intersect
和Union
的全部成本——相信我,Count
本身相对来说是微不足道的(尽管仍然是一个O(n)运算!)。
你很可能需要制定自己的方法。您希望避免可枚举的开销,更重要的是,您可能需要一个查找表。
几点:注释// get file length
是误导性的,因为它是缓冲区大小。Counter
不是读取的字节数,而是读取的块数。data
和data1
将以最后一个块读取的结果结束,忽略它们之前的任何数据。这是假设while循环中没有出现任何错误——您需要删除try结构来查看是否存在任何错误。
您可以做的是计算每个文件中每个字节的出现次数,如果任何文件中的一个字节计数大于1,则它是文件交叉点的成员,如果所有中的字节计数大于一,则它就是文件并集的成员。
为两个以上的文件编写代码和为两个文件编写代码一样容易,而LINQ对两个文件来说很容易,但对两个以上文件来说有点麻烦。(我将其与以天真的方式使用LINQ进行了比较,最后只使用了两个文件。)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var file1 = @"C:'Program Files (x86)'Electronic Arts'Crysis 3'Bin32'Crysis3.exe"; // 26MB
var file2 = @"C:'Program Files (x86)'Electronic Arts'Crysis 3'Bin32'd3dcompiler_46.dll"; // 3MB
List<string> files = new List<string> { file1, file2 };
var sw = System.Diagnostics.Stopwatch.StartNew();
// Prepare array of counters for the bytes
var nFiles = files.Count;
int[][] count = new int[nFiles][];
for (int i = 0; i < nFiles; i++)
{
count[i] = new int[256];
}
// Get the counts of bytes in each file
int bufLen = 32768;
byte[] buffer = new byte[bufLen];
int bytesRead;
for (int fileNum = 0; fileNum < nFiles; fileNum++)
{
using (var sr = new FileStream(files[fileNum], FileMode.Open, FileAccess.Read))
{
bytesRead = bufLen;
while (bytesRead > 0)
{
bytesRead = sr.Read(buffer, 0, bufLen);
for (int i = 0; i < bytesRead; i++)
{
count[fileNum][buffer[i]]++;
}
}
}
}
// Find which bytes are in any of the files or in all the files
var inAny = new List<byte>(); // union
var inAll = new List<byte>(); // intersect
for (int i = 0; i < 256; i++)
{
Boolean all = true;
for (int fileNum = 0; fileNum < nFiles; fileNum++)
{
if (count[fileNum][i] > 0)
{
if (!inAny.Contains((byte)i)) // avoid adding same value more than once
{
inAny.Add((byte)i);
}
}
else
{
all = false;
}
};
if (all)
{
inAll.Add((byte)i);
};
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
// Display the results
Console.WriteLine("Union: " + string.Join(",", inAny.Select(x => x.ToString("X2"))));
Console.WriteLine();
Console.WriteLine("Intersect: " + string.Join(",", inAll.Select(x => x.ToString("X2"))));
Console.WriteLine();
// Compare to using LINQ.
// N/B. Will need adjustments for more than two files.
var srcBytes1 = File.ReadAllBytes(file1);
var srcBytes2 = File.ReadAllBytes(file2);
sw.Restart();
var intersect = srcBytes1.Intersect(srcBytes2).ToArray().OrderBy(x => x);
var union = srcBytes1.Union(srcBytes2).ToArray().OrderBy(x => x);
Console.WriteLine(sw.ElapsedMilliseconds);
Console.WriteLine("Union: " + String.Join(",", union.Select(x => x.ToString("X2"))));
Console.WriteLine();
Console.WriteLine("Intersect: " + String.Join(",", intersect.Select(x => x.ToString("X2"))));
Console.ReadLine();
}
}
}
计数字节出现次数的方法比我电脑上的LINQ方法快大约五倍,即使后者没有加载文件和一定范围的文件大小(几KB到几MB)。