Search code examples
javareader

Convert java.io.Reader to the other Reader with skip reading some chars


I have java.io.Reader as input parameter to the method.

This reader reads file, like "abc\def\jhk"; I need to return from the method an other java.io.Reader, which skips reading "\" chars. So the final file should be: "abcdefjhk".

private Reader convertToFilteredReader(Reader reader) {
    Reader filteredReader = null;
    // some code here
    return filteredReader;
}

I have made this task by converting Reader to String, calling String.replace('\', '') and then converting this String into Reader. But I don't like this solution because of double converting (Reader -> String -> Reader).


Solution

  • Give this a try. Disclaimer: only superficially tested. Comments show how this is simple to reuse.

    import java.io.StringReader;
    import java.io.Writer;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.FilterReader;
    import java.io.IOException;
    import java.io.Reader;
    
    /**
     * Subclasses should implement isExcluded to determine which characters will be
     * 'swallowed' by the Reader
     *
     * @author g00se
     * @version 1.0
     */
    public abstract class ExclusionReader extends FilterReader {
        private char[] singleCharBuf = { 0 };
    
        public ExclusionReader(Reader in) {
            super(in);
        }
    
        // Testing only
        public static void main(String[] args) throws IOException {
            Reader downstream = args.length < 1 ? new StringReader("abc\\def\\ghi")
                    : Files.newBufferedReader(Path.of(args[0]));
            try (Reader in = new BackslashExcludingReader(downstream);
                    Writer out = new OutputStreamWriter(System.out)) {
                in.transferTo(out);
            }
        }
    
        public abstract boolean isExcluded(int codePoint);
    
        @Override
        public int read() throws IOException {
            int result = read(singleCharBuf, 0, 1);
            return result > -1 ? singleCharBuf[0] : result;
        }
    
        @Override
        public int read(char[] b) throws IOException {
            return read(b, 0, b.length);
        }
    
        @Override
        public int read(char[] buf, int off, int len) throws IOException {
            if (len <= 0) {
                if (len < 0) {
                    throw new IndexOutOfBoundsException();
                } else if ((off < 0) || (off > buf.length)) {
                    throw new IndexOutOfBoundsException();
                }
                return 0;
            }
            int charsCopied = 0;
            int c = -1;
    
            while ((charsCopied + off) < len && (c = in.read()) > -1) {
                if (!isExcluded(c)) {
                    buf[off + charsCopied] = (char) c;
                    charsCopied++;
                }
            }
    
            int returnVal = charsCopied;
            if (charsCopied == 0) {
                returnVal = Math.min(charsCopied, c);
            }
            return returnVal;
        }
    
        // What YOU need
        static class BackslashExcludingReader extends ExclusionReader {
            public BackslashExcludingReader(Reader in) {
                super(in);
            }
    
            @Override
            public boolean isExcluded(int codePoint) {
                return codePoint == '\\';
            }
    
        }
    }