JEditorPaneHTMLEditorKitParserStream流处理

wen java案例 2

本文目录导读:

JEditorPaneHTMLEditorKitParserStream流处理

  1. 核心组件与流处理机制
  2. 基本流处理示例
  3. 自定义Parser与流处理
  4. 高级流处理技巧
  5. 流处理优化建议
  6. 注意事项和最佳实践

在Java Swing开发中,JEditorPane 配合 HTMLEditorKit 处理HTML时,其底层解析器(Parser)是基于流(Stream)的,这涉及到如何将HTML文档作为输入流读取,并通过自定义或默认的解析器进行解析和渲染。

以下是关于 JEditorPaneHTMLEditorKit 及其解析器流处理的详细说明和实战示例:

核心组件与流处理机制

组件 作用
JEditorPane 用于显示HTML的文本组件
HTMLEditorKit 提供HTML编辑能力,包含解析器和视图工厂
Parser (解析器) 读取HTML输入流,解析成文档结构
Document 解析后的文档模型
Reader/InputStream 读取HTML数据的输入流

默认解析器链:

InputStream → InputStreamReader → HTMLEditorKit.Parser → HTMLDocument

基本流处理示例

1 从文件读取HTML并显示

import javax.swing.*;
import javax.swing.text.html.*;
import java.io.*;
public class HTMLStreamExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame("HTML Stream Example");
        JEditorPane editorPane = new JEditorPane();
        editorPane.setEditable(false);
        editorPane.setContentType("text/html");
        // 使用HTMLEditorKit
        HTMLEditorKit kit = new HTMLEditorKit();
        editorPane.setEditorKit(kit);
        // 创建HTML文档
        HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
        editorPane.setDocument(doc);
        try {
            // 从文件读取HTML流
            File htmlFile = new File("example.html");
            FileInputStream fis = new FileInputStream(htmlFile);
            InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
            // 直接将流读入文档
            kit.read(reader, doc, 0);
            reader.close();
            // 或者使用setPage方法(内部也会使用流)
            // editorPane.setPage(htmlFile.toURI().toURL());
        } catch (Exception e) {
            e.printStackTrace();
        }
        frame.add(new JScrollPane(editorPane));
        frame.setSize(600, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

自定义Parser与流处理

1 扩展HTMLEditorKit自定义解析器

import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;
import java.io.*;
public class CustomHTMLEditorKit extends HTMLEditorKit {
    @Override
    public HTMLEditorKit.Parser getParser() {
        return new CustomParser();
    }
    private static class CustomParser extends HTMLEditorKit.Parser {
        @Override
        public void parse(Reader r, ParserCallback cb, boolean ignoreCharSet) 
                throws IOException {
            System.out.println("[CustomParser] 开始解析HTML流...");
            // 包装Reader以便于监控流处理
            BufferedReader br = new BufferedReader(r);
            // 创建默认的解析器
            ParserDelegator delegator = new ParserDelegator();
            // 自定义回调包装
            ParserCallback wrappedCallback = new ParserCallbackWrapper(cb);
            // 委托给默认解析器
            delegator.parse(br, wrappedCallback, ignoreCharSet);
            System.out.println("[CustomParser] 解析完成!");
        }
    }
    // 记录解析过程的回调包装器
    private static class ParserCallbackWrapper extends ParserCallback {
        private ParserCallback original;
        public ParserCallbackWrapper(ParserCallback original) {
            this.original = original;
        }
        @Override
        public void handleText(char[] text, int pos) {
            System.out.println("[解析] 文本: " + new String(text).trim());
            original.handleText(text, pos);
        }
        @Override
        public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
            System.out.println("[解析] 开始标签: " + t + " at " + pos);
            original.handleStartTag(t, a, pos);
        }
        @Override
        public void handleEndTag(HTML.Tag t, int pos) {
            System.out.println("[解析] 结束标签: " + t);
            original.handleEndTag(t, pos);
        }
        @Override
        public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
            System.out.println("[解析] 自闭合标签: " + t);
            original.handleSimpleTag(t, a, pos);
        }
        @Override
        public void flush() throws BadLocationException {
            original.flush();
        }
    }
}

2 使用自定义解析器

public class CustomParserDemo {
    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame("Custom Parser Demo");
        JEditorPane editorPane = new JEditorPane();
        // 使用自定义EditorKit
        CustomHTMLEditorKit kit = new CustomHTMLEditorKit();
        editorPane.setEditorKit(kit);
        // HTML内容(作为字符串流)
        String html = "<html><body><h1>Hello</h1><p>This is <b>bold</b> text.</p></body></html>";
        StringReader reader = new StringReader(html);
        // 解析流
        HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
        kit.read(reader, doc, 0);
        editorPane.setDocument(doc);
        frame.add(new JScrollPane(editorPane));
        frame.setSize(400, 300);
        frame.setVisible(true);
    }
}

