ZetCode

Java FilterWriter 类

最后修改时间:2025 年 4 月 16 日

java.io.FilterWriter 类是一个用于写入过滤字符流的抽象类。它充当自定义写入器实现的基类,这些实现会在写入前转换或过滤数据。该类本身只是用将请求传递给包含的写入器的版本覆盖所有 Writer 方法。

FilterWriter 扩展了 Writer 并需要一个 Writer 对象才能运行。子类应该重写某些方法以添加过滤行为。与它的输入对应物 FilterReader 不同,这个类通常不直接在应用程序中使用。

FilterWriter 类概述

FilterWriter 为过滤字符输出流提供了一个框架。该类本身不执行任何过滤 - 子类必须实现实际的过滤逻辑。所有方法都委托给底层写入器。

public abstract class FilterWriter extends Writer {
    protected Writer out;
    protected FilterWriter(Writer out);
    public void write(int c) throws IOException;
    public void write(char[] cbuf, int off, int len) throws IOException;
    public void write(String str, int off, int len) throws IOException;
    public void flush() throws IOException;
    public void close() throws IOException;
}

上面的代码显示了 FilterWriter 的结构。受保护的 out 字段保存着底层的写入器。除非被子类覆盖,否则所有写入操作都将转发给此写入器。

创建自定义 FilterWriter

要创建一个有用的 FilterWriter,您必须扩展它并覆盖方法以添加过滤行为。此示例创建了一个简单的转换为大写的转换器,它在将所有写入的字符传递到底层写入器之前将其转换为大写。

Main.java
import java.io.FilterWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

class UppercaseFilterWriter extends FilterWriter {
    
    public UppercaseFilterWriter(Writer out) {
        super(out);
    }
    
    @Override
    public void write(int c) throws IOException {
        super.write(Character.toUpperCase(c));
    }
    
    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        char[] upperChars = new char[len];
        for (int i = 0; i < len; i++) {
            upperChars[i] = Character.toUpperCase(cbuf[off + i]);
        }
        super.write(upperChars, 0, len);
    }
    
    @Override
    public void write(String str, int off, int len) throws IOException {
        write(str.toCharArray(), off, len);
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        StringWriter sw = new StringWriter();
        UppercaseFilterWriter filter = new UppercaseFilterWriter(sw);
        
        filter.write("Hello, FilterWriter!");
        filter.close();
        
        System.out.println("Filtered output: " + sw.toString());
    }
}

此示例演示了一个自定义 FilterWriter 实现。UppercaseFilterWriter 在写入前将所有字符转换为大写。StringWriter 捕获输出。所有三个写入方法都被重写以确保行为一致。

带有字符替换的 FilterWriter

此示例创建一个 FilterWriter,用于替换输出流中的特定字符。替换发生在写入到底层写入器之前。这对于清理输出或实现简单的密码非常有用。

Main.java
import java.io.FilterWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

class ReplaceFilterWriter extends FilterWriter {
    private final char target;
    private final char replacement;
    
    public ReplaceFilterWriter(Writer out, char target, char replacement) {
        super(out);
        this.target = target;
        this.replacement = replacement;
    }
    
    @Override
    public void write(int c) throws IOException {
        super.write(c == target ? replacement : c);
    }
    
    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        char[] filtered = new char[len];
        for (int i = 0; i < len; i++) {
            filtered[i] = (cbuf[off + i] == target) ? replacement : cbuf[off + i];
        }
        super.write(filtered, 0, len);
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        StringWriter sw = new StringWriter();
        ReplaceFilterWriter filter = new ReplaceFilterWriter(sw, 'e', '3');
        
        filter.write("Replace all 'e' characters with '3'");
        filter.close();
        
        System.out.println("Filtered output: " + sw.toString());
    }
}

ReplaceFilterWriter 将所有目标字符的出现替换为替换字符。构造函数接受目标字符和替换字符。单字符和数组写入方法都被重写以执行替换。此示例将所有 'e' 字符替换为 '3'。

带有 HTML 转义的 FilterWriter

此示例实现了一个 FilterWriter,用于转义 HTML 特殊字符。这在编写将显示在 HTML 中的文本时非常有用,以防止 XSS 攻击或确保正确的渲染。

Main.java
import java.io.FilterWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

class HtmlEscapeFilterWriter extends FilterWriter {
    
    public HtmlEscapeFilterWriter(Writer out) {
        super(out);
    }
    
