我有一段代码需要能够在文件末尾修改几个字节。问题是文件很大。高达100+ Gb。
我需要尽可能快地进行操作,但是经过几个小时的Googeling后,看起来.Net在这里相当有限了吗?
我大部分时间都在尝试使用System.IO.FileStream并且不知道其他任何方法。一个“反向”文件流可以做,但我知道如何创建一个(从结束而不是从头开始写)。
以下是我的工作:(注意:关闭流时花费的时间)
static void Main(string[] args)
{
//Simulate a large file
int size = 1000 * 1024 * 1024;
string filename = "blah.dat";
FileStream fs = new FileStream(filename, FileMode.Create);
fs.SetLength(size);
fs.Close();
//Modify the last byte
fs = new FileStream(filename, FileMode.Open);
//If I don't seek, the modification happens instantly
fs.Seek(-1, SeekOrigin.End);
fs.WriteByte(255);
//Now, since I am modifying the last byte,
//this last step is very slow
fs.Close();
}
}
答案 0 :(得分:11)
就像Darin已经注意到的那样,这是一个大型文件“模拟”的工件。
延迟是实际上“填满”文件,延迟只在第一次发生。如果您将部分从//Modify the last byte
重复到fs.Close();
,则会非常快。
答案 1 :(得分:4)
我进行了一些测试,结果有点令人困惑。如果您创建文件并在同一程序中修改它,则速度很慢:
static void Main(string[] args)
{
//Simulate a large file
int size = 100 * 1024 * 1024;
string filename = "blah.datn";
using (var fs = new FileStream(filename, FileMode.Create))
{
fs.SetLength(size);
}
using (var fs = new FileStream(filename, FileMode.Open))
{
fs.Seek(-1, SeekOrigin.End);
fs.WriteByte(255);
}
}
但是如果文件存在而您只是尝试修改最后一个字节,那么它很快:
static void Main(string[] args)
{
string filename = "blah.datn";
using (var fs = new FileStream(filename, FileMode.Open))
{
fs.Seek(-1, SeekOrigin.End);
fs.WriteByte(255);
}
}
击> <击>嗯... 击>
更新:
请忽略我之前的观察结果,并取消标记为答案,因为它完全错误。
进一步调查这个问题我注意到以下模式。假设您分配给定大小的文件,其零字节如下:
using (var stream = File.OpenWrite("blah.dat"))
{
stream.SetLength(100 * 1024 * 1024);
}
此操作非常快,它会创建一个填充零的100MB文件。
现在,如果在某个其他程序中尝试修改最后一个字节,关闭流将会很慢:
using (var stream = File.OpenWrite("blah.dat"))
{
stream.Seek(-1, SeekOrigin.End);
stream.WriteByte(255);
}
我不知道文件系统的内部工作方式或者这个文件是如何创建的,但我觉得它没有完全初始化,直到你尝试修改它并关闭句柄会很慢。
为了确认这一点,我在非托管代码中进行了测试(随意修复任何异常,因为我的C非常生锈):
void main()
{
int size = 100 * 1024 * 1024 - 1;
FILE *handle = fopen("blah.dat", "wb");
if (handle != NULL) {
fseek(handle, size, SEEK_SET);
char buffer[] = {0};
fwrite(buffer, 1, 1, handle);
fclose(handle);
}
}
这与.NET =&gt;中的行为相同它分配一个100MB的文件,用零填充,速度非常快。
现在,当我尝试修改此文件的最后一个字节时:
void main()
{
int size = 100 * 1024 * 1024 - 1;
FILE *handle = fopen("blah.datn", "rb+");
if (handle != NULL) {
fseek(handle, -1, SEEK_END);
char buffer[] = {255};
fwrite(buffer, 1, 1, handle);
fclose(handle);
}
}
最后fclose(handle)
很慢。我希望有些专家能为此带来一些启示。
似乎使用以前的方法修改真实文件的最后一个字节(非稀疏)非常快。
答案 2 :(得分:3)
使用MemoryMappedFile时使用大文件的最快方法。内存映射文件是一个映射(未加载)到虚拟内存中的文件,因此您可以访问其中的随机字节,而无需寻找特定位置,加载缓冲区等。您还可以直接从文件中读取整个结构而无需进行反序列化。
以下代码直接来自MSDN,在512MB文件的中间加载并存储MyColor结构:
static void Main(string[] args)
{
long offset = 0x10000000; // 256 megabytes
long length = 0x20000000; // 512 megabytes
// Create a memory-mapped view of a portion of
// an extremely large image, from the 256th megabyte (the offset)
// to the 768th megabyte (the offset plus length).
using (var mmf =
MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data",
FileMode.Open,"ImgA"))
{
using (var accessor = mmf.CreateViewAccessor(offset, length))
{
int colorSize = Marshal.SizeOf(typeof(MyColor));
MyColor color;
// Make changes to the view.
for (long i = 0; i < length; i += colorSize)
{
accessor.Read(i, out color);
color.Brighten(10);
accessor.Write(i, ref color);
}
}
}
}
public struct MyColor
{
public short Red;
public short Green;
public short Blue;
public short Alpha;
// Make the view brigher.
public void Brighten(short value)
{
Red = (short)Math.Min(short.MaxValue, (int)Red + value);
Green = (short)Math.Min(short.MaxValue, (int)Green + value);
Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
}
}
找到更多信息和示例
答案 3 :(得分:2)
我建议您使用真实文件而不是“模拟”文件进行尝试。 可能是.net正在使用一些稀疏分配机制,并且只将文件写出到实际写入的最后一个字节。
因此,当你写入文件的开头时,它只需要写出几个字节,但是当你写到文件的末尾时,它实际上必须写出整个文件。