用于迭代FASTA文件并返回结构的代码设计应该是什么?

时间:2018-03-13 12:13:26

标签: file iterator rust fasta

我正在研究FASTA文件。 FASTA文件在生物学中用于存储序列。

  

> sequence1标识符(字符串)

     

一条或多条线上的序列(一个字符串)

     

...

     

>最后一个序列标识符(一个字符串)

     

一条或多条线上的序列(一个字符串)

我想创建一个在读取文件时返回结构的迭代器:

struct fasta_seq {
    identifier: String,
    sequence: String,
}

在Python中,它很容易。这段代码返回一个元组,但想法是相同的

def get_seq_one_by_one(file_):
    """Generator return prompt sequence for each sequence"""

   sequence = ''
   prompt = ''

   for line in file_:

       if line.startswith('>'):

           if sequence:
               yield (prompt, sequence)

           sequence = ''
           prompt = line.strip()[1:]

       else:
            sequence += line.strip()

   yield (prompt, sequence)

这很方便,并且允许我制作更清晰的代码,因为我可以使用简单的for循环遍历文件。

for identifier, sequence in get_seq_one_by_one(open_file):
    do

我发现了类似的主题:

如果我理解正确,他们知道要读取的缓冲区的大小。 在我的情况下,我不知道它,因为标识符和/或序列长度可能会改变。

我已经检查并使用Rust的yield似乎不是一个好主意,因为它被描述为不稳定。

我不希望你在我的位置编写代码,我试图通过重写我在Rust中用Python完成的脚本来学习。我不知道在这里用什么来回答我的问题。

如果你能指出如何实现这一目标的总体思路,那将是非常好的。如果不需要不安全的代码,它会更好。

2 个答案:

答案 0 :(得分:0)

正如评论中所说,更好的是使用an existing crate。如果您希望绝对编写自己的代码,则必须尝试以下内容:

use std::io::Read;
use std::fs::File;

struct FastaSeq {
    identifier: String,
    sequence: String,
}

fn main() {
    fn process_file(file_name: &str) -> Result<Vec<FastaSeq>, std::io::Error> {
        let mut lines = String::new();
        File::open(file_name)?.read_to_string(&mut lines)?;
        let mut ret = Vec::new();
        let mut lines = lines.split('\n');

        // I assume that the first line begin with '>'
        while let Some(line) = lines.by_ref().next() {
            ret.push(FastaSeq {
                identifier: line.into(),
                sequence: lines
                    .by_ref()
                    .take_while(|s| s.chars().next().map(|c| c != '>').unwrap_or(false))
                    .collect(),
            });
        }
        Ok(ret)
    }

    if let Err(e) = process_file("your file") {
        println!("An error occured: {}", e);
        std::process::exit(1);
    }
}

答案 1 :(得分:0)

我设法得到了一些工作。它显然是一个初学者级别的实现,但它的工作原理。

struct FastaSeq {
    identifier: String,
    sequence: String,
}

// come from: https://docs.rs/bio/0.17.0/src/bio/io/fasta.rs.html#7-1013
struct FastaReader<R: io::Read> {
    reader: io::BufReader<R>,
}

// come from: https://docs.rs/bio/0.17.0/src/bio/io/fasta.rs.html#7-1013
impl<R: io::Read> FastaReader<R> {
    /// Create a new Fasta reader given an instance of `io::Read`.
    pub fn new(reader: R) -> Self {
        FastaReader {
            reader: io::BufReader::new(reader),
        }
    }
}

impl<B: BufRead> Iterator for FastaReader<B> {
    type Item = Result<FastaSeq, io::Error>;

    fn next(&mut self) -> Option<Result<FastaSeq, io::Error>> {
        let mut this_string = String::new();
        let mut buf = vec![];
        match self.reader.read_until(b'>', &mut buf) {
            Ok(0) => None,

            Ok(my_buf) => {
                this_string = from_utf8(&buf).unwrap().to_string();
                if this_string == ">" {
                    self.reader.read_until(b'>', &mut buf);
                    this_string = from_utf8(&buf).unwrap().to_string();
                }
                this_string = this_string.trim_matches('>').to_string();

                let split_str = this_string.split("\n");
                let split_vec = split_str.collect::<Vec<&str>>();
                let identifier = split_vec[0];
                let sequence = split_vec[1..].join("");
                return Some(Ok(FastaSeq {
                    identifier: identifier.to_string(),
                    sequence: sequence.to_string(),
                }));
            }

            Err(e) => Some(Err(e)),
        }
    }
}