Làm cách nào để bỏ các thực thể ký tự HTML trong Java?


147

Về cơ bản, tôi muốn giải mã một tài liệu Html đã cho và thay thế tất cả các ký tự đặc biệt, chẳng hạn như " "-> " ", ">"-> ">".

Trong .NET chúng ta có thể sử dụng HttpUtility.HtmlDecode.

Hàm tương đương trong Java là gì?


4
& nbsp; được gọi là thực thể nhân vật. Chỉnh sửa tiêu đề.
Eugene Yokota

Câu trả lời:


182

Tôi đã sử dụng Apache Commons StringEscapeUtils.unescapeHtml4 () cho việc này:

Hủy bỏ một chuỗi chứa thực thể thoát thành một chuỗi chứa các ký tự Unicode thực tế tương ứng với các thoát. Hỗ trợ các thực thể HTML 4.0.


19
Đáng buồn là hôm nay tôi mới nhận ra rằng nó không giải mã được các ký tự đặc biệt HTML rất tốt :(
Sid

1
một mánh khóe bẩn là lưu trữ giá trị ban đầu trong một trường ẩn để thoát nó, sau đó trường đích sẽ lấy giá trị từ trường ẩn.
setzamora

2
Lớp StringEscapeUtils không được dùng nữa và được chuyển sang văn bản chung của Apache
Pauli

2
Tôi muốn chuyển đổi chuỗi <p>&uuml;&egrave;</p>thành <p>üé</p>, với StringEscapeUtils.unescapeHtml4()tôi nhận được &lt;p&gt;üè&lt;/p&gt;. Có cách nào để giữ nguyên các thẻ html hiện tại không?
Nickkk

48

Các thư viện được đề cập trong các câu trả lời khác sẽ là giải pháp tốt, nhưng nếu bạn đã tình cờ tìm hiểu về html thế giới thực trong dự án của bạn, thì Jsoupdự án có nhiều thứ để cung cấp hơn là chỉ quản lý những thứ "dấu chấm phẩy FFFF" .

// textValue: <p>This is a&nbsp;sample. \"Granny\" Smith &#8211;.<\/p>\r\n
// becomes this: This is a sample. "Granny" Smith –.
// with one line of code:
// Jsoup.parse(textValue).getText(); // for older versions of Jsoup
Jsoup.parse(textValue).text();

// Another possibility may be the static unescapeEntities method:
boolean strictMode = true;
String unescapedString = org.jsoup.parser.Parser.unescapeEntities(textValue, strictMode);

Và bạn cũng có được API thuận tiện để trích xuất và thao tác dữ liệu, sử dụng các phương thức tốt nhất của DOM, CSS và jquery. Đó là mã nguồn mở và giấy phép MIT.


3
upvote +, nhưng tôi nên chỉ ra rằng các phiên bản mới hơn của Jsoup sử dụng .text()thay vì.getText()
SourceVisor

4
Có lẽ trực tiếp hơn là sử dụng org.jsoup.parser.Parser.unescapeEntities(String string, boolean inAttribute). Tài liệu API: jsoup.org/apidocs/org/jsoup/parser/ triệt
danneu

3
Điều này thật hoàn hảo, vì tôi đã sử dụng Jsoup trong dự án của mình. Ngoài ra, @danneu đã đúng - Parser.unescapeEntities hoạt động chính xác như quảng cáo.
MandisaW

42

Tôi đã thử Apache Commons StringEscapeUtils.unescapeHtml3 () trong dự án của mình, nhưng không hài lòng với hiệu suất của nó. Hóa ra, nó làm rất nhiều thao tác không cần thiết. Đối với một, nó phân bổ StringWriter cho mỗi cuộc gọi, ngay cả khi không có gì để unescape trong chuỗi. Tôi đã viết lại mã đó theo cách khác, bây giờ nó hoạt động nhanh hơn nhiều. Bất cứ ai tìm thấy điều này trong google đều được chào đón để sử dụng nó.

Mã theo sau hủy bỏ tất cả các ký hiệu HTML 3 và thoát số (tương đương với Apache unescapeHtml3). Bạn chỉ có thể thêm nhiều mục vào bản đồ nếu bạn cần HTML 4.

package com.example;

import java.io.StringWriter;
import java.util.HashMap;

public class StringUtils {

    public static final String unescapeHtml3(final String input) {
        StringWriter writer = null;
        int len = input.length();
        int i = 1;
        int st = 0;
        while (true) {
            // look for '&'
            while (i < len && input.charAt(i-1) != '&')
                i++;
            if (i >= len)
                break;

            // found '&', look for ';'
            int j = i;
            while (j < len && j < i + MAX_ESCAPE + 1 && input.charAt(j) != ';')
                j++;
            if (j == len || j < i + MIN_ESCAPE || j == i + MAX_ESCAPE + 1) {
                i++;
                continue;
            }

            // found escape 
            if (input.charAt(i) == '#') {
                // numeric escape
                int k = i + 1;
                int radix = 10;

                final char firstChar = input.charAt(k);
                if (firstChar == 'x' || firstChar == 'X') {
                    k++;
                    radix = 16;
                }

                try {
                    int entityValue = Integer.parseInt(input.substring(k, j), radix);

                    if (writer == null) 
                        writer = new StringWriter(input.length());
                    writer.append(input.substring(st, i - 1));

                    if (entityValue > 0xFFFF) {
                        final char[] chrs = Character.toChars(entityValue);
                        writer.write(chrs[0]);
                        writer.write(chrs[1]);
                    } else {
                        writer.write(entityValue);
                    }

                } catch (NumberFormatException ex) { 
                    i++;
                    continue;
                }
            }
            else {
                // named escape
                CharSequence value = lookupMap.get(input.substring(i, j));
                if (value == null) {
                    i++;
                    continue;
                }

                if (writer == null) 
                    writer = new StringWriter(input.length());
                writer.append(input.substring(st, i - 1));

                writer.append(value);
            }

            // skip escape
            st = j + 1;
            i = st;
        }

        if (writer != null) {
            writer.append(input.substring(st, len));
            return writer.toString();
        }
        return input;
    }

    private static final String[][] ESCAPES = {
        {"\"",     "quot"}, // " - double-quote
        {"&",      "amp"}, // & - ampersand
        {"<",      "lt"}, // < - less-than
        {">",      "gt"}, // > - greater-than

        // Mapping to escape ISO-8859-1 characters to their named HTML 3.x equivalents.
        {"\u00A0", "nbsp"}, // non-breaking space
        {"\u00A1", "iexcl"}, // inverted exclamation mark
        {"\u00A2", "cent"}, // cent sign
        {"\u00A3", "pound"}, // pound sign
        {"\u00A4", "curren"}, // currency sign
        {"\u00A5", "yen"}, // yen sign = yuan sign
        {"\u00A6", "brvbar"}, // broken bar = broken vertical bar
        {"\u00A7", "sect"}, // section sign
        {"\u00A8", "uml"}, // diaeresis = spacing diaeresis
        {"\u00A9", "copy"}, // © - copyright sign
        {"\u00AA", "ordf"}, // feminine ordinal indicator
        {"\u00AB", "laquo"}, // left-pointing double angle quotation mark = left pointing guillemet
        {"\u00AC", "not"}, // not sign
        {"\u00AD", "shy"}, // soft hyphen = discretionary hyphen
        {"\u00AE", "reg"}, // ® - registered trademark sign
        {"\u00AF", "macr"}, // macron = spacing macron = overline = APL overbar
        {"\u00B0", "deg"}, // degree sign
        {"\u00B1", "plusmn"}, // plus-minus sign = plus-or-minus sign
        {"\u00B2", "sup2"}, // superscript two = superscript digit two = squared
        {"\u00B3", "sup3"}, // superscript three = superscript digit three = cubed
        {"\u00B4", "acute"}, // acute accent = spacing acute
        {"\u00B5", "micro"}, // micro sign
        {"\u00B6", "para"}, // pilcrow sign = paragraph sign
        {"\u00B7", "middot"}, // middle dot = Georgian comma = Greek middle dot
        {"\u00B8", "cedil"}, // cedilla = spacing cedilla
        {"\u00B9", "sup1"}, // superscript one = superscript digit one
        {"\u00BA", "ordm"}, // masculine ordinal indicator
        {"\u00BB", "raquo"}, // right-pointing double angle quotation mark = right pointing guillemet
        {"\u00BC", "frac14"}, // vulgar fraction one quarter = fraction one quarter
        {"\u00BD", "frac12"}, // vulgar fraction one half = fraction one half
        {"\u00BE", "frac34"}, // vulgar fraction three quarters = fraction three quarters
        {"\u00BF", "iquest"}, // inverted question mark = turned question mark
        {"\u00C0", "Agrave"}, // А - uppercase A, grave accent
        {"\u00C1", "Aacute"}, // Б - uppercase A, acute accent
        {"\u00C2", "Acirc"}, // В - uppercase A, circumflex accent
        {"\u00C3", "Atilde"}, // Г - uppercase A, tilde
        {"\u00C4", "Auml"}, // Д - uppercase A, umlaut
        {"\u00C5", "Aring"}, // Е - uppercase A, ring
        {"\u00C6", "AElig"}, // Ж - uppercase AE
        {"\u00C7", "Ccedil"}, // З - uppercase C, cedilla
        {"\u00C8", "Egrave"}, // И - uppercase E, grave accent
        {"\u00C9", "Eacute"}, // Й - uppercase E, acute accent
        {"\u00CA", "Ecirc"}, // К - uppercase E, circumflex accent
        {"\u00CB", "Euml"}, // Л - uppercase E, umlaut
        {"\u00CC", "Igrave"}, // М - uppercase I, grave accent
        {"\u00CD", "Iacute"}, // Н - uppercase I, acute accent
        {"\u00CE", "Icirc"}, // О - uppercase I, circumflex accent
        {"\u00CF", "Iuml"}, // П - uppercase I, umlaut
        {"\u00D0", "ETH"}, // Р - uppercase Eth, Icelandic
        {"\u00D1", "Ntilde"}, // С - uppercase N, tilde
        {"\u00D2", "Ograve"}, // Т - uppercase O, grave accent
        {"\u00D3", "Oacute"}, // У - uppercase O, acute accent
        {"\u00D4", "Ocirc"}, // Ф - uppercase O, circumflex accent
        {"\u00D5", "Otilde"}, // Х - uppercase O, tilde
        {"\u00D6", "Ouml"}, // Ц - uppercase O, umlaut
        {"\u00D7", "times"}, // multiplication sign
        {"\u00D8", "Oslash"}, // Ш - uppercase O, slash
        {"\u00D9", "Ugrave"}, // Щ - uppercase U, grave accent
        {"\u00DA", "Uacute"}, // Ъ - uppercase U, acute accent
        {"\u00DB", "Ucirc"}, // Ы - uppercase U, circumflex accent
        {"\u00DC", "Uuml"}, // Ь - uppercase U, umlaut
        {"\u00DD", "Yacute"}, // Э - uppercase Y, acute accent
        {"\u00DE", "THORN"}, // Ю - uppercase THORN, Icelandic
        {"\u00DF", "szlig"}, // Я - lowercase sharps, German
        {"\u00E0", "agrave"}, // а - lowercase a, grave accent
        {"\u00E1", "aacute"}, // б - lowercase a, acute accent
        {"\u00E2", "acirc"}, // в - lowercase a, circumflex accent
        {"\u00E3", "atilde"}, // г - lowercase a, tilde
        {"\u00E4", "auml"}, // д - lowercase a, umlaut
        {"\u00E5", "aring"}, // е - lowercase a, ring
        {"\u00E6", "aelig"}, // ж - lowercase ae
        {"\u00E7", "ccedil"}, // з - lowercase c, cedilla
        {"\u00E8", "egrave"}, // и - lowercase e, grave accent
        {"\u00E9", "eacute"}, // й - lowercase e, acute accent
        {"\u00EA", "ecirc"}, // к - lowercase e, circumflex accent
        {"\u00EB", "euml"}, // л - lowercase e, umlaut
        {"\u00EC", "igrave"}, // м - lowercase i, grave accent
        {"\u00ED", "iacute"}, // н - lowercase i, acute accent
        {"\u00EE", "icirc"}, // о - lowercase i, circumflex accent
        {"\u00EF", "iuml"}, // п - lowercase i, umlaut
        {"\u00F0", "eth"}, // р - lowercase eth, Icelandic
        {"\u00F1", "ntilde"}, // с - lowercase n, tilde
        {"\u00F2", "ograve"}, // т - lowercase o, grave accent
        {"\u00F3", "oacute"}, // у - lowercase o, acute accent
        {"\u00F4", "ocirc"}, // ф - lowercase o, circumflex accent
        {"\u00F5", "otilde"}, // х - lowercase o, tilde
        {"\u00F6", "ouml"}, // ц - lowercase o, umlaut
        {"\u00F7", "divide"}, // division sign
        {"\u00F8", "oslash"}, // ш - lowercase o, slash
        {"\u00F9", "ugrave"}, // щ - lowercase u, grave accent
        {"\u00FA", "uacute"}, // ъ - lowercase u, acute accent
        {"\u00FB", "ucirc"}, // ы - lowercase u, circumflex accent
        {"\u00FC", "uuml"}, // ь - lowercase u, umlaut
        {"\u00FD", "yacute"}, // э - lowercase y, acute accent
        {"\u00FE", "thorn"}, // ю - lowercase thorn, Icelandic
        {"\u00FF", "yuml"}, // я - lowercase y, umlaut
    };

    private static final int MIN_ESCAPE = 2;
    private static final int MAX_ESCAPE = 6;

    private static final HashMap<String, CharSequence> lookupMap;
    static {
        lookupMap = new HashMap<String, CharSequence>();
        for (final CharSequence[] seq : ESCAPES) 
            lookupMap.put(seq[1].toString(), seq[0]);
    }

}

Gần đây, tôi phải tối ưu hóa một dự án Struts chậm. Hóa ra, dưới vỏ bọc, Struts gọi Apache cho chuỗi html thoát theo mặc định ( <s:property value="..."/>). Tắt thoát ( <s:property value="..." escaping="false"/>) có một số trang để chạy nhanh hơn 5% đến 20%.
Stephan

Sau đó tôi phát hiện ra rằng mã này có thể nhập vòng lặp khi đưa ra chuỗi rỗng làm đối số. Phiên bản hiện tại đã khắc phục vấn đề đó.
Nick Frolov

Liệu điều này thoát hay không dấu vết? & amp; không được giải mã. Chỉ & được thêm vào bản đồ, vì vậy nó chỉ hoạt động một chiều?
mmm

3
StringWriter sử dụng StringBuffer bên trong sử dụng khóa. Sử dụng StringBuilder trực tiếp sẽ nhanh hơn.
Axel Dörfler 22/2/2016

4
@NickFrolov, bình luận của bạn có vẻ hơi lộn xộn. aumllà ví dụ ävà không д.
aioobe

12

Thư viện sau đây cũng có thể được sử dụng để thoát HTML trong Java: unbescape .

HTML có thể được bỏ qua theo cách này:

final String unescapedText = HtmlEscape.unescapeHtml(escapedText); 

2
Nó không làm gì với điều này:%3Chtml%3E%0D%0A%3Chead%3E%0D%0A%3Ctitle%3Etest%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%3E%0D%0Atest%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E
ThreaT

40
@ThreaT Văn bản của bạn không được mã hóa html, nó được mã hóa url.
Mikhail Batcer

9

Điều này đã làm công việc cho tôi,

import org.apache.commons.lang.StringEscapeUtils;
...
String decodedXML= StringEscapeUtils.unescapeHtml(encodedXML);

hoặc là

import org.apache.commons.lang3.StringEscapeUtils;
...
String decodedXML= StringEscapeUtils.unescapeHtml4(encodedXML);

Tôi đoán nó luôn luôn tốt hơn để sử dụng lang3cho các lý do rõ ràng. Hi vọng điêu nay co ich :)


