package htsjdk.samtools.cram.encoding.reader; import net.sf.cram.common.Utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; public class FastqReader implements Iterator<FastqRead> { private InputStream is; private boolean stripSegmentIndexFromReadName = true; private FastqRead next; private int indexInSegment = 0; public FastqReader(InputStream is) throws IOException { this.is = is; is.mark(10); if (is.read() == -1) { next = null; } else { is.reset(); next = readNext(); } } @Override public boolean hasNext() { return next != null; } private byte[] readName() throws IOException { int nameLen = 0; is.mark(1024); int b = is.read(); for (; b != -1 && b != '\n'; nameLen++, b = is.read()) ; if (b == -1) throw new EOFException("Unexpected end of fastq stream."); is.reset(); byte[] name = null; if (stripSegmentIndexFromReadName) { is.skip(nameLen - 2); switch (is.read()) { case -1: throw new EOFException("Unexpected end of fastq stream."); case '/': indexInSegment = is.read(); if (indexInSegment == -1) throw new EOFException("Unexpected end of fastq stream."); if (indexInSegment > 0) { is.reset(); name = Utils.readFully(is, nameLen - 2); // a slash, a digit and a new line: is.skip(3); } else indexInSegment = 0; break; default: break; } } if (name == null) { is.reset(); name = Utils.readFully(is, nameLen); // skip new line byte: is.skip(1); } return name; } private byte[] readBases() throws IOException { int readLen = 0; is.mark(1024 * 1024); int b = is.read(); for (; b != -1 && b != '\n'; readLen++, b = is.read()) ; if (b == -1) throw new EOFException("Unexpected end of fastq stream."); is.reset(); byte[] bases = Utils.readFully(is, readLen); // skip new line byte: is.skip(1); return bases; } private FastqRead readNext() throws IOException { is.mark(10); if (is.read() == -1) return null; else is.reset(); byte[] name = readName(); byte[] bases = readBases(); // skip scores defintion line: '+', new line: is.skip(1 + 1); byte[] scores = Utils.readFully(is, bases.length); // skip new line byte at the end of scores line: is.skip(1); return new FastqRead(bases.length, name, true, indexInSegment, bases, scores); } @Override public FastqRead next() { if (next == null) throw new RuntimeException("No elements left."); FastqRead result = next; try { next = readNext(); return result; } catch (IOException e) { throw new RuntimeException(e); } } @Override public void remove() { throw new RuntimeException("Remove not supported."); } public static void main(String[] args) throws IOException { String r1 = "n1\nACGT\n+\n!!!!\n"; String r2 = "n2/1\nCGT\n+\n!!!\n"; String r3 = "n2/2\nCGT\n+\n!!!\n"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(r1.getBytes()); baos.write(r2.getBytes()); baos.write(r3.getBytes()); baos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); FastqReader reader = new FastqReader(bais); while (reader.hasNext()) { FastqRead read = reader.next(); System.out.println(new String(read.data)); } } }