JEditorPaneHTMLEditorKitParserOctal八进制处理

wen java案例 1

本文目录导读:

JEditorPaneHTMLEditorKitParserOctal八进制处理

  1. 核心概念:HTML 字符引用中的进制
  2. HTMLEditorKit.Parser 的实际行为
  3. 为什么有人会问“八进制处理”?
  4. 如果你确实需要支持自定义的“八进制” NCR

这是一个关于 Java Swing 中 JEditorPaneHTMLEditorKit 以及 HTML 解析器对八进制(Octal)编码处理的技术问题。

在标准的 HTMLEditorKit / Parser 中,对于数字字符引用(Numeric Character Reference),它通常只支持十进制(如 A)和十六进制(如 A),而不支持八进制(如 e,但注意 e 实际上是十进制 101,不是八进制 101 对应的十进制 65)。

我们需要先澄清一个重要的概念混淆,然后再说实现。


核心概念:HTML 字符引用中的进制

在 HTML 或 XML 中,数字字符引用(NCR)只有两种标准写法:

  • 十进制&#d; —— 其中的 d 是十进制数字。
  • 十六进制&#xh;&#Xh; —— h 是十六进制数字。

HTML 标准从来就不支持八进制。

当你看到 e,解析器(包括 HTMLEditorKit.Parser)会按照十进制 101 来解释,它对应的是字符 e,而不是八进制 101(十进制 65,即 A)。


HTMLEditorKit.Parser 的实际行为

javax.swing.text.html.HTMLEditorKit.Parser 是一个抽象类,它依赖于具体的实现(通常是 javax.swing.text.html.parser.ParserDelegator)来解析 HTML。ParserDelegator 内部使用了一个 DocumentParser,它严格遵循 HTML 规范,仅处理十进制和以 0x0X 开头的十六进制值

测试示例(证明不支持八进制):

import javax.swing.*;
import javax.swing.text.html.*;
import java.io.StringReader;
public class OctalTest {
    public static void main(String[] args) throws Exception {
        JEditorPane pane = new JEditorPane();
        pane.setEditorKit(new HTMLEditorKit());
        // 情况1:标准十进制 65 -> 'A'
        String htmlDec = "<html><body>&#65;</body></html>";
        pane.read(new StringReader(htmlDec), "text/html");
        SwingUtilities.invokeLater(() -> System.out.println("Decimal 65: " + pane.getText()));
        // 情况2:如果误以为 &#101; 是八进制(对应十进制65),实际解析为十进制101 -> 'e'
        String htmlMis = "<html><body>&#101;</body></html>";
        pane.read(new StringReader(htmlMis), "text/html");
        SwingUtilities.invokeLater(() -> System.out.println("Numeric 101: " + pane.getText()));
        // 情况3:官方不支持八进制,&#0101; 会被解析为什么?
        // 由于0开头,一些解析器可能将0101解释为十进制101(忽略前导零),或者报错。
        // Swing的实现会将其作为十进制101处理(前导零被忽略)
        String htmlLeadZero = "<html><body>&#0101;</body></html>";
        pane.read(new StringReader(htmlLeadZero), "text/html");
        SwingUtilities.invokeLater(() -> System.out.println("With leading zero 0101: " + pane.getText()));
    }
}

结果:

  • &#65; → 显示 A
  • &#101; → 显示 e (不是八进制处理后的 A
  • &#0101; → 显示 e (前导零被忽略,当作十进制 101)

为什么有人会问“八进制处理”?

可能是因为在 其他语言或旧版解析器 中,有些(非标准)实现会将带有前导零的数字(如 &#0101;)当作八进制来处理。

  • 在 C/C++ 中,0101 是八进制,等于十进制 65。
  • 在 JavaScript 的旧版 String.fromCharCode() 中,'\101' 是八进制。

但在 Java 的 HTML 解析器 中,不会这样处理。


如果你确实需要支持自定义的“八进制” NCR

如果由于某些历史原因,你需要让 JEditorPane 支持将 &#0101; 等视为八进制,你必须重写解析逻辑,方法如下:

方案:自定义 HTMLEditorKit + ViewFactory

  1. 继承 HTMLEditorKit,重写 getViewFactory(),返回一个自定义的 ViewFactory
  2. 自定义 HTMLDocument 或使用 HTMLReader 的回调处理自定义实体。
  3. 正则替换(简单但有限):在将 HTML 字符串设置给 JEditorPane 之前,手动将 &#0[0-7]+; 匹配的内容转换为对应的十进制 Unicode。
// 示例:将 &#0xxx 视为八进制
String html = "&#0101;"; // 八进制101 -> 十进制65 -> 'A'
String processed = html.replaceAll("&#0([0-7]+);", match -> {
    String octalDigits = match.group(1);
    int charCode = Integer.parseInt(octalDigits, 8); // 八进制转十进制
    return "&#" + charCode + ";";
});
// processed = "&#65;"

然后将 processed 交给 pane.read(...)pane.setText(processed)


问法 实际含义 HTMLEditorKit 行为
&#65; 十进制 65 正确解析为 A
&#x41; 十六进制 41 正确解析为 A
&#101; 十进制 101 解析为 e(不是八进制)
&#0101; 带前导零的十进制 解析为 e(忽略前导零)
&#O101; / &#o101; 非标准八进制 ❌ 不被识别,保留原样

javax.swing.text.html.HTMLEditorKit.Parser 及其默认实现不支持八进制数字字符引用,它只遵循 HTML 标准:十进制和十六进制(&#d;&#xh;),如果你需要八进制支持,必须在传递给 Swing 之前手动转换。

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