Bagaimana cara menghapus entitas karakter HTML di Java?

147

Pada dasarnya saya ingin men-decode dokumen Html yang diberikan, dan mengganti semua karakter khusus, seperti " "-> " ", ">"-> ">".

Dalam. NET kita dapat menggunakan HttpUtility.HtmlDecode.

Apa fungsi yang setara di Jawa?

yinyueyouge
sumber
4
& nbsp; disebut entitas karakter. Mengedit judul.
Eugene Yokota

Jawaban:

182

Saya telah menggunakan Apache Commons StringEscapeUtils.unescapeHtml4 () untuk ini:

Membatalkan penghapusan string yang mengandung entitas melarikan diri ke string yang berisi karakter Unicode aktual yang sesuai dengan lolos. Mendukung entitas HTML 4.0.

Kevin Hakanson
sumber
19
Sayangnya saya baru menyadari hari ini bahwa itu tidak men-decode karakter HTML yang sangat baik :(
Sid
1
trik kotor adalah menyimpan nilai awalnya di bidang tersembunyi untuk menghindarinya, maka bidang target harus mendapatkan nilai dari bidang tersembunyi.
setzamora
2
Kelas StringEscapeUtils tidak digunakan lagi dan dipindahkan ke Apache commons-text
Pauli
2
Saya ingin mengonversi string <p>&uuml;&egrave;</p>menjadi <p>üé</p>, dengan StringEscapeUtils.unescapeHtml4()saya dapatkan &lt;p&gt;üè&lt;/p&gt;. Apakah ada cara untuk menjaga tag html yang ada tetap utuh?
Nickkk
48

Perpustakaan yang disebutkan dalam jawaban lain akan menjadi solusi yang baik, tetapi jika Anda sudah menggali html dunia nyata dalam proyek Anda, Jsoupproyek ini memiliki lebih banyak hal untuk ditawarkan daripada sekadar mengelola "ampersand pound FFFF semicolon" .

// 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);

Dan Anda juga mendapatkan API yang nyaman untuk mengekstraksi dan memanipulasi data, menggunakan yang terbaik dari metode DOM, CSS, dan seperti jquery. Ini open source dan lisensi MIT.

Lembah
sumber
3
upvote +, tapi saya harus menunjukkan bahwa versi Jsoup yang lebih baru digunakan .text()alih-alih.getText()
SourceVisor
4
Mungkin lebih langsung adalah menggunakan org.jsoup.parser.Parser.unescapeEntities(String string, boolean inAttribute). API docs: jsoup.org/apidocs/org/jsoup/parser/…
danneu
3
Ini sempurna, karena saya sudah menggunakan Jsoup dalam proyek saya. @Danneu juga benar - Parser.unescapeEntities bekerja persis seperti yang diiklankan.
MandisaW
42

Saya mencoba Apache Commons StringEscapeUtils.unescapeHtml3 () di proyek saya, tetapi tidak puas dengan kinerjanya. Ternyata, ia melakukan banyak operasi yang tidak perlu. Untuk satu, itu mengalokasikan StringWriter untuk setiap panggilan, bahkan jika tidak ada yang dihapus dalam string. Saya telah menulis ulang kode itu secara berbeda, sekarang bekerja lebih cepat. Siapa pun yang menemukan ini di google dapat menggunakannya.

Kode berikut menghapus semua simbol HTML 3 dan pelarian angka (setara dengan Apache unescapeHtml3). Anda bisa menambahkan lebih banyak entri ke peta jika Anda membutuhkan 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]);
    }

}
Nick Frolov
sumber
Baru-baru ini, saya harus mengoptimalkan proyek Struts yang lambat. Ternyata di bawah penutup Struts memanggil Apache untuk string html lolos secara default ( <s:property value="..."/>). Mematikan melarikan diri ( <s:property value="..." escaping="false"/>) membuat beberapa halaman berjalan 5% hingga 20% lebih cepat.
Stephan
Kemudian saya menemukan bahwa kode ini dapat memasukkan loop ketika diberikan string kosong sebagai argumen. Edisi saat ini memiliki masalah yang diperbaiki.
Nick Frolov
Apakah ini lolos atau hilang? & amp; tidak diterjemahkan. Hanya & ditambahkan ke peta, jadi hanya berfungsi satu arah?
mmm
3
StringWriter menggunakan StringBuffer secara internal yang menggunakan penguncian. Menggunakan StringBuilder secara langsung harus lebih cepat.
Axel Dörfler
4
@NickFrolov, komentar Anda agak kacau. aumlmisalnya ädan tidak д.
aioobe
12

