本文目录导读:

我来详细解释 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 & World <test></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("&", '&');
put("<", '<');
put(">", '>');
put(""", '"');
put(" ", ' '); // 非断行空格
put("'", '\'');
put("©", '©');
put("®", '®');
put("€", '€');
put("£", '£');
put("¥", '¥');
}};
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实体: & < > "</p>
<p>2. 数字引用: A A ☃</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("&", '&');
charCache.put("<", '<');
charCache.put(">", '>');
charCache.put(""", '"');
}
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实体: & < > " '</p>
<p>数字引用: A B C (ASCII A, B, C)</p>
<p>十六进制: A B C</p>
<p>特殊符号: © ® € £ ¥</p>
<p>Unicode: ★ ☆ ♠ ♥ ♦ ♣</p>
<p>数学符号: ∑ ∏ √ ∞</p>
<pre>
保留字符: < > &
空格: 非断行空格
</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();
}
}
}
这个解析机制提供了:
- 自动实体解析 - HTML字符实体的自动转换
- 编码支持 - 多字符集编码处理
- 性能优化 - 缓存机制提高解析效率
- 灵活扩展 - 支持自定义解析规则
- 错误处理 - 健壮的错误处理机制
需要注意的问题是:
- 默认解析器可能不支持所有HTML5实体
- 大文档解析可能影响性能
- 特殊字符的处理需要谨慎
- 编码声明可能被忽略