随机化真正巨大的文本文件的行

时间:2013-11-20 09:09:33

标签: c# performance memory file-io

我想将文件中的行随机化,该文件包含超过3200万行10位数字符串。我知道如何使用File.ReadAllLines(...).OrderBy(s => random.Next()).ToArray(),但这不是内存效率,因为它会将所有内容加载到内存(超过1.4GB),并且只适用于x64架构。

替代方法是拆分它并随机化较短的文件然后合并它们但我想知道是否有更好的方法来做到这一点。

2 个答案:

答案 0 :(得分:3)

您当前的方法将分配至少2个大型字符串数组(可能更多 - 我不知道OrderBy是如何实现的,但它可能会自己进行分配)。

如果通过在行之间进行随机排列(例如使用Fisher-Yates shuffle)来“就地”随机化数据,它将最小化内存使用量。当然,如果文件很大,它仍然会很大,但你不会分配超过必要的内存。


编辑:如果所有行都具有相同的长度(*),则意味着您可以随机访问文件中的给定行,因此您可以直接在文件中执行Fisher-Yates shuffle。

(*)并假设您没有使用字符可以具有不同字节长度的编码,例如UTF-8

答案 1 :(得分:1)

此应用程序使用字节数组

演示您想要的内容
  1. 它创建一个填充数字为0到32000000的文件。
  2. 加载文件,然后使用块复制Fisher-Yates方法将它们在内存中混洗。
  3. 最后,它以随机顺序将文件写回来
  4. 峰值内存使用量约为400 MB。在我的机器上运行大约20秒(主要是文件IO)。

    public class Program
    {
        private static Random random = new Random();
    
        public static void Main(string[] args)
        {
            // create massive file
            var random = new Random();
            const int lineCount = 32000000;
    
            var file = File.CreateText("BigFile.txt");
    
            for (var i = 0; i < lineCount ; i++)
            {
                file.WriteLine("{0}",i.ToString("D10"));
            }
    
            file.Close();
    
            int sizeOfRecord = 12;
    
            var loadedLines = File.ReadAllBytes("BigFile.txt");
    
            ShuffleByteArray(loadedLines, lineCount, sizeOfRecord);
    
            File.WriteAllBytes("BigFile2.txt", loadedLines);
        }
    
        private static void ShuffleByteArray(byte[] byteArray, int lineCount, int sizeOfRecord)
        {
            var temp = new byte[sizeOfRecord];
    
            for (int i = lineCount - 1; i > 0; i--)
            {
                int j = random.Next(0, i + 1);
                // copy i to temp
                Buffer.BlockCopy(byteArray, sizeOfRecord * i, temp, 0, sizeOfRecord);
                // copy j to i
                Buffer.BlockCopy(byteArray, sizeOfRecord * j, byteArray, sizeOfRecord * i, sizeOfRecord);
                // copy temp to j
                Buffer.BlockCopy(temp, 0, byteArray, sizeOfRecord * j, sizeOfRecord);
            }
        }
    }