如何计算.txt文件中出现一串字母的频率? (在C ++中)

时间:2017-08-18 11:48:04

标签: c++ sorting count

我搜索了如何计算经常在.txt文件中出现一串字母并查找(以及其他)此帖子的方式:Count the number of times each word occurs in a file

通过计算单词(用空格分隔)来处理问题。

但是,我需要做一些略有不同的事情: 我有.txt file包含数十亿字母而没有任何格式(没有空格,没有任何空格,没有换行符,没有硬回复等),只是字母a的loooooong线, g,t和c(即:DNA序列;))。

现在我想编写一个贯穿整个序列的程序,并计算每个可能的9个字母连续序列出现在该文件中的频率。

是的,有4^9种可能的9个字母'字组合组合。由字符A,G,T和C组成,但我只想输出前1000名。

由于没有空格或任何东西,我必须逐个查看文件并检查所有9个字母的单词'出现,即:

ATAGAGCTAGATCCCTAGCTAGTGACTA

包含序列:

ATAGAGCTA, TAGAGCTAG, AGAGCTAGA, etc.

我希望你知道我的意思,我很难用英语描述它,因为它不是我的母语。

致以最诚挚的问候,并提前感谢大家!

2 个答案:

答案 0 :(得分:2)

与数十亿相比,2 ^ 18或256k似乎突然变小。好消息是,这意味着您的直方图可以存储在大约1 MB的数据中。一个简单的方法是将每个字母转换为2位表示,假设您的文件只包含AGCT,而没有RYMK ......短号和通配符。

这就是''esquisse'所做的。它将9个字节的文本打包成18位值,并递增相应的直方图bin。为了加速[转换一点,它读取4个字节并使用查找表一次转换4个字形。

我不知道这会有多快,但它应该是合理的。我没有测试它,但我知道它编译,至少在gcc下。没有打印输出,但有一个辅助函数可以将序列打包二进制格式解压缩回文本。

它应该至少给你一个好的起点

#include <vector>
#include <array>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <exception>

namespace dna {

    // helpers to convert nucleotides to packed binary form
    enum Nucleotide : uint8_t { A, G, C, T };

    uint8_t simple_lut[4][256] = {};

    void init_simple_lut()
    {
        for (size_t i = 0 ; i < 4; ++i)
        {
            simple_lut[i]['A'] = A << (i * 2);
            simple_lut[i]['C'] = C << (i * 2);
            simple_lut[i]['G'] = G << (i * 2);
            simple_lut[i]['T'] = T << (i * 2);
        }
    }


    uint32_t pack4(const char(&seq)[4])  
    {
        return simple_lut[0][seq[0]] 
             + simple_lut[1][seq[1]]
             + simple_lut[2][seq[2]]
             + simple_lut[3][seq[3]];
    }

    // you can use this to convert the historghrtam 
    // index back to text.
    std::string hist_index2string(uint32_t n)
    {
        std::string result;
        result.reserve(9);
        for (size_t i = 0; i < 9; ++i, n >>= 2)
        {
            switch (n & 0x03)
            {
            case A: result.insert(result.begin(), 'A'); break;
            case C: result.insert(result.begin(), 'C'); break;
            case G: result.insert(result.begin(), 'G'); break;
            case T: result.insert(result.begin(), 'T'); break;
            default:
                throw std::runtime_error{ "totally unexpected error while unpacking index !!" };
            }
        }
        return result;
    }
}

int main(int argc, const char**argv, const char**)
{
    if (argc < 2)
    {
        std::cerr << "Usage: prog_name <input_file> <output_file>\n";
        return 3;
    }

    using dna::pack4;
    dna::init_simple_lut();
    std::vector<uint32_t> result;

    try
    {
        result.resize(1 << 18);
        std::ifstream ifs(argv[1]);

        // read 4 bytes at a time, convert to packed bits representation
        // then rotate in bits 2 by 2 in our 18 bits buffer.
        // increment coresponding bin by 1
        const uint32_t MASK{ (1 << 19) - 1 };
        const std::streamsize RB{ 4 };
        uint32_t accu{};
        uint32_t packed{};

        // we need to load at least 9 bytes to 'prime' the engine
        char buffer[4];
        ifs.read(buffer, RB);
        accu = pack4(buffer) << 8;
        ifs.read(buffer, RB);
        accu |= pack4(buffer);
        if (ifs.gcount() != RB)
        {
            throw std::runtime_error{ " input file is too short "};
        }

        ifs.read(buffer, RB);
        while (ifs.gcount() != 0)
        {
            packed = pack4(buffer);

            for (size_t i = 0; i < (size_t)ifs.gcount(); ++i)
            {
                accu <<= 2;
                accu |= packed & 0x03;
                packed >>= 2;

                accu &= MASK;
                ++result[accu];
            }
            ifs.read(buffer.pch, RB);
        }
        ifs.close();

        // histogram is compiled. store data to another file...
        // you can crate a secondary table of { index and count } 
        // it's only 5MB long and use partial_sort to extract the first 
        // 1000.
    }
    catch(std::exception& e)
    {
        std::cerr << "Error \'" << e.what() <<  "\' while reading file.\n";
        return 3;
    }

   return 0;
}

该算法可以适用于在多个线程上运行,方法是使用正确的共享配置打开多个流中的文件,并在文件的位上运行循环。在过程结束时必须小心处理16字节的接缝。

如果并行运行,内部循环非常短,最好为每个线程提供自己的直方图并在最后合并结果,否则,锁定开销会使事情变得相当缓慢。 / p>

[编辑]傻傻的我把打包的二进制查找错了。

[EDIT2]用更快的版本替换了打包的lut。

答案 1 :(得分:1)

这对你有用,

-- Beginning of script

DECLARE @codes TABLE(code NVARCHAR(8) PRIMARY KEY NOT NULL)
INSERT @codes VALUES ('ABC'), ('FEGHJ')

-- Many more instructions
-- .....