从String中删除HTML标记

Mas*_*son 408 html java parsing

有没有一种从Java字符串中删除HTML的好方法?一个简单的正则表达式

 replaceAll("\\<.*?>","") 
Run Code Online (Sandbox Code Playgroud)

会工作,但&amp;不会正确转换,两个尖括号之间的非HTML将被删除(即.*?正则表达式将消失).

Bal*_*usC 547

使用HTML解析器而不是正则表达式.使用Jsoup这很简单.

public static String html2text(String html) {
    return Jsoup.parse(html).text();
}
Run Code Online (Sandbox Code Playgroud)

Jsoup还支持对可定制的白名单,如果你希望只允许例如这是非常有用的去除HTML标签<b>,<i><u>.

也可以看看:

  • @Zeroows:这对于Lorem ipsum 1 <3 dolor sit amet </ p>来悲惨地失败了.同样,[HTML不是常规语言](http://stackoverflow.com/a/1732454/157882).这完全超出了我的原因,为什么每个人都在试图抛出正则表达式来解析感兴趣的部分,而不是使用真正的解析器. (19认同)
  • Jsoup很好,但我遇到了一些缺点.我用它来摆脱XSS,所以基本上我期望一个纯文本输入,但一些邪恶的人可能会试图给我发一些HTML.使用Jsoup,我可以删除所有HTML,但不幸的是,它还会将许多空格缩小为一个并删除链接中断(\n字符) (17认同)
  • @Ridcully:因为你想使用[`Jsoup#clean()`](http://jsoup.org/cookbook/cleaning-html/whitelist-sanitizer). (7认同)
  • 使用`Jsoup.clean(unsafeString,"",Whitelist.none(),new OutputSettings().prettyPrint(false));`来保留换行符 (4认同)
  • 使用clean()仍然会导致额外的空格和\n字符被删除.例如:Jsoup.clean("a \n b",Whitelist.none())返回"a b" (3认同)
  • @Nels:你在谈论“Jsoup#clean()”?当然是。单击我之前评论中的“Jsoup#clean()”链接。 (2认同)
  • 编译这个:编译 'org.jsoup:jsoup:1.9.2' (2认同)
  • 这可能会更好,但它会给您的项目引入另一个依赖项,这不好 (2认同)
  • @FrankKrumnow:“Html.fromHtml(..).toString()”在标准 Java 中不可用,也不能作为标准 Java 库使用。它仅适用于 Android。 (2认同)

Ken*_*dge 267

如果你正在为Android写作,你可以这样做......

android.text.Html.fromHtml(instruction).toString()
Run Code Online (Sandbox Code Playgroud)

  • 很棒的提示.:)如果您在TextView中显示文本,则可以删除.toString()以保留一些格式. (12认同)
  • 这很好,但是 &lt;img&gt; 标签被一些奇怪的东西所取代。我得到了有图像的小方块 (2认同)

Chr*_*org 82

如果用户输入<b>hey!</b>,您要显示<b>hey!</b>还是hey!?如果第一个,逃避少数,和html编码&符号(和可选的引号),你很好.对代码实施第二个选项的修改是:

replaceAll("\\<[^>]*>","")
Run Code Online (Sandbox Code Playgroud)

但如果用户输入格式错误的内容,您将遇到问题<bhey!</b>.

您还可以查看将解析"脏"html输入的JTidy,并且应该为您提供一种删除标记的方法,保留文本.

尝试剥离html的问题是浏览器具有非常宽松的解析器,比你能找到的任何库都宽松,所以即使你尽力剥离所有标签(使用上面的替换方法,DOM库或JTidy) ,您仍然需要确保编码任何剩余的HTML特殊字符,以确保您的输出安全.

  • 如果 html 节点内容中存在未转义的 &lt; 或 &gt; 符号,您还会遇到问题。&lt;span&gt;我的年龄是&lt;很多文字&gt;然后是你的年龄&lt;/span&gt;。我认为只有 100% 的方法是通过一些 XML DOM 接口(如 SAX 或类似的)来使用 node.getText()。 (2认同)

Rea*_*wTo 28

另一种方法是使用 javax.swing.text.html.HTMLEditorKit来提取文本.

import java.io.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;

public class Html2Text extends HTMLEditorKit.ParserCallback {
    StringBuffer s;

    public Html2Text() {
    }

    public void parse(Reader in) throws IOException {
        s = new StringBuffer();
        ParserDelegator delegator = new ParserDelegator();
        // the third parameter is TRUE to ignore charset directive
        delegator.parse(in, this, Boolean.TRUE);
    }

    public void handleText(char[] text, int pos) {
        s.append(text);
    }

    public String getText() {
        return s.toString();
    }

    public static void main(String[] args) {
        try {
            // the HTML to convert
            FileReader in = new FileReader("java-new.html");
            Html2Text parser = new Html2Text();
            parser.parse(in);
            in.close();
            System.out.println(parser.getText());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ref:从文件中删除HTML标记以仅提取TEXT

  • "a <b或b> c"的结果是"ab或b> c",这看起来很不幸. (5认同)
  • 这对我来说效果最好。我需要保留换行符。我通过将这个简单的方法添加到解析器中来实现: @Override public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) { if (t == HTML.Tag.P || t == HTML.Tag.BR) { s.append('\n'); } } (2认同)
  • dfrankow:数学表达式 a &lt; b 或 b &gt; c 在 html 中应该这样写:a &lt; b 或 b &gt; c b或b>C (2认同)

Ser*_*rge 24

我认为过滤html标签的最简单方法是:

private static final Pattern REMOVE_TAGS = Pattern.compile("<.+?>");

public static String removeTags(String string) {
    if (string == null || string.length() == 0) {
        return string;
    }

    Matcher m = REMOVE_TAGS.matcher(string);
    return m.replaceAll("");
}
Run Code Online (Sandbox Code Playgroud)


小智 18

使用Jericho也很简单,你可以保留一些格式(例如换行符和链接).

    Source htmlSource = new Source(htmlText);
    Segment htmlSeg = new Segment(htmlSource, 0, htmlSource.length());
    Renderer htmlRend = new Renderer(htmlSeg);
    System.out.println(htmlRend.toString());
Run Code Online (Sandbox Code Playgroud)

  • 杰里科能够解析一下换行符.Jsoup和HTMLEditorKit无法做到这一点. (4认同)
  • 杰里科的工作就像一个魅力.谢谢你的建议.一个注意事项:您不必创建整个字符串的Segment.Source扩展Segment,因此要么在Renderer构造函数中工作. (3认同)

Dam*_*ien 15

接受的答案Jsoup.parse(html).text()只有两个潜在的问题(使用JSoup 1.7.3):

  • 它从文本中删除换行符
  • 它将文本&lt;script&gt;转换为<script>

如果您使用它来防止XSS,这有点烦人.以下是使用JSoup和Apache StringEscapeUtils改进的解决方案的最佳镜头:

// breaks multi-level of escaping, preventing &amp;lt;script&amp;gt; to be rendered as <script>
String replace = input.replace("&amp;", "");
// decode any encoded html, preventing &lt;script&gt; to be rendered as <script>
String html = StringEscapeUtils.unescapeHtml(replace);
// remove all html tags, but maintain line breaks
String clean = Jsoup.clean(html, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false));
// decode html again to convert character entities back into text
return StringEscapeUtils.unescapeHtml(clean);
Run Code Online (Sandbox Code Playgroud)

请注意,最后一步是因为我需要将输出用作纯文本.如果您只需要HTML输出,那么您应该能够删除它.

这里有一堆测试用例(输入到输出):

{"regular string", "regular string"},
{"<a href=\"link\">A link</a>", "A link"},
{"<script src=\"http://evil.url.com\"/>", ""},
{"&lt;script&gt;", ""},
{"&amp;lt;script&amp;gt;", "lt;scriptgt;"}, // best effort
{"\" ' > < \n \\ é å à ü and & preserved", "\" ' > < \n \\ é å à ü and & preserved"}
Run Code Online (Sandbox Code Playgroud)

如果您找到了改善它的方法,请告诉我.

  • 这将导致类似“&#38; ltlt; script&#38; lt; / script&#38; gt;”的操作失败。&#x26;也是如此。JSoup不会转换`&lt; script&gt; 放入&lt;script&gt;中,是因为在JSoup清除输入后调用了StringEscapeUtils.unescapeHtml。 (2认同)

Ame*_*een 15

在Android上,试试这个:

String result = Html.fromHtml(html).toString();
Run Code Online (Sandbox Code Playgroud)

  • 惊人的。删除了所有 html 标签。 (2认同)
  • 看起来很熟悉,就像我 2011 年的回答一样。 (2认同)

Tim*_*and 12

HTML Escaping真的很难做 - 我肯定建议使用库代码来做到这一点,因为它比你想象的要复杂得多.查看Apache的StringEscapeUtils,找到一个非常好的库,用于在Java中处理它.

  • 关于utils用于unescaping但没有回答问题的好信息. (5认同)
  • StringEscapeUtils.unescapeHtml不会删除html (4认同)
  • 令人困惑的答案.删除!= Unescaping (3认同)

小智 9

您可以简单地使用 Android 的默认 HTML 过滤器

    public String htmlToStringFilter(String textToFilter){

    return Html.fromHtml(textToFilter).toString();

    }
Run Code Online (Sandbox Code Playgroud)

上述方法将为您的输入返回经过 HTML 过滤的字符串。


San*_*699 7

这应该工作-

用这个

  text.replaceAll('<.*?>' , " ") -> This will replace all the html tags with a space.
Run Code Online (Sandbox Code Playgroud)

和这个

  text.replaceAll('&.*?;' , "")-> this will replace all the tags which starts with "&" and ends with ";" like &nbsp;, &amp;, &gt; etc.
Run Code Online (Sandbox Code Playgroud)


fox*_*oxy 6

你可能想要在剥离HTML之前用换行符替换<br/></p>标记,以防止它成为一个难以辨认的混乱,如蒂姆建议的那样.

我可以考虑删除HTML标记但将非HTML放在尖括号之间的唯一方法是检查HTML标记列表.沿着这些方向......

replaceAll("\\<[\s]*tag[^>]*>","")
Run Code Online (Sandbox Code Playgroud)

然后HTML解码特殊字符如&amp;.结果不应被视为消毒.


小智 5

这是一个稍微更充实的更新,尝试处理中断和列表的一些格式。我使用 Amaya 的输出作为指导。

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Stack;
import java.util.logging.Logger;

import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.parser.ParserDelegator;

public class HTML2Text extends HTMLEditorKit.ParserCallback {
    private static final Logger log = Logger
            .getLogger(Logger.GLOBAL_LOGGER_NAME);

    private StringBuffer stringBuffer;

    private Stack<IndexType> indentStack;

    public static class IndexType {
        public String type;
        public int counter; // used for ordered lists

        public IndexType(String type) {
            this.type = type;
            counter = 0;
        }
    }

    public HTML2Text() {
        stringBuffer = new StringBuffer();
        indentStack = new Stack<IndexType>();
    }

    public static String convert(String html) {
        HTML2Text parser = new HTML2Text();
        Reader in = new StringReader(html);
        try {
            // the HTML to convert
            parser.parse(in);
        } catch (Exception e) {
            log.severe(e.getMessage());
        } finally {
            try {
                in.close();
            } catch (IOException ioe) {
                // this should never happen
            }
        }
        return parser.getText();
    }

    public void parse(Reader in) throws IOException {
        ParserDelegator delegator = new ParserDelegator();
        // the third parameter is TRUE to ignore charset directive
        delegator.parse(in, this, Boolean.TRUE);
    }

    public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
        log.info("StartTag:" + t.toString());
        if (t.toString().equals("p")) {
            if (stringBuffer.length() > 0
                    && !stringBuffer.substring(stringBuffer.length() - 1)
                            .equals("\n")) {
                newLine();
            }
            newLine();
        } else if (t.toString().equals("ol")) {
            indentStack.push(new IndexType("ol"));
            newLine();
        } else if (t.toString().equals("ul")) {
            indentStack.push(new IndexType("ul"));
            newLine();
        } else if (t.toString().equals("li")) {
            IndexType parent = indentStack.peek();
            if (parent.type.equals("ol")) {
                String numberString = "" + (++parent.counter) + ".";
                stringBuffer.append(numberString);
                for (int i = 0; i < (4 - numberString.length()); i++) {
                    stringBuffer.append(" ");
                }
            } else {
                stringBuffer.append("*   ");
            }
            indentStack.push(new IndexType("li"));
        } else if (t.toString().equals("dl")) {
            newLine();
        } else if (t.toString().equals("dt")) {
            newLine();
        } else if (t.toString().equals("dd")) {
            indentStack.push(new IndexType("dd"));
            newLine();
        }
    }

    private void newLine() {
        stringBuffer.append("\n");
        for (int i = 0; i < indentStack.size(); i++) {
            stringBuffer.append("    ");
        }
    }

    public void handleEndTag(HTML.Tag t, int pos) {
        log.info("EndTag:" + t.toString());
        if (t.toString().equals("p")) {
            newLine();
        } else if (t.toString().equals("ol")) {
            indentStack.pop();
            ;
            newLine();
        } else if (t.toString().equals("ul")) {
            indentStack.pop();
            ;
            newLine();
        } else if (t.toString().equals("li")) {
            indentStack.pop();
            ;
            newLine();
        } else if (t.toString().equals("dd")) {
            indentStack.pop();
            ;
        }
    }

    public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
        log.info("SimpleTag:" + t.toString());
        if (t.toString().equals("br")) {
            newLine();
        }
    }

    public void handleText(char[] text, int pos) {
        log.info("Text:" + new String(text));
        stringBuffer.append(text);
    }

    public String getText() {
        return stringBuffer.toString();
    }

    public static void main(String args[]) {
        String html = "<html><body><p>paragraph at start</p>hello<br />What is happening?<p>this is a<br />mutiline paragraph</p><ol>  <li>This</li>  <li>is</li>  <li>an</li>  <li>ordered</li>  <li>list    <p>with</p>    <ul>      <li>another</li>      <li>list        <dl>          <dt>This</dt>          <dt>is</dt>            <dd>sdasd</dd>            <dd>sdasda</dd>            <dd>asda              <p>aasdas</p>            </dd>            <dd>sdada</dd>          <dt>fsdfsdfsd</dt>        </dl>        <dl>          <dt>vbcvcvbcvb</dt>          <dt>cvbcvbc</dt>            <dd>vbcbcvbcvb</dd>          <dt>cvbcv</dt>          <dt></dt>        </dl>        <dl>          <dt></dt>        </dl></li>      <li>cool</li>    </ul>    <p>stuff</p>  </li>  <li>cool</li></ol><p></p></body></html>";
        System.out.println(convert(html));
    }
}
Run Code Online (Sandbox Code Playgroud)