高级流处理技巧

1 带监控的流包装器

import java.io.*;
public class MonitoredInputStream extends FilterInputStream {
    private long totalBytesRead = 0;
    private long lastReportTime = System.currentTimeMillis();
    public MonitoredInputStream(InputStream in) {
        super(in);
    }
    @Override
    public int read() throws IOException {
        int b = super.read();
        if (b != -1) {
            totalBytesRead++;
            reportProgress();
        }
        return b;
    }
    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int bytesRead = super.read(b, off, len);
        if (bytesRead > 0) {
            totalBytesRead += bytesRead;
            reportProgress();
        }
        return bytesRead;
    }
    private void reportProgress() {
        long now = System.currentTimeMillis();
        if (now - lastReportTime > 1000) { // 每秒报告一次
            System.out.println("已读取: " + (totalBytesRead / 1024) + " KB");
            lastReportTime = now;
        }
    }
    public long getTotalBytesRead() {
        return totalBytesRead;
    }
}

2 流转换:从网络获取HTML

import java.net.*;
import javax.swing.text.html.*;
public class NetworkHTMLReader {
    public static void loadHTMLFromURL(JEditorPane editorPane, String urlString) {
        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            // 获取输入流
            InputStream inputStream = conn.getInputStream();
            // 使用监控流
            MonitoredInputStream monitoredStream = new MonitoredInputStream(inputStream);
            // 转换为Reader
            InputStreamReader reader = new InputStreamReader(monitoredStream, "UTF-8");
            // 解析
            HTMLEditorKit kit = (HTMLEditorKit) editorPane.getEditorKit();
            HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
            kit.read(reader, doc, 0);
            reader.close();
            System.out.println("总共读取: " + monitoredStream.getTotalBytesRead() + " bytes");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

流处理优化建议

1 使用BufferedReader提高性能

// 不推荐
InputStreamReader reader = new InputStreamReader(inputStream);
kit.read(reader, doc, 0);
// 推荐 - 使用缓冲
BufferedReader reader = new BufferedReader(
    new InputStreamReader(inputStream, "UTF-8"), 8192); // 8KB缓冲区
kit.read(reader, doc, 0);

2 处理字符编码问题

public static void loadHTMLWithEncoding(JEditorPane pane, InputStream is, String encoding) {
    try {
        // 先检测BOM标记
        PushbackInputStream pushback = new PushbackInputStream(is, 4);
        byte[] bom = new byte[4];
        int n = pushback.read(bom, 0, 4);
        String detectedEncoding = encoding;
        if (n >= 2) {
            if (bom[0] == (byte)0xFE && bom[1] == (byte)0xFF) {
                detectedEncoding = "UTF-16BE";
                pushback.unread(bom, 2, n - 2);
            } else if (bom[0] == (byte)0xFF && bom[1] == (byte)0xFE) {
                detectedEncoding = "UTF-16LE";
                pushback.unread(bom, 2, n - 2);
            } else if (n >= 3 && bom[0] == (byte)0xEF && 
                       bom[1] == (byte)0xBB && bom[2] == (byte)0xBF) {
                detectedEncoding = "UTF-8";
                pushback.unread(bom, 3, n - 3);
            } else {
                pushback.unread(bom, 0, n);
            }
        }
        Reader reader = new BufferedReader(
            new InputStreamReader(pushback, detectedEncoding));
        HTMLEditorKit kit = (HTMLEditorKit) pane.getEditorKit();
        HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
        kit.read(reader, doc, 0);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

注意事项和最佳实践

  1. 线程安全:解析过程应在EDT(事件分发线程)外进行,完成后更新UI
  2. 资源管理:始终在finally块中关闭流
  3. 错误处理:HTML解析错误不会抛出异常,但文档可能不完整
  4. 性能监控:大型HTML文件推荐使用缓冲区监控
// 在后台线程中加载HTML
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
    @Override
    protected Void doInBackground() throws Exception {
        // 在后台线程中解析
        HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
        kit.read(reader, doc, 0);
        return null;
    }
    @Override
    protected void done() {
        // 在EDT中更新UI
        editorPane.setDocument(doc);
    }
};
worker.execute();

通过合理使用流处理,你可以高效地从各种来源(文件、网络、内存)加载HTML内容到 JEditorPane 中显示,并且可以根据需要进行自定义解析和监控。

抱歉,评论功能暂时关闭!