在C#中创建一个随机文件

32

我正在创建一个指定大小的文件 - 我不关心里面的数据是什么,但随机数据会更好。目前我正在这样做:

        var sizeInMB = 3; // Up to many Gb
        using (FileStream stream = new FileStream(fileName, FileMode.Create))
        {
            using (BinaryWriter writer = new BinaryWriter(stream))
            {
                while (writer.BaseStream.Length <= sizeInMB * 1000000)
                {
                    writer.Write("a"); //This could be random. Also, larger strings improve performance obviously
                }
                writer.Close();
            }
        }

这不是有效的或正确的方法。有更高效的解决方案吗?

感谢所有的回答。

编辑

对以下方法进行了一些测试,针对一个2Gb的文件(时间以毫秒为单位):

方法1:Jon Skeet

byte[] data = new byte[sizeInMb * 1024 * 1024];
Random rng = new Random();
rng.NextBytes(data);
File.WriteAllBytes(fileName, data);

内存不足异常-处理2GB文件

方法二:Jon Skeet

byte[] data = new byte[8192];
Random rng = new Random();
using (FileStream stream = File.OpenWrite(fileName))
{
    for (int i = 0; i < sizeInMB * 128; i++)
    {
         rng.NextBytes(data);
         stream.Write(data, 0, data.Length);
    }
}

@1K - 45,868, 23,283, 23,346

@128K - 24,877, 20,585, 20,716

@8Kb - 30,426, 22,936, 22,936

方法3 - Hans Passant(超快但数据不随机)

using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
    fs.SetLength(sizeInMB * 1024 * 1024);
}

257、287、3、3、2、3等。

5个回答

47

好的,一个非常简单的解决方案:

byte[] data = new byte[sizeInMb * 1024 * 1024];
Random rng = new Random();
rng.NextBytes(data);
File.WriteAllBytes(fileName, data);

稍微更节省内存的版本 :)

// Note: block size must be a factor of 1MB to avoid rounding errors :)
const int blockSize = 1024 * 8;
const int blocksPerMb = (1024 * 1024) / blockSize;
byte[] data = new byte[blockSize];
Random rng = new Random();
using (FileStream stream = File.OpenWrite(fileName))
{
    // There 
    for (int i = 0; i < sizeInMb * blocksPerMb; i++)
    {
        rng.NextBytes(data);
        stream.Write(data, 0, data.Length);
    }
}

然而,如果您在非常快速的连续操作中每次创建一个新的Random实例,可能会得到重复的数据。请参阅我的关于随机性的文章获取更多信息 - 您可以通过使用System.Security.Cryptography.RandomNumberGenerator来避免这种情况......或者通过多次重复使用相同的Random实例 - 但需要注意它不是线程安全的。


我会选择128k的块大小,这在大多数I/O测试中提供了更好的性能。至少要4k,因为这是32位Windows操作系统上的页面大小。 - Ben Voigt
2
@Ben:我会尽量避免使用128K,因为它会占用大对象堆。不过我会将其增加到8K :) - Jon Skeet
有人也对这个问题进行了负评。我想这是一次负评袭击。 - Jason

17

利用NTFS内置的稀疏文件支持是最快的方法,该文件系统用于硬盘上的Windows系统。以下代码可以在几秒钟内创建一个1 GB的文件:

using System;
using System.IO;

class Program {
    static void Main(string[] args) {
        using (var fs = new FileStream(@"c:\temp\onegigabyte.bin", FileMode.Create, FileAccess.Write, FileShare.None)) {
            fs.SetLength(1024 * 1024 * 1024);
        }
    }
}

读取时,文件中只包含零。


没有,但我在源代码上运行了反射器。在FileStream中没有任何DeviceIoControl(FSCTL_SET_SPARSE)的迹象。你确定“在一秒钟内”不是写缓存在起作用吗? - Ben Voigt
现在我已经尝试过了...文件属性显示“磁盘上的大小” == “总大小”,这对于稀疏文件来说是不可能的。 - Ben Voigt
将其设置为大于文件系统缓存的大小。 - Hans Passant
@Hans:我刚刚使用你的代码加上一些明显的更改创建了一个26GB的文件。我的驱动器上的可用空间立即减少了26GB。空簇可能不需要在缓存中占用空间,但它们确实被分配在卷位图中。使用稀疏文件就不会有这种情况。 - Ben Voigt
一个文件系统应该考虑“保留”空间,这是明智的。但这并不是这个问题的重点,“花了多长时间”才是关键。 - Hans Passant
显示剩余2条评论

0
你可以使用我创建的以下类来生成随机字符串。
using System;
using System.Text;

public class RandomStringGenerator
{
    readonly Random random;

    public RandomStringGenerator()
    {
        random = new Random();
    }
    public string Generate(int length)
    {
        if (length < 0)
        {
            throw new ArgumentOutOfRangeException("length");
        }
        var stringBuilder = new StringBuilder();

        for (int i = 0; i < length; i++)
        {
            char ch = (char)random.Next(0,255 );
            stringBuilder.Append(ch);
        }

        return stringBuilder.ToString();

    }

}

用于使用

 int length = 10;
        string randomString = randomStringGenerator.Generate(length);

-1 这也会非常慢,因为它只适用于内存中的字符串,并且不针对 OP 直接将数据写入文件的情况进行优化。没有必要使用 chars(它们比 bytes 大两倍),也不需要在内存中保留整个字节字符串。 - cdhowie
+1 表示补偿踩票。虽然解决方案不是最佳的,但总比没有好,所以踩票是不合理的。 - Nicolas Raoul

0

创建大文件的高效方法:

    FileStream fs = new FileStream(@"C:\temp\out.dat", FileMode.Create);
    fs.Seek(1024 * 6, SeekOrigin.Begin);
    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    fs.Write(encoding.GetBytes("test"), 0, 4);
    fs.Close();

然而,这个文件将是空的(除了结尾处的“test”)。不清楚你究竟想做什么——是带有数据的大文件,还是只是一个大文件。你也可以修改它,使其在文件中稀疏地写入一些数据,但不要完全填满它。如果你确实想要整个文件都填满随机数据,那么我能想到的唯一方法就是使用上面Jon提供的随机字节。


-1
一个改进的方法是用所需大小填充缓冲区中的数据,然后一次性刷新所有数据。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接