Filter Streams
Home ] Up ] Files and Directories ] I/O Exceptions ] Streams and Stream Classification ] Character Streams ] Byte Streams ] Data Conversion Streams ] [ Filter Streams ] Message Formatting ] Tokenizers ] Zip & Jar Files ] Random Access ]

 

 

Here's an example of how you might write a filter stream -- here, a FilterReader. This one filters in such a way that it causes Java-style comments to be stripped from the stream as it passes through the filter:

package inputOutput;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.PushbackReader;
import java.io.Reader;

/**
 *  This class filters out Java-style comments.
 *  It replaces "/*" comments with a single space,
 *  and "//" comments with a newline.
 *
 *  NOTE: The algorithm used here is not perfect;
 *        It should ignore cases of "/*" and "//" within literal 
 *        strings. Perfecting the algorithm is left as an exercise
 *        for the reader (pun intended!).
 *
 *  @author B. J. Higgs
 */
public class CommentFilterReader extends PushbackReader
{
    public CommentFilterReader(Reader in)
    {
        super(in);
    }
    
    /**
     *  Read a single character. 
     *  This method will block until a character is available, 
     *  an I/O error occurs, or the end of the stream is reached. 
     *
     *  @return The character read, as an integer in the range 
     *          0 to 16383 (0x00-0xffff), 
     *          or -1 if the end of the stream has been reached
     */
    public int read() 
        throws IOException
    {
        int c = readNextNonCommentChar();
        return c;
    }
    
    /**
     *  Reads the input, and returns the next non-comment character.
     *  It detects the start and end of both kinds of Java comments,
     *  and skips over comment characters as it detects them.
     *
     *  @return The character read, as an integer in the range 
     *          0 to 16383 (0x00-0xffff), 
     *          or -1 if the end of the stream has been reached
     */
    private int readNextNonCommentChar() 
        throws IOException
    {
        int c = super.read();
        if (c == '/')
        {
            // Possible start of comment, so check for '*' or '/'
            int lookAheadChar = super.read();
            switch (lookAheadChar)
            {
                case '*':
                    // We've found a '/*' comment, so silently
                    // consume characters until we see a '*/'
                    while (true)
                    {
                        c = super.read();
                        if (c == -1)
                            break;      // End of stream
                        if (c == '*')
                        {
                            lookAheadChar = super.read();
                            if (lookAheadChar == '/')
                            {
                                c = ' '; // Replace comment with space
                                break;  // End of comment
                            }
                        }
                    }
                    break;
                        
                case '/':
                    // We've found a '//' comment, so silently
                    // consume characters until we see a newline
                    while (true)
                    {
                        c = super.read();
                        if (c == -1)
                            break;      // End of stream
                        if (c == '\n')
                        {
                            // Newline ends comment -- 
                            // return the newline
                            break;
                        }
                    }
                    break;
                        
                default:
                    // Not a command, so push back the look 
                    // ahead char so it gets read on the next read.
                    unread(lookAheadChar);
                    break;
            }
        }
        
        // No matter how we get here, 
        // c will contain the character to return.
        return c;
    }
    
    /**
     *  Read characters into a portion of an array. 
     *  This method will block until some input is available,
     *  an I/O error occurs, or the end of the stream is reached.
     *
     *  @param cbuf - Destination buffer 
     *  @param off - Offset at which to start storing characters 
     *  @param len - Maximum number of characters to read 
     *  @return The number of characters read, 
     *          or -1 if the end of the stream has been reached
     */
    public int read(char[] cbuf, int off, int len) 
        throws IOException
    {
        int count = 0;
        // For loop takes into account the available size of the 
        // buffer as well as the maximum number of characters to copy.
        for (int avail = cbuf.length - off; 
	   len > 0 && avail > 0; 
	   len--, avail--)
        {
            int c = readNextNonCommentChar();
            if (c == -1)
            {
                // If this read didn't read any characters, 
                // return end of stream; otherwise, return 
                // the actual number read. (The next call will
                // return end of stream.)
                if (count == 0)
                    count = -1;
                break;
            }
            // Move character into buffer and keep count and 
            // offset updated.
            cbuf[off++] = (char)c;
            count++;
        }
        
        return count;
    }
    