4

Một giải pháp rất đơn giản nhưng không hiệu quả mà không cần bất kỳ thư viện bên ngoài nào là:

public static String unescapeHtml3( String str ) {
    try {
        HTMLDocument doc = new HTMLDocument();
        new HTMLEditorKit().read( new StringReader( "<html><body>" + str ), doc, 0 );
        return doc.getText( 1, doc.getLength() );
    } catch( Exception ex ) {
        return str;
    }
}

Điều này chỉ nên được sử dụng nếu bạn chỉ có một số lượng nhỏ chuỗi để giải mã.


1
Rất gần, nhưng không chính xác - nó đã chuyển đổi "qwAS12ƷƸDžǚǪǼȌ" thành "qwAS12ƷƸDžǚǪǼȌ \ n".
Greg

3

Cách đáng tin cậy nhất là với

String cleanedString = StringEscapeUtils.unescapeHtml4(originalString);

từ org.apache.commons.lang3.StringEscapeUtils.

Và để thoát khỏi khoảng trắng

cleanedString = cleanedString.trim();

Điều này sẽ đảm bảo rằng các khoảng trắng do sao chép và dán vào các biểu mẫu web để không bị tồn tại trong DB.


1

Spring Framework HtmlUtils

Nếu bạn đã sử dụng Spring framework, hãy sử dụng phương pháp sau:

