Java'da HTML karakter varlıklarının görünümünü nasıl kaldırabilirim?


147

Temelde belirli bir Html belgesinin kodunu çözmek ve " "-> " ", ">"-> gibi tüm özel karakterleri değiştirmek istiyorum ">".

. NET'te kullanabiliriz HttpUtility.HtmlDecode.

Java'da eşdeğer işlev nedir?


4
& nbsp; karakter varlığı denir. Başlığı düzenledi.
Eugene Yokota

Yanıtlar:


182

Bunun için Apache Commons StringEscapeUtils.unescapeHtml4 () kullandım:

Varlık içeren bir dizenin çıkışını, çıkış karakterlerine karşılık gelen gerçek Unicode karakterlerini içeren bir dizeye çıkarır. HTML 4.0 varlıklarını destekler.


19
Ne yazık ki bugün HTMLspecial karakterleri çok iyi kod çözmediğini fark ettim :(
Sid

1
Kirli bir hile, değeri başlangıçta gizli bir alanda saklamaktır, daha sonra hedef alanın değeri gizli alandan alması gerekir.
setzamora

2
Sınıf StringEscapeUtils kullanımdan kaldırıldı ve Apache commons-
Pauli

2
Ben dize dönüştürmek istediğiniz <p>&uuml;&egrave;</p>için <p>üé</p>, birlikte StringEscapeUtils.unescapeHtml4()alıyorum &lt;p&gt;üè&lt;/p&gt;. Mevcut HTML etiketlerini sağlam tutmanın bir yolu var mı?
Nickkk

48

Diğer cevaplarda bahsedilen kütüphaneler iyi çözümler olacaktır, ancak projenizde gerçek dünya html'sini kazmaya başlıyorsanız, Jsoupprojenin sadece "ve işareti FFFF noktalı virgül" şeylerini yönetmekten çok daha fazlası vardır .

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

Ayrıca, en iyi DOM, CSS ve jquery benzeri yöntemleri kullanarak verileri ayıklamak ve işlemek için uygun API'yı alırsınız. Açık kaynak kodlu ve MIT lisansı.


3
upvote + ama Jsoup yeni sürümleri kullanan işaret etmelidir .text()yerine.getText()
SourceVisor

4
Belki daha doğrudan kullanmaktır org.jsoup.parser.Parser.unescapeEntities(String string, boolean inAttribute). API dokümanları: jsoup.org/apidocs/org/jsoup/parser/…
danneu

3
Projemde zaten Jsoup kullandığım için bu mükemmeldi. Ayrıca, @danneu haklıydı - Parser.unescapeEntities tam olarak ilan edildiği gibi çalışır.
MandisaW

42

Projemde Apache Commons StringEscapeUtils.unescapeHtml3 () denedim, ancak performansından memnun kalmadım. Görünen o ki, pek çok gereksiz işlem yapıyor. Birincisi, dizede unescape için hiçbir şey olmasa bile her çağrı için bir StringWriter ayırır. Bu kodu farklı şekilde yeniden yazdım, şimdi çok daha hızlı çalışıyor. Bunu Google'da bulan kişi onu kullanabilir.

Aşağıdaki kod tüm HTML 3 sembollerinden ve sayısal çıkışlardan (Apache unescapeHtml3'e eşdeğer) kaçar. HTML 4'e ihtiyacınız varsa haritaya daha fazla giriş ekleyebilirsiniz.

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

}

Son zamanlarda yavaş bir Struts projesini optimize etmek zorunda kaldım. Struts kapağının altında varsayılan olarak kaçan html dizesi için Apache'yi çağırdığı ortaya çıktı ( <s:property value="..."/>). Escaping ( <s:property value="..." escaping="false"/>) işlevinin kapatılması bazı sayfaların% 5 ila% 20 daha hızlı çalışmasını sağladı.
Stephan

Daha sonra argüman olarak boş dize verildiğinde bu kodun döngü girebilir öğrendim. Mevcut sürümde bu sorun düzeltildi.
Nick Frolov

Bu kaçar mı yoksa boş kalır mı? ve Haefely, kodu çözülmez. Haritaya yalnızca & eklenir, bu nedenle yalnızca bir şekilde çalışır?
mmm

3
Bir StringWriter dahili olarak kilitleme kullanan bir StringBuffer kullanır. Doğrudan bir StringBuilder kullanmak daha hızlı olmalıdır.
Axel Dörfler

4
@NickFrolov, yorumlarınız biraz dağınık görünüyor. aumlörneğin ä, değil д.
aioobe

12

Aşağıdaki kitaplık Java'dan kaçan HTML için de kullanılabilir: unbescape .

HTML bu şekilde kaçabilir:

final String unescapedText = HtmlEscape.unescapeHtml(escapedText); 

2
Bunun için hiçbir şey yapmadı:%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
Tehdit

40
@ThreaT Metniniz html kodlu değil, url kodlu.
Mikhail Batcer

9

Bu benim için işi yaptı,

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

veya

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

lang3Açık nedenlerle kullanmak her zaman daha iyidir sanırım . Bu yardımcı olur umarım :)


4

Harici kütüphane olmadan çok basit ama verimsiz bir çözüm:

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

Bu, yalnızca kodunu çözmek için çok az sayıda dizeniz varsa kullanılmalıdır.


1
Çok yakın, ama tam değil - "qwAS12ƷƸDžǚǪǼȌ" i "qwAS12ƷƸDžǚǪǼȌ \ n" biçimine dönüştürdü.
Greg

3

En güvenilir yol

String cleanedString = StringEscapeUtils.unescapeHtml4(originalString);

den org.apache.commons.lang3.StringEscapeUtils.

Beyaz boşluklardan kaçmak için

cleanedString = cleanedString.trim();

Bu, web formlarında kopyalama ve yapıştırma nedeniyle oluşan boşlukların DB'de kalıcı olmamasını sağlayacaktır.


1

Spring Framework HtmlUtils

Spring framework'ü zaten kullanıyorsanız, aşağıdaki yöntemi kullanın:

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

...

String result = htmlUnescape(source);

0

HtmlManipulator Java sınıfını kullanmayı düşünün . Bazı öğeler eklemeniz gerekebilir (tüm varlıklar listede değildir).

Kevin Hakanson tarafından önerildiği gibi Apache Commons StringEscapeUtils benim için% 100 çalışmadı; & # 145 (sol tek tırnak) gibi birçok varlık bir şekilde '222' ye çevrildi. Ayrıca org.jsoup'u denedim ve aynı sorunu yaşadım.


0

Benim durumumda, her değişkendeki her varlığı test ederek replace yöntemini kullanıyorum, kodum şöyle görünüyor:

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

Benim durumumda bu çok iyi çalıştı.


2
Bu her özel varlık değil. Soruda bahsedilen iki kişi bile eksik.
Sandy Gifford

bu iyi ölçeklenmeyecek
denov

-7

Eğer htmlspecialchars_decode tablo dökümü ve sonra gibi java kodu kullanmak için php get_html_translation_table () php işlevini kullandığı taklit etmek istiyorsanız,

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
Çok fazla oyuncu olmayın; o HashMap üzerinde jenerikler kullanın! Ayrıca, bir foreach kullanın, tekrarlamak için bir süre değil, kod daha okunabilir görünecek!
WhyNotHugo

3
@BalaDutt Cevabınızı geliştirirseniz, çocuklar size puan verecektir :)
sparkyspider

3
Fonksiyonunuzu ve değişken isimlerinizi de geliştirin, @Bala.
Thomas W
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.