JEditorPaneHTMLEditorKitParserChar字符解析

wen java案例 2

本文目录导读:

JEditorPaneHTMLEditorKitParserChar字符解析

  1. 基础架构
  2. 字符解析机制
  3. 字符编码处理
  4. 特殊字符处理示例
  5. 性能优化和缓存
  6. 完整的解析示例

我来详细解释 JEditorPane、HTMLEditorKit 的字符解析机制:

基础架构

import javax.swing.*;
import javax.swing.text.html.*;
import javax.swing.text.*;
import java.io.*;
public class HTMLCharParserExample {
    public static void main(String[] args) {
        JEditorPane editor = new JEditorPane();
        editor.setEditorKit(new HTMLEditorKit());
        // 设置HTML内容
        String html = "<html><body>Hello &amp; World &lt;test&gt;</body></html>";
        editor.setText(html);
    }
}

字符解析机制

1 HTML实体解析

// HTMLEditorKit的内置解析器
HTMLEditorKit.Parser parser = new HTMLEditorKit.Parser() {
    @Override
    public void parse(Reader r, HTMLEditorKit.ParserCallback cb, boolean ignoreCharSet) 
            throws IOException {
        // 默认解析器实现
    }
};
// 常见HTML实体的解析
Map<String, Character> htmlEntities = new HashMap<>() {{
    put("&amp;", '&');
    put("&lt;", '<');
    put("&gt;", '>');
    put("&quot;", '"');
    put("&nbsp;", ' ');  // 非断行空格
    put("&apos;", '\'');
    put("&copy;", '©');
    put("&reg;", '®');
    put("&euro;", '€');
    put("&pound;", '£');
    put("&yen;", '¥');
}};

2 自定义字符解析器

class CustomCharParser extends HTMLEditorKit.Parser {
    private static final Pattern ENTITY_PATTERN = 
        Pattern.compile("&(#\\d+|#x[0-9a-fA-F]+|[a-zA-Z]+);");
    @Override
    public void parse(Reader r, HTMLEditorKit.ParserCallback cb, boolean ignoreCharSet) 
            throws IOException {
        BufferedReader br = new BufferedReader(r);
        String line;
        StringBuilder content = new StringBuilder();
        while ((line = br.readLine()) != null) {
            content.append(line).append("\n");
        }
        String processedContent = processEntities(content.toString());
        // 解析处理后的内容
        processHTMLContent(processedContent, cb);
    }
    private String processEntities(String html) {
        StringBuffer result = new StringBuffer();
        Matcher matcher = ENTITY_PATTERN.matcher(html);
        while (matcher.find()) {
            String entity = matcher.group(1);
            String replacement = resolveEntity(entity);
            matcher.appendReplacement(result, replacement);
        }
        matcher.appendTail(result);
        return result.toString();
    }
    private String resolveEntity(String entity) {
        if (entity.startsWith("#x")) {
            // 十六进制字符引用
            int codePoint = Integer.parseInt(entity.substring(2), 16);
            return String.valueOf((char) codePoint);
        } else if (entity.startsWith("#")) {
            // 十进制字符引用
            int codePoint = Integer.parseInt(entity.substring(1));
            return String.valueOf((char) codePoint);
        } else {
            // 命名实体引用
            return resolveNamedEntity(entity);
        }
    }
    private String resolveNamedEntity(String name) {
        Map<String, String> entityMap = new HashMap<>();
        entityMap.put("amp", "&");
        entityMap.put("lt", "<");
        entityMap.put("gt", ">");
        // 更多实体映射...
        return entityMap.getOrDefault(name, "&" + name + ";");
    }
}

字符编码处理

class CharEncodingHandler {
    public static void setEncoding(JEditorPane editor, String encoding) {
        HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
        // 设置文档属性
        Document doc = kit.createDefaultDocument();
        doc.putProperty("charset", encoding);
        // 设置字符集
        editor.setDocument(doc);
        // 设置内容类型
        editor.setContentType("text/html; charset=" + encoding);
    }
    public static String detectEncoding(byte[] data) {
        // 检测BOM
        if (data.length >= 3) {
            if (data[0] == (byte)0xEF && data[1] == (byte)0xBB && data[2] == (byte)0xBF) {
                return "UTF-8";
            } else if (data[0] == (byte)0xFF && data[1] == (byte)0xFE) {
                return "UTF-16LE";
            } else if (data[0] == (byte)0xFE && data[1] == (byte)0xFF) {
                return "UTF-16BE";
            }
        }
        return "ISO-8859-1"; // 默认编码
    }
}

特殊字符处理示例