import static org.springframework.web.util.HtmlUtils.htmlUnescape;

...

String result = htmlUnescape(source);

0

Xem xét sử dụng lớp Java HtmlManipulator . Bạn có thể cần thêm một số mục (không phải tất cả các thực thể đều có trong danh sách).

StringEscapeUtils của Apache như được đề xuất bởi Kevin Hakanson đã không hoạt động 100% với tôi; một số thực thể như & # 145 (trích dẫn đơn bên trái) đã được dịch thành '222' bằng cách nào đó. Tôi cũng đã thử org.jsoup và gặp vấn đề tương tự.


0

Trong trường hợp của tôi, tôi sử dụng phương thức thay thế bằng cách kiểm tra mọi thực thể trong mọi biến, mã của tôi trông như thế này:

text = text.replace("&Ccedil;", "Ç");
text = text.replace("&ccedil;", "ç");
text = text.replace("&Aacute;", "Á");
text = text.replace("&Acirc;", "Â");
text = text.replace("&Atilde;", "Ã");
text = text.replace("&Eacute;", "É");
text = text.replace("&Ecirc;", "Ê");
text = text.replace("&Iacute;", "Í");
text = text.replace("&Ocirc;", "Ô");
text = text.replace("&Otilde;", "Õ");
text = text.replace("&Oacute;", "Ó");
text = text.replace("&Uacute;", "Ú");
text = text.replace("&aacute;", "á");
text = text.replace("&acirc;", "â");
text = text.replace("&atilde;", "ã");
text = text.replace("&eacute;", "é");
text = text.replace("&ecirc;", "ê");
text = text.replace("&iacute;", "í");
text = text.replace("&ocirc;", "ô");
text = text.replace("&otilde;", "õ");
text = text.replace("&oacute;", "ó");
text = text.replace("&uacute;", "ú");

