C# 随机排列真正巨大的文本文件行

C# 随机排列真正巨大的文本文件行,c#,performance,memory,file-io,C#,Performance,Memory,File Io,我想随机化一个文件中有超过3200万行10位字符串的行。我知道如何使用File.ReadAllLines(…).OrderBy(s=>random.Next()).ToArray(),但这不是内存效率,因为它会将所有内容加载到内存中(超过1.4GB),并且仅适用于x64体系结构 另一种方法是将其拆分并随机分配较短的文件,然后合并它们,但我想知道是否有更好的方法来做到这一点。您当前的方法将分配至少2个大字符串数组(可能更多——我不知道OrderBy是如何实现的,但它可能自己分配) 如果通过在行之间

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


另一种方法是将其拆分并随机分配较短的文件,然后合并它们,但我想知道是否有更好的方法来做到这一点。

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

如果通过在行之间进行随机排列(例如使用),将数据“就地”随机化,将最大限度地减少内存使用。当然,如果文件很大,它仍然会很大,但是您不会分配超出需要的内存


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


(*)并且假设您没有使用字符可以具有不同字节长度的编码,如UTF-8,此应用程序使用字节数组演示您想要的内容

  • 它创建一个填充数字为0到32000000的文件
  • 它加载文件,然后使用块复制Fisher-Yates方法在内存中洗牌
  • 最后,它以无序顺序将文件写回
  • 峰值内存使用量约为400 MB。在我的机器上运行大约20秒(主要是文件IO)

    公共类程序
    {
    私有静态随机=新随机();
    公共静态void Main(字符串[]args)
    {
    //创建大量文件
    var random=新的random();
    const int lineCount=32000000;
    var file=file.CreateText(“BigFile.txt”);
    对于(变量i=0;i0;i--)
    {
    int j=随机。下一个(0,i+1);
    //将i复制到temp
    Buffer.BlockCopy(byteArray,sizeOfRecord*i,temp,0,sizeOfRecord);
    //将j复制到i
    Buffer.BlockCopy(byteArray,sizeOfRecord*j,byteArray,sizeOfRecord*i,sizeOfRecord);
    //将温度复制到j
    Buffer.BlockCopy(temp,0,byteArray,sizeOfRecord*j,sizeOfRecord);
    }
    }
    }
    
    所有行的长度是否相同?这将使它变得更容易…如果你只是在没有OrderBy的情况下执行“ReadAllLines”,会占用多少内存?这已经太多了吗?@ThomasLevesque yes所有行的大小都相同。@Baldrick yes无论OrderBy
    如何,都会发生这种情况。OrderBy(s=>random.Next())
    不保证终止。
    OrderBy
    子句中的函数应该提供总排序。看,为了做费舍尔·耶茨的洗牌,我不需要加载内存中的所有行,然后进行原地洗牌吗?或者,这可以在不加载文件的情况下完成吗memory@idipous,是的,您仍然需要加载内存中的所有行。这并不理想,但最好在加载时加载2到3次。@idipous,是的,对不起,我修复了它。是的,它只有相同长度的数字(例如,一个文件可以有32M 10位数字,另一个与第一个文件无关的文件可以有3M 9位数字)。我会尝试一下,让你知道。虽然你是对的,峰值内存约400米的输出文件是不正确的。。。我得到一个文件,里面有这样的东西47 30090968697002 3064902714 34101 30751697421430@idipous:嗯。。为我获得完美的输出。您是否使用我的精确样本看到该问题,或将其更改为与您的文件一起使用?请记住,如果行长为10个字符,则记录的大小应为12。您需要为行终止符增加2个字符。如果您也运行Mono,则可能会有所不同。注意:只是修复了临时字节数组声明中的错误-使其太大(不应影响您的输出,但值得注意),无法获取我所做的sizeofrecord
    sizeofrecord=System.Text.AscienceOding.ASCII.GetByteCount(行)其中行来自文件读取,所有行大小相同,但编号不同,所以我只选择最后一行。如果获取长度的“行”是从“ReadLines”获取的字符串,那么它将不包括行终止符。这不包括在内。你可能需要在这个数字上加2。
    
    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);
            }
        }
    }