我试图了解位置/偏移量在 HTMLDocument
中的工作原理。描述了位置/偏移语义 here .我的解释是,这些是由 HTMLDocument
表示的屏幕字符序列中的索引。
考虑来自 the HTMLDocument
documentation 的示例 HTML :
<html>
<head>
<title>An example HTMLDocument</title>
<style type="text/css">
div { background-color: silver; }
ul { color: red; }
</style>
</head>
<body>
<div id="BOX">
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</div>
</body>
</html>
当我在浏览器中打开此 HTML 时,我只看到“第 1 段”和“第 2 段”(没有前导空格或换行符)。所以我认为“第 1 段”从偏移 0
开始。
但请考虑以下代码,我在示例 HTML 中打印文本和主体的偏移量:
import java.io.StringReader;
import javax.swing.text.Element;
import javax.swing.text.html.*;
public class Test {
public static void main(String[] args) throws Exception {
String html = " <html>\n"
+ " <head>\n"
+ " <title>An example HTMLDocument</title>\n"
+ " <style type=\"text/css\">\n"
+ " div { background-color: silver; }\n"
+ " ul { color: red; }\n"
+ " </style>\n"
+ " </head>\n"
+ " <body>\n"
+ " <div id=\"BOX\">\n"
+ " <p>Paragraph 1</p>\n"
+ " <p>Paragraph 2</p>\n"
+ " </div>\n"
+ " </body>\n"
+ " </html>\n";
HTMLEditorKit htmlKit = new HTMLEditorKit();
HTMLDocument doc = (HTMLDocument) htmlKit.createDefaultDocument();
htmlKit.read(new StringReader(html), doc, 0);
System.out.println("doc length: " + doc.getLength());
String text = doc.getText(0, doc.getLength());
System.out.println("doc text, surrounded by quotes, with newlines replaced with /: \""
+ text.replace('\n', '/') + "\"");
Element element = doc.getDefaultRootElement().getElement(1);
System.out.println("element name: " + element.getName());
int offset = element.getStartOffset();
System.out.println("offset of body: " + offset);
}
}
输出:
doc length: 26
doc text, surrounded by quotes, with newlines replaced with /: " /Paragraph 1/Paragraph 2"
element name: body
offset of body: 3
基本问题:为什么“Paragraph 1”(正文的开头)位于索引 3
处?文本的前三个字符(两个空格和一个换行符)来自哪里?我是否误解了“偏移量”的含义?
挑战问题:给定一些 HTML(简单到可以通过检查完全理解),我怎样才能严格地手工计算出所有 DOM 元素的偏移量?
更多信息:
如果我从 HTML 中删除 style
标签,我会得到相同的结果(3
的正文偏移量)。如果我还删除了 title
,我得到的正文偏移量为 1
。如果我最终完全删除 head
,我会得到 0
的正文偏移量(正如预期的那样)。那么显然 style
贡献了 0,title
贡献了 2,而 head
贡献了 1 到 body 的偏移量?这背后的原因是什么?
这似乎也不受 HTML 字符串中空格的影响。
最佳答案
好问题。您可以根据一些规则计算出偏移量(以及 JEditorPane
中必要的插入符位置)——您已经提到了最重要的规则。
也许有几个关键标签是:
-
<head>
+1 -
<title>
+2 -
<meta>
+1 -
<p>
文本长度 +1(对于 CR)
如果您还没有找到它,查看偏移量列表及其分解方式的最简单方法是 HTMLDocument.dump(System.out);
.例如。对于上面的示例 HTML:
<html
name=html
>
<head
name=head
>
<p-implied
name=p-implied
>
<title
name=title
>
[0,1][ ]
<title
endtag=true
name=title
>
[1,2][ ]
<content
CR=true
name=content
>
[2,3][
]
<body
name=body
>
<div
id=BOX
name=div
>
<p
name=p
>
<content
name=content
>
[3,14][Paragraph 1]
<content
CR=true
name=content
>
[14,15][
]
<p
name=p
>
<content
name=content
>
[15,26][Paragraph 2]
<content
CR=true
name=content
>
[26,27][
]
<bidi root>
<bidi level
bidiLevel=0
>
[0,27][
Paragraph 1
Paragraph 2
]
如果您有兴趣深入研究,这将意味着探索 Swing 解析逻辑中的 HTML 规则。不同标签类型有很多规则 - 您可以在 source 中查看列表.
每个标签在此层次结构中使用一个“Action”类:
例如<p>
是 ParagraphAction
, 和 <head>
是 HeadAction
, 而这两个都是 BlockAction
的类型. <div>
也是直接一个BlockAction
.
A BlockAction
可以添加额外的 <content CR...>
元素,以完成 block ,因此偏移量额外+1。它通常只有在标签中有直接文本内容时才会这样做。对于 <head>
不过,HeadAction
子类添加了 <p-implied>
您可以在上面的转储中看到,这导致了一个额外的偏移量。 (您在本例中看不到它,但值得注意的是带有文本内容的 <div>
还会插入额外的 <p-implied>
- 以保存 block 文本)。
事情从那里开始变得更加具体。例如。 <title>
(连同 <applet>
和 <object>
)似乎是“非空” HiddenActions
.这意味着为开始和结束标记都插入了一个元素。 <meta>
虽然,例如,是一个空的 HiddenAction
, 因此只获取一个元素作为开始标记。
希望这足以解释如何计算任何给定标签的偏移量。如果浏览 XxxActions
的源代码类,寻找类似 new ElementSpec(..., 0, 1)
的行- 最后一个参数是长度。
您还提到空格被忽略。这至少在 HTML 解析中是正常的,在浏览器中也是如此。标签之间或文本前后的空白通常会被忽略——只保留单词之间的空白。然后,空格序列被折叠成一个空格。
总而言之,我仍然不清楚为什么 <head>
需要额外的偏移量和 <title>
.例如。如果你使用 setCaretPosition(x)
针对 JEditorPane
基于 doc
和 htmlKit
在上面,如果 x
,您只会看到插入符号是3个或更多。也许其他人可以阐明这一点......
关于java - HTMLDocument 文本中位置或偏移量的含义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50602095/