public class SpecialCharDemo {
    public static void main(String[] args) {
        JEditorPane editor = new JEditorPane();
        editor.setEditorKit(new HTMLEditorKit());
        // 测试各种字符
        String testHtml = """
            <html>
            <body>
                <h3>特殊字符测试</h3>
                <p>1. HTML实体: &amp; &lt; &gt; &quot;</p>
                <p>2. 数字引用: &#65; &#x41; &#x2603;</p>
                <p>3. Unicode字符: \u2603 \u00A9 \u20AC</p>
                <p>4. 控制字符: 制表符\t 换行\n</p>
            </body>
            </html>
            """;
        editor.setText(testHtml);
        // 监听文档变化
        editor.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
            @Override
            public void insertUpdate(javax.swing.event.DocumentEvent e) {
                analyzeChars(editor);
            }
            @Override
            public void removeUpdate(javax.swing.event.DocumentEvent e) {}
            @Override
            public void changedUpdate(javax.swing.event.DocumentEvent e) {}
        });
    }
    private static void analyzeChars(JEditorPane editor) {
        try {
            String text = editor.getDocument().getText(
                0, editor.getDocument().getLength());
            System.out.println("字符串分析:");
            System.out.println("原始文本: " + text);
            System.out.println("字符数量: " + text.length());
            // 分析每个字符
            for (int i = 0; i < text.length(); i++) {
                char c = text.charAt(i);
                System.out.printf("位置 %3d: U+%04X '%c' %s%n",
                    i, (int)c, 
                    Character.isISOControl(c) ? '.' : c,
                    getCharCategory(c));
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private static String getCharCategory(char c) {
        if (Character.isLetter(c)) return "字母";
        if (Character.isDigit(c)) return "数字";
        if (Character.isWhitespace(c)) return "空白";
        if (Character.isISOControl(c)) return "控制字符";
        return "其他";
    }
}

性能优化和缓存

class CharCacheManager {
    private static final int MAX_CACHE_SIZE = 10000;
    private static final LinkedHashMap<String, String> entityCache = 
        new LinkedHashMap<String, String>(MAX_CACHE_SIZE, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
                return size() > MAX_CACHE_SIZE;
            }
        };
    private static final Map<String, Character> charCache = new HashMap<>();
    static {
        // 预加载常用字符
        charCache.put("&amp;", '&');
        charCache.put("&lt;", '<');
        charCache.put("&gt;", '>');
        charCache.put("&quot;", '"');
    }
    public static synchronized String getOrParse(String entity) {
        // 检查缓存
        String cached = entityCache.get(entity);
        if (cached != null) {
            return cached;
        }
        // 解析并缓存
        String parsed = parseEntity(entity);
        entityCache.put(entity, parsed);
        return parsed;
    }
    private static String parseEntity(String entity) {
        // 实现具体的解析逻辑
        if (entity.startsWith("&#")) {
            try {
                int codePoint = Integer.parseInt(
                    entity.substring(2, entity.length() - 1));
                return String.valueOf((char) codePoint);
            } catch (NumberFormatException e) {
                return entity;
            }
        }
        return entity;
    }
}

完整的解析示例

public class CompleteCharParserExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("HTML字符解析示例");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JEditorPane editor = new JEditorPane();
            editor.setEditorKit(new HTMLEditorKit());
            // 创建一个包含各种字符的HTML
            String htmlContent = createTestHTML();
            editor.setText(htmlContent);
            // 添加滚动面板
            JScrollPane scrollPane = new JScrollPane(editor);
            // 添加控制面板
            JPanel controlPanel = new JPanel();
            JButton analyzeBtn = new JButton("分析字符");
            analyzeBtn.addActionListener(e -> analyzeCharacters(editor));
            controlPanel.add(analyzeBtn);
            frame.add(scrollPane, BorderLayout.CENTER);
            frame.add(controlPanel, BorderLayout.SOUTH);
            frame.setSize(600, 400);
            frame.setVisible(true);
        });
    }
    private static String createTestHTML() {
        return """
            <html>
            <head>
                <meta charset="UTF-8">
            </head>
            <body>
                <h2>HTML字符解析测试</h2>
                <p>HTML实体: &amp; &lt; &gt; &quot; &apos;</p>
                <p>数字引用: &#65; &#66; &#67; (ASCII A, B, C)</p>
                <p>十六进制: &#x41; &#x42; &#x43;</p>
                <p>特殊符号: &copy; &reg; &euro; &pound; &yen;</p>
                <p>Unicode: ★ ☆ ♠ ♥ ♦ ♣</p>
                <p>数学符号: &sum; &prod; &radic; &infin;</p>
                <pre>
                保留字符: < > &
                空格: &nbsp;&nbsp;&nbsp;非断行空格
                </pre>
            </body>
            </html>
            """;
    }
    private static void analyzeCharacters(JEditorPane editor) {
        try {
            Document doc = editor.getDocument();
            String text = doc.getText(0, doc.getLength());
            System.out.println("===== 字符分析结果 =====");
            System.out.println("总字符数: " + text.length());
            // 分类统计
            Map<String, Integer> categoryCount = new HashMap<>();
            for (char c : text.toCharArray()) {
                String category;
                if (Character.isLetter(c)) category = "字母";
                else if (Character.isDigit(c)) category = "数字";
                else if (Character.isWhitespace(c)) category = "空白";
                else if (Character.isISOControl(c)) category = "控制";
                else category = "符号";
                categoryCount.merge(category, 1, Integer::sum);
            }
            System.out.println("字符类别统计:");
            categoryCount.forEach((k, v) -> 
                System.out.println("  " + k + ": " + v));
            // 显示所有非ASCII字符
            System.out.println("\n非ASCII字符:");
            for (char c : text.toCharArray()) {
                if (c > 127) {
                    System.out.printf("  U+%04X: '%c'%n", (int)c, c);
                }
            }
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }
}

这个解析机制提供了:

  1. 自动实体解析 - HTML字符实体的自动转换
  2. 编码支持 - 多字符集编码处理
  3. 性能优化 - 缓存机制提高解析效率
  4. 灵活扩展 - 支持自定义解析规则
  5. 错误处理 - 健壮的错误处理机制

需要注意的问题是:

  • 默认解析器可能不支持所有HTML5实体
  • 大文档解析可能影响性能
  • 特殊字符的处理需要谨慎
  • 编码声明可能被忽略

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