【发布时间】:2012-01-26 15:09:45
【问题描述】:
我想做什么...
我的 android 应用中有一个 webview。我从服务器获得一个巨大的 html 内容作为字符串和来自应用程序用户(android 手机用户)的搜索字符串。现在我打破搜索字符串并从中创建一个正则表达式。我希望在将与我的正则表达式匹配的所有 html 内容显示到我的 WebView 时突出显示它。
我尝试了什么...
由于是html,我只想将正则表达式匹配的单词包装成一对黄色背景的标签。
- 我得到的 html 内容上的简单正则表达式和 replaceAll。非常错误,因为它甚至会替换 '' 中的内容。
- 我尝试使用 Matcher 和 Pattern 组合。很难省略标签内的内容。
- 我使用了 JSOUP 解析器,它成功了!
我使用 NodeTraversor 类遍历 html。我使用 Matcher 和 Pattern 类来查找匹配的单词并将其替换为我想做的标记。
但是速度很慢。我基本上想在Android上使用它,它的大小是284kB。我删除了一些不需要的类,现在是 201kB,但对于 android 设备来说仍然太多了。此外,html 内容可能非常大。我也查看了 JSoup 源代码。它在解析时会迭代每个字符。我不知道所有解析器是否都这样做,但对于大型 html 文档来说肯定很慢。
这是我的代码 -
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Highlighter {
private String regex;
private String htmlContent;
Pattern pat;
Matcher mat;
public Highlighter(String searchString, String htmlString) {
regex = buildRegexFromQuery(searchString);
htmlContent = htmlString;
pat = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
}
public String getHighlightedHtml() {
Document doc = Jsoup.parse(htmlContent);
final List<TextNode> nodesToChange = new ArrayList<TextNode>();
NodeTraversor nd = new NodeTraversor(new NodeVisitor() {
@Override
public void tail(Node node, int depth) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String text = textNode.getWholeText();
mat = pat.matcher(text);
if(mat.find()) {
nodesToChange.add(textNode);
}
}
}
@Override
public void head(Node node, int depth) {
}
});
nd.traverse(doc.body());
for (TextNode textNode : nodesToChange) {
Node newNode = buildElementForText(textNode);
textNode.replaceWith(newNode);
}
return doc.toString();
}
private static String buildRegexFromQuery(String queryString) {
String regex = "";
String queryToConvert = queryString;
/* Clean up query */
queryToConvert = queryToConvert.replaceAll("[\\p{Punct}]*", " ");
queryToConvert = queryToConvert.replaceAll("[\\s]*", " ");
String[] regexArray = queryString.split(" ");
regex = "(";
for(int i = 0; i < regexArray.length - 1; i++) {
String item = regexArray[i];
regex += "(\\b)" + item + "(\\b)|";
}
regex += "(\\b)" + regexArray[regexArray.length - 1] + "[a-zA-Z0-9]*?(\\b))";
return regex;
}
private Node buildElementForText(TextNode textNode) {
String text = textNode.getWholeText().trim();
ArrayList<MatchedWord> matchedWordSet = new ArrayList<MatchedWord>();
mat = pat.matcher(text);
while(mat.find()) {
matchedWordSet.add(new MatchedWord(mat.start(), mat.end()));
}
StringBuffer newText = new StringBuffer(text);
for(int i = matchedWordSet.size() - 1; i >= 0; i-- ) {
String wordToReplace = newText.substring(matchedWordSet.get(i).start, matchedWordSet.get(i).end);
wordToReplace = "<b>" + wordToReplace+ "</b>";
newText = newText.replace(matchedWordSet.get(i).start, matchedWordSet.get(i).end, wordToReplace);
}
return new DataNode(newText.toString(), textNode.baseUri());
}
class MatchedWord {
public int start;
public int end;
public MatchedWord(int start, int end) {
this.start = start;
this.end = end;
}
}
}
我是这样称呼它的 -
htmlString = getHtmlFromServer();
Highlighter hl = new Highlighter("Hello World!", htmlString);
new htmlString = hl.getHighlightedHTML();
我确信我正在做的不是最理想的方式。但我似乎想不出别的了。
我想 - 减少突出显示它所需的时间。 - 减少库的大小
有什么建议吗?
【问题讨论】:
标签: android html html-parsing jsoup