Pustaka berikut ini juga dapat digunakan untuk keluar HTML di Jawa: unbescape .

HTML dapat dihapus dengan cara ini:

final String unescapedText = HtmlEscape.unescapeHtml(escapedText); 
Stephan
sumber
2
Itu tidak melakukan apa pun untuk ini:%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 Teks Anda tidak dikodekan html, itu dikodekan url.
Mikhail Batcer
9

Ini berhasil bagi saya,

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

atau

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

Saya kira itu selalu lebih baik untuk menggunakan lang3alasan yang jelas. Semoga ini membantu :)

tk_
sumber
4

Solusi yang sangat sederhana namun tidak efisien tanpa perpustakaan eksternal adalah:

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;
    }
}

Ini harus digunakan hanya jika Anda hanya memiliki sedikit string untuk diterjemahkan.

Horcrux7
sumber
1
Sangat dekat, tetapi tidak tepat - itu dikonversi "qwAS12ƷƸDžǚǪǼȌ" menjadi "qwAS12ƷƸDžǚǪǼȌ \ n".
Greg
3

Cara yang paling dapat diandalkan adalah dengan

String cleanedString = StringEscapeUtils.unescapeHtml4(originalString);

dari org.apache.commons.lang3.StringEscapeUtils.

Dan untuk melarikan diri dari ruang putih

cleanedString = cleanedString.trim();

Ini akan memastikan bahwa spasi putih karena menyalin dan menempel di formulir web untuk tidak bertahan di DB.

mike oganyan
sumber
1

Kerangka Kerja HtmlUtils

Jika Anda sudah menggunakan kerangka kerja Spring, gunakan metode berikut:

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

...

String result = htmlUnescape(source);
herman
sumber
0

Pertimbangkan untuk menggunakan kelas Java HtmlManipulator . Anda mungkin perlu menambahkan beberapa item (tidak semua entitas ada dalam daftar).

Apache Commons StringEscapeUtils seperti yang disarankan oleh Kevin Hakanson tidak bekerja 100% untuk saya; beberapa entitas seperti & # 145 (kutipan tunggal kiri) entah bagaimana diterjemahkan ke '222'. Saya juga mencoba org.jsoup, dan memiliki masalah yang sama.

Joost
sumber
0

Dalam kasus saya, saya menggunakan metode ganti dengan menguji setiap entitas di setiap variabel, kode saya terlihat seperti ini:

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;", "ú");

Dalam kasus saya ini bekerja dengan sangat baik.

Luiz dev
sumber
2
Ini bukan setiap entitas khusus. Bahkan dua yang disebutkan dalam pertanyaan itu hilang.
Sandy Gifford
ini tidak akan skala dengan baik
denov
-7

Jika Anda ingin meniru apa fungsi php htmlspecialchars_decode tidak menggunakan fungsi php get_html_translation_table () untuk membuang tabel dan kemudian menggunakan kode java seperti,

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;
}
Bala Dutt
sumber
7
Jangan membuang terlalu banyak; gunakan obat generik pada HashMap itu! Juga, gunakan foreach, tidak sebentar untuk mengulanginya, kode akan terlihat jauh lebih mudah dibaca!
WhyNotHugo
3
@BalaDutt jika Anda meningkatkan jawaban Anda, orang-orang akan memberi Anda poin :)
sparkyspider
3
Tingkatkan fungsi & nama variabel Anda juga, @Bala.
Thomas W