DNA文件转换的位操作

时间:2014-05-02 22:51:31

标签: java scala stream bit-manipulation bit-shift

我正在Scala中构建一个程序,它将每个字符串8位的txt文件中存储的DNA数据转换为每个字符使用2位的文件。 DNA使用的唯一Chars是T,C,A,G。我想为每个char使用2位,其中T = 00,C = 01,A = 10,G = 11.我希望它尽可能紧凑,没有任何浪费的位。

现在,我每个Char写出8位而不是我想要使用的两位。对于outPut方法,你能建议我可以做的任何位操作来最大化空间并且每个字符只使用两位吗?

3 个答案:

答案 0 :(得分:1)

如果你只能一次写出整个字节,听起来像我一样,你必须读取4个字母组的字母,并使用一些位移,以便能够使用每个字节的所有8位。如果字母数不是4的倍数,这可能会导致一些问题,因为你无法区分填充和正常字母......

(抱歉,我不知道Scala,但这个算法应该独立于语言工作)

类似

// Store byte equivalents of ATCG in variables, or use an enum
// create new byte[4]
// Fill array with next 4 chars, ideally using something like Java's InputStream.read(byte[] b) so you can read in groups
// create temp byte variable
for (int i = 3; i >= 0; i--) {
    switch(b) {
        case <byte equivalent of A>:
            temp += <binary equivalent of A> << 3 - i;
// Repeat for other letters
// Write out temp

答案 1 :(得分:1)

一次输出少于一个字节是不可能的。在写出char之前,你必须用2位dna字母构建一个8位字符。

我不知道Scala或Java,但是在你的代码和更像C的东西的组合中它会是这样的:

  def outPut(in: DataInputStream, out: DataOutputStream) {
    var result = getChars(in)

    int letter_count = 0       // whatever a plain old integer is called
    char byte = 0

    for(i <- 0 until result.length) {

      if (result(i) == "A") {
        byte = (byte << 2) | 2         // move byte over 2 bits and "or" in new bits
      }else if(result(i) == "T") {
        byte = (byte << 2) | 0         // the "| 0" part here actually does nothing
      }else if(result(i) == "C") {
        byte = (byte << 2) | 1
      }else {
        byte = (byte << 2) | 3
      }

      letter_count += 1

      if (letter_count == 4) {
        out.writeByte(byte)
        letter_count = 0
        byte = 0
      }

   }
  }

请注意user3580294的答案。

进入另一个方向(从2位编码到字符编码):

  def toLetter(x) {
    if (x == 0)
      return "T"
    else if (x == 1)
      return "C"
    else if (x == 2)
      return "A"
    else if (x == 3)
      return "G"
  }

  def outputLetters(in: DataInputStream, out: DataOutputStream) {
    var twobit = getChars(in)   // or however you read the file

    for(i <- 0 until twobit.length) {
      byte = twobit(i)
      out.writeByte(toLetter((byte >> 6) & 3))
      out.writeByte(toLetter((byte >> 4) & 3))
      out.writeByte(toLetter((byte >> 2) & 3))
      out.writeByte(toLetter( byte       & 3))
    }
  }

这基本上假设字母的数量可以被4整除。为了克服这个限制,你需要在文件中存储额外的信息。它可以是最后一个字节(1到4)中的字母数,也可以是文件中表示的字母总数(可以计算最后一个字节中的字母数)。

答案 2 :(得分:1)

更多scala-ish。将基数分为四个(可能少于最后一个)并将每个最多四个字符的序列映射到适当的值。

def makeBits(base:Char):Int = {
        base match {
        case 'T' => 0
        case 'C' => 1
        case 'A' => 2
        case 'G' => 3
        case _ =>   -1 // some error here
    }
}  

def packBits(bases:String):Int = {
    var res:Int = 0
    for (bits <- bases) { res = (res << 2) + makeBits(bits)}
    res
}                           
val packed = "ATGCTTTADGCA".grouped(4).map(packBits)