出于分析目的写入和读取大文件
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 读入内存。
我正在尝试制作一个 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 读入内存。