rjh*_*a94 5

另一种方法是使用 com.google.gdata.util.common.html.HtmlToText 类,例如

MyWriter.toConsole(HtmlToText.htmlToPlainText(htmlResponse));
Run Code Online (Sandbox Code Playgroud)

但这不是防弹代码,当我在维基百科条目上运行它时,我也得到了样式信息。不过我相信对于小型/简单的工作来说这会是有效的。


dfr*_*kow 5

对于我指出的测试用例,接受的答案对我不起作用:“a < b 或 b > c”的结果是“ab 或 b > c”。

所以,我用 TagSoup 来代替。这是适用于我的测试用例(以及其他几个)的镜头:

import java.io.IOException;
import java.io.StringReader;
import java.util.logging.Logger;

import org.ccil.cowan.tagsoup.Parser;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
 * Take HTML and give back the text part while dropping the HTML tags.
 *
 * There is some risk that using TagSoup means we'll permute non-HTML text.
 * However, it seems to work the best so far in test cases.
 *
 * @author dan
 * @see <a href="http://home.ccil.org/~cowan/XML/tagsoup/">TagSoup</a> 
 */
public class Html2Text2 implements ContentHandler {
private StringBuffer sb;

public Html2Text2() {
}

public void parse(String str) throws IOException, SAXException {
    XMLReader reader = new Parser();
    reader.setContentHandler(this);
    sb = new StringBuffer();
    reader.parse(new InputSource(new StringReader(str)));
}

public String getText() {
    return sb.toString();
}

@Override
public void characters(char[] ch, int start, int length)
    throws SAXException {
    for (int idx = 0; idx < length; idx++) {
    sb.append(ch[idx+start]);
    }
}

@Override
public void ignorableWhitespace(char[] ch, int start, int length)
    throws SAXException {
    sb.append(ch);
}

// The methods below do not contribute to the text
@Override
public void endDocument() throws SAXException {
}

@Override
public void endElement(String uri, String localName, String qName)
    throws SAXException {
}

@Override
public void endPrefixMapping(String prefix) throws SAXException {
}


@Override
public void processingInstruction(String target, String data)
    throws SAXException {
}

@Override
public void setDocumentLocator(Locator locator) {
}

@Override
public void skippedEntity(String name) throws SAXException {
}

@Override
public void startDocument() throws SAXException {
}

@Override
public void startElement(String uri, String localName, String qName,
    Attributes atts) throws SAXException {
}

@Override
public void startPrefixMapping(String prefix, String uri)
    throws SAXException {
}
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*han 5

或者,可以使用HtmlCleaner

private CharSequence removeHtmlFrom(String html) {
    return new HtmlCleaner().clean(html).getText();
}
Run Code Online (Sandbox Code Playgroud)