Trong trường hợp của tôi, điều này làm việc rất tốt.


2
Đây không phải là mỗi thực thể đặc biệt. Ngay cả hai đề cập trong câu hỏi cũng bị thiếu.
Sandy Gifford

điều này sẽ không có quy mô tốt
denov

-7

Trong trường hợp bạn muốn bắt chước chức năng php htmlspecialchars_decode nào sử dụng hàm php get_html_translation_table () để kết xuất bảng và sau đó sử dụng mã java như,

static Map<String,String> html_specialchars_table = new Hashtable<String,String>();
static {
        html_specialchars_table.put("&lt;","<");
        html_specialchars_table.put("&gt;",">");
        html_specialchars_table.put("&amp;","&");
}
static String htmlspecialchars_decode_ENT_NOQUOTES(String s){
        Enumeration en = html_specialchars_table.keys();
        while(en.hasMoreElements()){
                String key = en.nextElement();
                String val = html_specialchars_table.get(key);
                s = s.replaceAll(key, val);
        }
        return s;
}

7
Đừng bỏ quá nhiều; sử dụng thuốc generic trên HashMap đó! Ngoài ra, sử dụng một foreach, không phải một lúc để lặp lại điều đó, mã sẽ trông dễ đọc hơn!
WhyNotHugo

3
@BalaDutt nếu bạn cải thiện câu trả lời của mình, các bạn sẽ cho bạn điểm :)
sparkyspider

3
Cải thiện chức năng và tên biến của bạn quá, @Bala.
Thomas W
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.