    /**
     *  Skip non-comment characters. 
     *  This method will block until some characters are available, 
     *  an I/O error occurs, or the end of the stream is reached.
     *
     *  @param n The number of characters to skip
     *  @return The number of characters actually skipped
     */
    public long skip(long n) 
        throws IOException
    {
        long count = 0;
        for (count = 0; count < n; count++)
        {
            int c = readNextNonCommentChar();
            if (c == -1)
                break;
        }
        
        return count;
    }


    /**
     *  Main entry point, for testing.
     *  Expects one or two arguments:
     *      inputFileName   -- the name of the file to read
     *      outputFileName  -- the name of the file to write 
     *                         the results
     *  (If only one argument is supplied, the output is to System.out)
     *  
     */
    public static void main(String[] args)
    {
        try
        {
            BufferedReader reader = new BufferedReader( 
                                            new CommentFilterReader(
                                                new FileReader(
                                                    args[0])));
            PrintWriter writer = null;
            if (args.length > 1)
            {
                writer = new PrintWriter( 
                            new BufferedWriter(
                                new FileWriter(args[1])));
            }
            else
            {
                writer = new PrintWriter(
                            new OutputStreamWriter(
                                System.out));
            }
            
            while (true)
            {
                String line = reader.readLine();
                if (line == null)
                    break;
                writer.println(line);
            }
            reader.close();
            writer.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

Note that I extended this class from PushbackReader, because I needed the ability to do a one-character read-ahead to determine whether a '/' character was the start of a comment or not.

When this program is applied to its own source, it outputs the following:

package inputOutput;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.PushbackReader;
import java.io.Reader;

 
public class CommentFilterReader extends PushbackReader
{
    public CommentFilterReader(Reader in)
    {
        super(in);
    }
    
     
    public int read() 
        throws IOException
    {
        int c = readNextNonCommentChar();
        return c;
    }
    
     
    private int readNextNonCommentChar() 
        throws IOException
    {
        int c = super.read();
        if (c == '/')
        {
            
            int lookAheadChar = super.read();
            switch (lookAheadChar)
            {
                case '*':
                    
                    
                    while (true)
                    {
                        c = super.read();
                        if (c == -1)
                            break;      
                        if (c == '*')
                        {
                            lookAheadChar = super.read();
                            if (lookAheadChar == '/')
                            {
                                c = ' '; 
                                break;  
                            }
                        }
                    }
                    break;
                        
                case '/':
                    
                    
                    while (true)
                    {
                        c = super.read();
                        if (c == -1)
                            break;      
                        if (c == '\n')
                        {
                            
                            
                            break;
                        }
                    }
                    break;
                        
                default:
                    
                    
                    unread(lookAheadChar);
                    break;
            }
        }
        
        
        
        return c;
    }
    
     
    public int read(char[] cbuf, int off, int len) 
        throws IOException
    {
        int count = 0;
        
        
        for (int avail = cbuf.length - off; 
           len > 0 && avail > 0; 
           len--, avail--)
        {
            int c = readNextNonCommentChar();
            if (c == -1)
            {
                
                
                
                
                if (count == 0)
                    count = -1;
                break;
            }
            
            
            cbuf[off++] = (char)c;
            count++;
        }
        
        return count;
    }
    
     
    public long skip(long n) 
        throws IOException
    {
        long count = 0;
        for (count = 0; count < n; count++)
        {
            int c = readNextNonCommentChar();
            if (c == -1)
                break;
        }
        
        return count;
    }


     
    public static void main(String[] args)
    {
        try
        {
            BufferedReader reader = new BufferedReader( 
                                            new CommentFilterReader(
                                                new FileReader(
                                                    args[0])));
            PrintWriter writer = null;
            if (args.length > 1)
            {
                writer = new PrintWriter( 
                            new BufferedWriter(
                                new FileWriter(args[1])));
            }
            else
            {
                writer = new PrintWriter(
                            new OutputStreamWriter(
                                System.out));
            }
            
            while (true)
            {
                String line = reader.readLine();
                if (line == null)
                    break;
                writer.println(line);
            }
            reader.close();
            writer.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
 
 
The page was last updated February 19, 2008