  • HtmlCleaner 运行良好,保持换行,并有最新版本(2017 年 5 月的 2.21)。 (2认同)

Int*_*iya 5

Html.fromHtml

HTML标签是

<a href=”…”> <b>,  <big>, <blockquote>, <br>, <cite>, <dfn>
<div align=”…”>,  <em>, <font size=”…” color=”…” face=”…”>
<h1>,  <h2>, <h3>, <h4>,  <h5>, <h6>
<i>, <p>, <small>
<strike>,  <strong>, <sub>, <sup>, <tt>, <u>
Run Code Online (Sandbox Code Playgroud)

根据Android 的官方文档HTML 中的任何标签都将显示为通用替换字符串,然后您的程序可以通过该字符串替换为真正的字符串

Html.formHtml方法将一个Html.TagHandler和一个 Html.ImageGetter 作为参数以及要解析的文本。

例子

String Str_Html=" <p>This is about me text that the user can put into their profile</p> ";
Run Code Online (Sandbox Code Playgroud)

然后

Your_TextView_Obj.setText(Html.fromHtml(Str_Html).toString());
Run Code Online (Sandbox Code Playgroud)

输出

这是关于我的文本,用户可以将其放入他们的个人资料中


sil*_*udo 5

这是如何替换全部的另一种变体(HTML 标签 | HTML 实体 | HTML 内容中的空白)

content.replaceAll("(<.*?>)|(&.*?;)|([ ]{2,})", "");其中内容是一个字符串。