    @Override
    public void write(int c) throws IOException {
        switch (c) {
            case '&':  super.write("&"); break;
            case '<':  super.write("<"); break;
            case '>':  super.write(">"); break;
            case '"': super.write("""); break;
            case '\'': super.write("'"); break;
            default:   super.write(c); break;
        }
    }
    
    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        for (int i = 0; i < len; i++) {
            write(cbuf[off + i]);
        }
    }
    
    @Override
    public void write(String str, int off, int len) throws IOException {
        write(str.toCharArray(), off, len);
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        StringWriter sw = new StringWriter();
        HtmlEscapeFilterWriter filter = new HtmlEscapeFilterWriter(sw);
        
        filter.write("");
        filter.close();
        
        System.out.println("Escaped HTML: " + sw.toString());
    }
}

HtmlEscapeFilterWriter 将特殊 HTML 字符转换为其实体等价物。单字符写入方法处理转换,而数组和字符串方法将其委托给它。这确保所有输出都得到正确转义,无论调用哪个写入方法。

使用 FilterWriter 计数字符

此示例创建一个 FilterWriter,用于计算写入的字符数。该计数可以随时检索,对于日志记录或监视目的很有用。

Main.java
import java.io.FilterWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

class CountingFilterWriter extends FilterWriter {
    private int count = 0;
    
    public CountingFilterWriter(Writer out) {
        super(out);
    }
    
    public int getCount() {
        return count;
    }
    
    @Override
    public void write(int c) throws IOException {
        count++;
        super.write(c);
    }
    
    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        count += len;
        super.write(cbuf, off, len);
    }
    
    @Override
    public void write(String str, int off, int len) throws IOException {
        count += len;
        super.write(str, off, len);
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        StringWriter sw = new StringWriter();
        CountingFilterWriter filter = new CountingFilterWriter(sw);
        
        filter.write("Hello");
        filter.write(", ");
        filter.write("world!".toCharArray());
        filter.close();
        
        System.out.println("Output: " + sw.toString());
        System.out.println("Characters written: " + filter.getCount());
    }
}

CountingFilterWriter 维护通过它写入的所有字符的计数。每个写入方法都会相应地增加计数器。可以使用 getCount 方法检索计数。此示例写入三个段并报告总字符数。

带有行号的 FilterWriter

此示例实现了一个 FilterWriter,它将行号添加到输出中。每行新行都以其行号为前缀,这对于创建编号列表或日志很有用。

Main.java
import java.io.FilterWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

class LineNumberingFilterWriter extends FilterWriter {
    private int lineNumber = 1;
    private boolean atStartOfLine = true;
    
    public LineNumberingFilterWriter(Writer out) {
        super(out);
    }
    
    @Override
    public void write(int c) throws IOException {
        if (atStartOfLine) {
            super.write(String.format("%3d: ", lineNumber++));
            atStartOfLine = false;
        }
        super.write(c);
        if (c == '\n') {
            atStartOfLine = true;
        }
    }
    
    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        for (int i = 0; i < len; i++) {
            write(cbuf[off + i]);
        }
    }
    
    @Override
    public void write(String str, int off, int len) throws IOException {
        write(str.toCharArray(), off, len);
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        StringWriter sw = new StringWriter();
        LineNumberingFilterWriter filter = new LineNumberingFilterWriter(sw);
        
        filter.write("First line\nSecond line\nThird line");
        filter.close();
        
        System.out.println("Numbered output:\n" + sw.toString());
    }
}

LineNumberingFilterWriter 将行号添加到每一行新行。它跟踪它何时位于一行的开头,并插入行号前缀。行号在每个换行符后递增。所有写入方法都委托给单字符版本以保持一致的行为。

带有缩进的 FilterWriter

此示例创建了一个 FilterWriter,用于维护缩进级别。每一行新行都以对应于当前缩进级别的多个制表符开头,这对于代码生成或结构化输出很有用。

Main.java
import java.io.FilterWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

class IndentingFilterWriter extends FilterWriter {
    private int indentLevel = 0;
    private boolean atStartOfLine = true;
    
    public IndentingFilterWriter(Writer out) {
        super(out);
    }
    
    public void increaseIndent() {
        indentLevel++;
    }
    
    public void decreaseIndent() {
        if (indentLevel > 0) {
            indentLevel--;
        }
    }
    
    @Override
    public void write(int c) throws IOException {
        if (atStartOfLine && c != '\n') {
            for (int i = 0; i < indentLevel; i++) {
                super.write('\t');
            }
            atStartOfLine = false;
        }
        super.write(c);
        if (c == '\n') {
            atStartOfLine = true;
        }
    }
    
    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        for (int i = 0; i < len; i++) {
            write(cbuf[off + i]);
        }
    }
    
    @Override
    public void write(String str, int off, int len) throws IOException {
        write(str.toCharArray(), off, len);
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        StringWriter sw = new StringWriter();
        IndentingFilterWriter filter = new IndentingFilterWriter(sw);
        
        filter.write("First line\n");
        filter.increaseIndent();
        filter.write("Indented line\n");
        filter.increaseIndent();
        filter.write("More indented\n");
        filter.decreaseIndent();
        filter.write("Less indented\n");
        filter.close();
        
        System.out.println("Indented output:\n" + sw.toString());
    }
}

IndentingFilterWriter 维护新行的缩进级别。increaseIndentdecreaseIndent 方法调整当前级别。每一行新行都以适当数量的制表符开头。写入器跟踪行开头,以便仅在需要时插入缩进。

来源

Java FilterWriter 类文档

在本文中,我们介绍了 Java FilterWriter 类的基本方法和特性。理解这些概念对于在 Java 应用程序中创建自定义输出过滤器至关重要。

作者

我叫 Jan Bodnar,是一名经验丰富的程序员,在这一领域拥有多年的经验。我从 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和八本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助其他人掌握编程概念。

列出所有Java教程