出于分析目的写入和读取大文件

Writing and Reading a big file for analytical purposes

我正在尝试制作一个 DNA 分析工具,但我遇到了一个大问题。

这是应用程序的屏幕截图。

我面临的问题是处理大数据。我使用过流和内存映射文件,但我不确定我的方向是否正确。 我想要实现的是能够编写一个包含 30 亿个随机字母的文本文件,然后将该文本文件用于以后的目的。 目前我有 3000 个字母,但生成更多字母需要很长时间。你会如何解决这个问题?将全文文件存储到字符串中对我来说似乎超载了。有什么想法吗?

    private void WriteDNASequence(string dnaFile)
    {
        Dictionary<int, char> neucleotides = new Dictionary<int, char>();
        neucleotides.Add(0, 'A');
        neucleotides.Add(1, 'T');
        neucleotides.Add(2, 'C');
        neucleotides.Add(3, 'G');

        int BasePairs = 3000;

        using (StreamWriter sw = new StreamWriter(filepath + @"\" + dnaFile))
        {
            for (int i = 0; i < (BasePairs / 2); i++)
            {
                int neucleotide = RandomNumber(0, 4);
                sw.Write(neucleotides[neucleotide]);
            }
        }
    }

    private string ReadDNASequence(string dnaFile)
    {
        _DNAData = "";
        using (StreamReader file = new StreamReader(filepath + @"\" + dnaFile))
        {
            _DNAData = file.ReadToEnd();
        }
        return _DNAData;
    }
    //Function to get a random number 
    private static readonly Random random = new Random();
    private static readonly object syncLock = new object();
    public static int RandomNumber(int min, int max)
    {
        lock (syncLock)
        { // synchronize
            return random.Next(min, max);
        }
    }

处理如此大量的数据时,每一位都很重要,您必须尽可能密集地打包数据。

截至目前,每个核苷酸由一个字符表示,您使用的编码中的一个字符(默认情况下为 UTF-8)占用 1 个字节(对于您使用的那 4 个字符)。

但是因为你只有 4 个不同的字符 - 每个字符只包含 2 位信息,所以我们可以将它们表示为:

00 - A
01 - T
10 - C
11 - G

这意味着我们可以在一个字节中打包 4 个核苷酸,使输出文件大小缩小 4 倍。

假设你有这样的地图:

static readonly Dictionary<char, byte> _neucleotides = new Dictionary<char, byte> { 
{ 'A', 0},
{ 'T', 1},
{ 'C', 2},
{ 'G', 3}
};
static readonly Dictionary<int, char> _reverseNucleotides = new Dictionary<int, char> {
    {0, 'A'},
    {1, 'T'},
    {2, 'C'},
    {3, 'G'}
};

你可以像这样在一个字节中打包 4 个核苷酸:

string toPack = "ATCG";
byte packed = 0;
for (int i = 0; i < 4; i++) {
    packed = (byte) (packed | _neucleotides[toPack[i]] << (i * 2));
}

然后像这样打开包装:

string unpacked = new string(new[] {
    _reverseNucleotides[packed & 0b11],
    _reverseNucleotides[(packed & 0b1100) >> 2],
    _reverseNucleotides[(packed & 0b110000) >> 4],
    _reverseNucleotides[(packed & 0b11000000) >> 6],
});

至于将字节写入文件,我认为这很容易。如果在这种情况下需要一些随机数据,请使用:

int chunkSize = 1024 * 1024; // 8 million pairs at once (since each byte is 4 nucleotides)
byte[] chunk = new byte[chunkSize];
random.NextBytes(chunk);
// fileStream is instance of `FileStream`, no need for `StreamWriter`
fileStream.Write(chunk, 0, chunk.Length);

有一些注意事项(比如文件中的最后一个字节可能存储的不是 4 个核苷酸,而是更少),但我希望你能自己弄明白。

使用这种方法(以二进制打包,一次生成大随机块,将大块写入文件)- 在我非常旧(7 年)的硬盘上生成 30 亿对花费了 8 秒,输出大小为 350MB。如有必要,您甚至可以一次将所有 350MB 读入内存。