为分析目的编写和阅读大文件

时间:2018-03-27 12:40:43

标签: c# file memory stream dna-sequence

我试图制作DNA分析工具,但我在这里面临一个大问题。

以下是有关应用程序外观的截图。enter image description here

我面临的问题是处理大数据。我已经使用了流和内存映射文件,但我不确定我是否朝着正确的方向前进。 我想要实现的是能够编写一个包含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);
        }
    }

1 个答案:

答案 0 :(得分:3)

当处理如此大量的数据时 - 每一点都很重要,你必须尽可能密集数据。

截至目前,每个核苷酸由一个char表示,并且您使用的编码中的一个char(默认为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年)HDD上生成30亿对花了8秒,输出大小为350MB。如有必要,您甚至可以一次性将所有350MB的内容读入内存。