Penguraian karakter HTML di Objective-C / Cocoa Touch

103

Pertama-tama, saya menemukan ini: Objective C HTML escape / unescape , tetapi tidak berhasil untuk saya.

Karakter saya yang dikodekan (berasal dari umpan RSS, btw) terlihat seperti ini: &

Saya mencari di seluruh internet dan menemukan diskusi terkait, tetapi tidak ada perbaikan untuk pengkodean khusus saya, saya pikir mereka disebut karakter heksadesimal.

treznik
sumber
3
Komentar ini enam bulan setelah pertanyaan awal, jadi lebih bagi mereka yang tersandung pada pertanyaan ini mencari jawaban dan solusi. Sebuah pertanyaan yang sangat mirip muncul baru-baru ini yang saya jawab stackoverflow.com/questions/2254862/… Ia menggunakan RegexKitLite dan Blocks untuk melakukan pencarian dan penggantian &#...;dalam string dengan karakter yang setara.
johne
Apa yang secara spesifik "tidak berhasil"? Saya tidak melihat apa pun dalam pertanyaan ini yang bukan duplikat dari pertanyaan sebelumnya.
Peter Hosey
Itu desimal. Heksadesimal adalah 8.
kennytm
Perbedaan antara desimal dan heksadesimal adalah bahwa desimal adalah basis 10, sedangkan heksadesimal adalah basis 16. "38" adalah nomor berbeda di setiap basis; di basis 10, itu 3 × 10 + 8 × 1 = tiga puluh delapan, sedangkan di basis 16, itu 3 × 16 + 8 × 1 = lima puluh enam. Angka yang lebih tinggi adalah (kelipatan dari) pangkat yang lebih tinggi dari basis; bilangan bulat terkecil adalah basis 0 (= 1), digit selanjutnya yang lebih tinggi adalah basis 1 (= basis), digit berikutnya adalah basis ** 2 (= basis * basis), dll. Ini adalah eksponen yang sedang bekerja.
Peter Hosey

Jawaban:

46

Itu disebut Referensi Entitas Karakter . Ketika mereka mengambil bentuk &#<number>;mereka disebut referensi entitas numerik . Pada dasarnya, ini adalah representasi string dari byte yang harus diganti. Dalam kasus &#038;, ini mewakili karakter dengan nilai 38 dalam skema pengkodean karakter ISO-8859-1, yaitu &.

Alasan ampersand harus dikodekan di RSS adalah karena karakter khusus yang dicadangkan.

Yang perlu Anda lakukan adalah mengurai string dan mengganti entitas dengan byte yang cocok dengan nilai antara &#dan ;. Saya tidak tahu cara yang bagus untuk melakukan ini di objektif C, tapi pertanyaan stack overflow ini mungkin bisa membantu.

Sunting: Sejak menjawab ini sekitar dua tahun yang lalu ada beberapa solusi hebat; lihat jawaban Air Terjun @ Michael di bawah ini.

Matt Bridges
sumber
2
+1 Saya baru saja akan mengirimkan jawaban yang persis sama (termasuk tautan yang sama, tidak kurang!)
e. James
"Pada dasarnya, ini adalah representasi string dari byte yang harus diganti." Lebih mirip karakter. Ini teks, bukan data; setelah mengonversi teks menjadi data, karakter mungkin menempati beberapa byte, tergantung pada karakter dan pengkodeannya.
Peter Hosey
Terima kasih balasannya. Anda mengatakan "ini mewakili karakter dengan nilai 38 dalam skema pengkodean karakter ISO-8859-1, yaitu &". Apa kamu yakin akan hal itu? Apakah Anda memiliki link ke tabel karakter jenis ini? Karena dari yang saya ingat itu adalah kutipan tunggal.
treznik
en.wikipedia.org/wiki/ISO/IEC_8859-1#ISO-8859-1 atau cukup ketik & # 038; ke google.
Matt Bridges
dan bagaimana dengan & amp; atau & salin; simbol?
vokilam
162

Lihat kategori NSString saya untuk HTML . Berikut metode yang tersedia:

- (NSString *)stringByConvertingHTMLToPlainText;
- (NSString *)stringByDecodingHTMLEntities;
- (NSString *)stringByEncodingHTMLEntities;
- (NSString *)stringWithNewLinesAsBRs;
- (NSString *)stringByRemovingNewLinesAndWhitespace;
Air Terjun Michael
sumber
3
Bung, fungsi luar biasa. Metode stringByDecodingXMLEntities Anda membuat hari saya menyenangkan! Terima kasih!
Brian Moeskau
3
Tidak masalah;) Senang Anda menganggapnya berguna!
Air Terjun Michael
4
Setelah beberapa jam mencari, saya tahu bahwa ini adalah satu-satunya cara untuk melakukannya yang benar-benar berhasil. NSString terlambat untuk metode string yang dapat melakukan ini. Sudah selesai dilakukan dengan baik.
Adam Eberbach
1
Saya menemukan (2) pada lisensi Michael terlalu membatasi untuk kasus penggunaan saya, jadi saya menggunakan solusi Nikita. Menyertakan tiga file berlisensi Apache-2.0 dari google toolbox sangat cocok untuk saya.
jaime
10
Pembaruan kode untuk ARC akan berguna .. Xcode membuang banyak kesalahan dan peringatan ARC pada build
Matej
52

Yang oleh Daniel pada dasarnya sangat bagus, dan saya memperbaiki beberapa masalah di sana:

  1. menghapus karakter melewatkan untuk NSSCanner (jika tidak, spasi antara dua entitas berkelanjutan akan diabaikan

    [pemindai setCharactersToBeSkipped: nil];

  2. perbaiki parsing ketika ada simbol '&' yang terisolasi (saya tidak yakin apa output yang 'benar' untuk ini, saya baru saja membandingkannya dengan firefox):

misalnya

    &#ABC DF & B&#39;  & C&#39; Items (288)

berikut adalah kode yang dimodifikasi:

- (NSString *)stringByDecodingXMLEntities {
    NSUInteger myLength = [self length];
    NSUInteger ampIndex = [self rangeOfString:@"&" options:NSLiteralSearch].location;

    // Short-circuit if there are no ampersands.
    if (ampIndex == NSNotFound) {
        return self;
    }
    // Make result string with some extra capacity.
    NSMutableString *result = [NSMutableString stringWithCapacity:(myLength * 1.25)];

    // First iteration doesn't need to scan to & since we did that already, but for code simplicity's sake we'll do it again with the scanner.
    NSScanner *scanner = [NSScanner scannerWithString:self];

    [scanner setCharactersToBeSkipped:nil];

    NSCharacterSet *boundaryCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r;"];

    do {
        // Scan up to the next entity or the end of the string.
        NSString *nonEntityString;
        if ([scanner scanUpToString:@"&" intoString:&nonEntityString]) {
            [result appendString:nonEntityString];
        }
        if ([scanner isAtEnd]) {
            goto finish;
        }
        // Scan either a HTML or numeric character entity reference.
        if ([scanner scanString:@"&amp;" intoString:NULL])
            [result appendString:@"&"];
        else if ([scanner scanString:@"&apos;" intoString:NULL])
            [result appendString:@"'"];
        else if ([scanner scanString:@"&quot;" intoString:NULL])
            [result appendString:@"\""];
        else if ([scanner scanString:@"&lt;" intoString:NULL])
            [result appendString:@"<"];
        else if ([scanner scanString:@"&gt;" intoString:NULL])
            [result appendString:@">"];
        else if ([scanner scanString:@"&#" intoString:NULL]) {
            BOOL gotNumber;
            unsigned charCode;
            NSString *xForHex = @"";

            // Is it hex or decimal?
            if ([scanner scanString:@"x" intoString:&xForHex]) {
                gotNumber = [scanner scanHexInt:&charCode];
            }
            else {
                gotNumber = [scanner scanInt:(int*)&charCode];
            }

            if (gotNumber) {
                [result appendFormat:@"%C", (unichar)charCode];

                [scanner scanString:@";" intoString:NULL];
            }
            else {
                NSString *unknownEntity = @"";

                [scanner scanUpToCharactersFromSet:boundaryCharacterSet intoString:&unknownEntity];


                [result appendFormat:@"&#%@%@", xForHex, unknownEntity];

                //[scanner scanUpToString:@";" intoString:&unknownEntity];
                //[result appendFormat:@"&#%@%@;", xForHex, unknownEntity];
                NSLog(@"Expected numeric character entity but got &#%@%@;", xForHex, unknownEntity);

            }

        }
        else {
            NSString *amp;

            [scanner scanString:@"&" intoString:&amp];  //an isolated & symbol
            [result appendString:amp];

            /*
            NSString *unknownEntity = @"";
            [scanner scanUpToString:@";" intoString:&unknownEntity];
            NSString *semicolon = @"";
            [scanner scanString:@";" intoString:&semicolon];
            [result appendFormat:@"%@%@", unknownEntity, semicolon];
            NSLog(@"Unsupported XML character entity %@%@", unknownEntity, semicolon);
             */
        }

    }
    while (![scanner isAtEnd]);

finish:
    return result;
}
Walty Yeung
sumber
Ini harus menjadi jawaban pasti untuk pertanyaan itu !! Terima kasih!
boliva
Ini bekerja dengan baik. Sayangnya kode jawaban berperingkat tertinggi tidak berfungsi lagi karena masalah ARC, tetapi ini berfungsi.
Ted Kulp
@TedKulp berfungsi dengan baik, Anda hanya perlu menonaktifkan ARC per file. stackoverflow.com/questions/6646052/…
Kyle
Saya akan mengacungkan jempol dua kali jika saya bisa.
Kibitz503
Terjemahan cepat untuk orang-orang yang masih mengunjungi pertanyaan ini pada tahun 2016+: stackoverflow.com/a/35303635/1153630
Max Chuquimia
46

Mulai iOS 7, Anda dapat mendekode karakter HTML secara native dengan menggunakan NSAttributedStringdengan NSHTMLTextDocumentTypeatribut:

NSString *htmlString = @"&#63743; &amp; &#38; &lt; &gt; &trade; &copy; &hearts; &clubs; &spades; &diams;";
NSData *stringData = [htmlString dataUsingEncoding:NSUTF8StringEncoding];

NSDictionary *options = @{NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType};
NSAttributedString *decodedString;
decodedString = [[NSAttributedString alloc] initWithData:stringData
                                                 options:options
                                      documentAttributes:NULL
                                                   error:NULL];

String atribut yang didekode sekarang akan ditampilkan sebagai:  & & <> ™ © ♥ ♣ ♠ ♦.

Catatan: Ini hanya akan berfungsi jika dipanggil di utas utama.

Bryan Luby
sumber
6
jawaban terbaik jika Anda tidak perlu mendukung iOS 6 dan yang lebih lama
jcesarmobile
1
tidak, bukan yang terbaik jika seseorang ingin menyandikannya pada utas bg; O
badeleux
4
Ini berfungsi untuk mendekode entitas, tetapi juga mengacaukan tanda hubung yang tidak dikodekan.
Andrew
Ini terpaksa terjadi di utas utama. Jadi, Anda mungkin tidak ingin melakukan ini jika tidak perlu.
Keith Smiley
Itu hanya hang GUI ketika itu masalah UITableView. Karenanya, tidak berfungsi dengan benar.
Asif Bilal
35

Sepertinya tidak ada yang menyebutkan salah satu opsi paling sederhana: Google Toolbox untuk Mac
(Terlepas dari namanya, ini juga berfungsi di iOS.)

https://github.com/google/google-toolbox-for-mac/blob/master/Foundation/GTMNSString%2BHTML.h

/// Get a string where internal characters that are escaped for HTML are unescaped 
//
///  For example, '&amp;' becomes '&'
///  Handles &#32; and &#x32; cases as well
///
//  Returns:
//    Autoreleased NSString
//
- (NSString *)gtm_stringByUnescapingFromHTML;

Dan saya harus menyertakan hanya tiga file dalam proyek: header, implementasi dan GTMDefines.h.

Nikita Rybak
sumber
Saya telah menyertakan tiga skrip ini, tetapi bagaimana saya dapat menggunakannya sekarang?
Borut Tomazin
@ borut-t [myString gtm_stringByUnescapingFromHTML]
Nikita Rybak
2
Saya memilih untuk hanya menyertakan tiga file tersebut, jadi saya perlu melakukan ini agar kompatibel dengan arc: code.google.com/p/google-toolbox-for-mac/wiki/ARC_Compatibility
jaime
saya harus mengatakan ini adalah solusi paling sederhana dan paling ringan sejauh ini
lensovet
Saya berharap saya bisa membuat ini bekerja sepenuhnya. Sepertinya melewatkan banyak dari mereka di string saya.
Joseph Toronto
17

Saya harus memposting ini di GitHub atau sesuatu. Ini masuk dalam kategori NSString, digunakan NSScanneruntuk implementasi, dan menangani entitas karakter numerik heksadesimal dan desimal serta simbolik biasa.

Juga, ini menangani string yang salah format (ketika Anda memiliki & diikuti oleh urutan karakter yang tidak valid) dengan relatif anggun, yang ternyata sangat penting dalam aplikasi yang saya rilis yang menggunakan kode ini.

- (NSString *)stringByDecodingXMLEntities {
    NSUInteger myLength = [self length];
    NSUInteger ampIndex = [self rangeOfString:@"&" options:NSLiteralSearch].location;

    // Short-circuit if there are no ampersands.
    if (ampIndex == NSNotFound) {
        return self;
    }
    // Make result string with some extra capacity.
    NSMutableString *result = [NSMutableString stringWithCapacity:(myLength * 1.25)];

    // First iteration doesn't need to scan to & since we did that already, but for code simplicity's sake we'll do it again with the scanner.
    NSScanner *scanner = [NSScanner scannerWithString:self];
    do {
        // Scan up to the next entity or the end of the string.
        NSString *nonEntityString;
        if ([scanner scanUpToString:@"&" intoString:&nonEntityString]) {
            [result appendString:nonEntityString];
        }
        if ([scanner isAtEnd]) {
            goto finish;
        }
        // Scan either a HTML or numeric character entity reference.
        if ([scanner scanString:@"&amp;" intoString:NULL])
            [result appendString:@"&"];
        else if ([scanner scanString:@"&apos;" intoString:NULL])
            [result appendString:@"'"];
        else if ([scanner scanString:@"&quot;" intoString:NULL])
            [result appendString:@"\""];
        else if ([scanner scanString:@"&lt;" intoString:NULL])
            [result appendString:@"<"];
        else if ([scanner scanString:@"&gt;" intoString:NULL])
            [result appendString:@">"];
        else if ([scanner scanString:@"&#" intoString:NULL]) {
            BOOL gotNumber;
            unsigned charCode;
            NSString *xForHex = @"";

            // Is it hex or decimal?
            if ([scanner scanString:@"x" intoString:&xForHex]) {
                gotNumber = [scanner scanHexInt:&charCode];
            }
            else {
                gotNumber = [scanner scanInt:(int*)&charCode];
            }
            if (gotNumber) {
                [result appendFormat:@"%C", charCode];
            }
            else {
                NSString *unknownEntity = @"";
                [scanner scanUpToString:@";" intoString:&unknownEntity];
                [result appendFormat:@"&#%@%@;", xForHex, unknownEntity];
                NSLog(@"Expected numeric character entity but got &#%@%@;", xForHex, unknownEntity);
            }
            [scanner scanString:@";" intoString:NULL];
        }
        else {
            NSString *unknownEntity = @"";
            [scanner scanUpToString:@";" intoString:&unknownEntity];
            NSString *semicolon = @"";
            [scanner scanString:@";" intoString:&semicolon];
            [result appendFormat:@"%@%@", unknownEntity, semicolon];
            NSLog(@"Unsupported XML character entity %@%@", unknownEntity, semicolon);
        }
    }
    while (![scanner isAtEnd]);

finish:
    return result;
}
Daniel Dickison
sumber
Sepotong kode yang sangat berguna, namun memiliki beberapa masalah yang telah ditangani oleh Walty. Terima kasih telah berbagi!
Air Terjun Michael
tahukah Anda cara menampilkan simbol lambda, mu, nu, pi dengan mendekode entitas XML mereka seperti & mikro; ... dll ????
chinthakad
Anda harus menghindari penggunaan gotos sebagai gaya kode yang buruk. Anda harus mengganti baris goto finish;dengan break;.
Cantik
4

Ini adalah cara saya melakukannya menggunakan kerangka RegexKitLite :

-(NSString*) decodeHtmlUnicodeCharacters: (NSString*) html {
NSString* result = [html copy];
NSArray* matches = [result arrayOfCaptureComponentsMatchedByRegex: @"\\&#([\\d]+);"];

if (![matches count]) 
    return result;

for (int i=0; i<[matches count]; i++) {
    NSArray* array = [matches objectAtIndex: i];
    NSString* charCode = [array objectAtIndex: 1];
    int code = [charCode intValue];
    NSString* character = [NSString stringWithFormat:@"%C", code];
    result = [result stringByReplacingOccurrencesOfString: [array objectAtIndex: 0]
                                               withString: character];      
}   
return result;  

}

Semoga ini bisa membantu seseorang.

gula pasir
sumber
4

Anda hanya dapat menggunakan fungsi ini untuk menyelesaikan masalah ini.

+ (NSString*) decodeHtmlUnicodeCharactersToString:(NSString*)str
{
    NSMutableString* string = [[NSMutableString alloc] initWithString:str];  // #&39; replace with '
    NSString* unicodeStr = nil;
    NSString* replaceStr = nil;
    int counter = -1;

    for(int i = 0; i < [string length]; ++i)
    {
        unichar char1 = [string characterAtIndex:i];    
        for (int k = i + 1; k < [string length] - 1; ++k)
        {
            unichar char2 = [string characterAtIndex:k];    

            if (char1 == '&'  && char2 == '#' ) 
            {   
                ++counter;
                unicodeStr = [string substringWithRange:NSMakeRange(i + 2 , 2)];    
                // read integer value i.e, 39
                replaceStr = [string substringWithRange:NSMakeRange (i, 5)];     //     #&39;
                [string replaceCharactersInRange: [string rangeOfString:replaceStr] withString:[NSString stringWithFormat:@"%c",[unicodeStr intValue]]];
                break;
            }
        }
    }
    [string autorelease];

    if (counter > 1)
        return  [self decodeHtmlUnicodeCharactersToString:string]; 
    else
        return string;
}
Krishna Gupta
sumber
2

Berikut adalah versi Swift dari jawaban Walty Yeung :

extension String {
    static private let mappings = ["&quot;" : "\"","&amp;" : "&", "&lt;" : "<", "&gt;" : ">","&nbsp;" : " ","&iexcl;" : "¡","&cent;" : "¢","&pound;" : " £","&curren;" : "¤","&yen;" : "¥","&brvbar;" : "¦","&sect;" : "§","&uml;" : "¨","&copy;" : "©","&ordf;" : " ª","&laquo" : "«","&not" : "¬","&reg" : "®","&macr" : "¯","&deg" : "°","&plusmn" : "±","&sup2; " : "²","&sup3" : "³","&acute" : "´","&micro" : "µ","&para" : "¶","&middot" : "·","&cedil" : "¸","&sup1" : "¹","&ordm" : "º","&raquo" : "»&","frac14" : "¼","&frac12" : "½","&frac34" : "¾","&iquest" : "¿","&times" : "×","&divide" : "÷","&ETH" : "Ð","&eth" : "ð","&THORN" : "Þ","&thorn" : "þ","&AElig" : "Æ","&aelig" : "æ","&OElig" : "Œ","&oelig" : "œ","&Aring" : "Å","&Oslash" : "Ø","&Ccedil" : "Ç","&ccedil" : "ç","&szlig" : "ß","&Ntilde;" : "Ñ","&ntilde;":"ñ",]

    func stringByDecodingXMLEntities() -> String {

        guard let _ = self.rangeOfString("&", options: [.LiteralSearch]) else {
            return self
        }

        var result = ""

        let scanner = NSScanner(string: self)
        scanner.charactersToBeSkipped = nil

        let boundaryCharacterSet = NSCharacterSet(charactersInString: " \t\n\r;")

        repeat {
            var nonEntityString: NSString? = nil

            if scanner.scanUpToString("&", intoString: &nonEntityString) {
                if let s = nonEntityString as? String {
                    result.appendContentsOf(s)
                }
            }

            if scanner.atEnd {
                break
            }

            var didBreak = false
            for (k,v) in String.mappings {
                if scanner.scanString(k, intoString: nil) {
                    result.appendContentsOf(v)
                    didBreak = true
                    break
                }
            }

            if !didBreak {

                if scanner.scanString("&#", intoString: nil) {

                    var gotNumber = false
                    var charCodeUInt: UInt32 = 0
                    var charCodeInt: Int32 = -1
                    var xForHex: NSString? = nil

                    if scanner.scanString("x", intoString: &xForHex) {
                        gotNumber = scanner.scanHexInt(&charCodeUInt)
                    }
                    else {
                        gotNumber = scanner.scanInt(&charCodeInt)
                    }

                    if gotNumber {
                        let newChar = String(format: "%C", (charCodeInt > -1) ? charCodeInt : charCodeUInt)
                        result.appendContentsOf(newChar)
                        scanner.scanString(";", intoString: nil)
                    }
                    else {
                        var unknownEntity: NSString? = nil
                        scanner.scanUpToCharactersFromSet(boundaryCharacterSet, intoString: &unknownEntity)
                        let h = xForHex ?? ""
                        let u = unknownEntity ?? ""
                        result.appendContentsOf("&#\(h)\(u)")
                    }
                }
                else {
                    scanner.scanString("&", intoString: nil)
                    result.appendContentsOf("&")
                }
            }

        } while (!scanner.atEnd)

        return result
    }
}
Max Chuquimia
sumber
1

Sebenarnya kerangka kerja MWFeedParser yang hebat dari Air Terjun Michael (mengacu pada jawabannya) telah bercabang oleh rmchaara yang telah memperbaruinya dengan dukungan ARC!

Anda dapat menemukannya di Github di sini

Ini benar-benar berfungsi dengan baik, saya menggunakan metode stringByDecodingHTMLEntities dan bekerja dengan sempurna.

angelos.p
sumber
Itu memperbaiki masalah ARC - tetapi memperkenalkan beberapa peringatan. Saya pikir aman untuk mengabaikannya?
Robert J. Clegg
0

Seolah Anda membutuhkan solusi lain! Yang ini cukup sederhana dan cukup efektif:

@interface NSString (NSStringCategory)
- (NSString *) stringByReplacingISO8859Codes;
@end


@implementation NSString (NSStringCategory)
- (NSString *) stringByReplacingISO8859Codes
{
    NSString *dataString = self;
    do {
        //*** See if string contains &# prefix
        NSRange range = [dataString rangeOfString: @"&#" options: NSRegularExpressionSearch];
        if (range.location == NSNotFound) {
            break;
        }
        //*** Get the next three charaters after the prefix
        NSString *isoHex = [dataString substringWithRange: NSMakeRange(range.location + 2, 3)];
        //*** Create the full code for replacement
        NSString *isoString = [NSString stringWithFormat: @"&#%@;", isoHex];
        //*** Convert to decimal integer
        unsigned decimal = 0;
        NSScanner *scanner = [NSScanner scannerWithString: [NSString stringWithFormat: @"0%@", isoHex]];
        [scanner scanHexInt: &decimal];
        //*** Use decimal code to get unicode character
        NSString *unicode = [NSString stringWithFormat:@"%C", decimal];
        //*** Replace all occurences of this code in the string
        dataString = [dataString stringByReplacingOccurrencesOfString: isoString withString: unicode];
    } while (TRUE); //*** Loop until we hit the NSNotFound

    return dataString;
}
@end
mpemburn.dll
sumber
0

Jika Anda memiliki Referensi Entitas Karakter sebagai string, misalnya @"2318", Anda dapat mengekstrak NSString yang dikodekan ulang dengan karakter unicode yang benar menggunakan strtoul;

NSString *unicodePoint = @"2318"
unichar iconChar = (unichar) strtoul(unicodePoint.UTF8String, NULL, 16);
NSString *recoded = [NSString stringWithFormat:@"%C", iconChar];
NSLog(@"recoded: %@", recoded");
// prints out "recoded: ⌘"
Henrik Hartz
sumber
0

Jawaban Jugale versi Swift 3

extension String {
    static private let mappings = ["&quot;" : "\"","&amp;" : "&", "&lt;" : "<", "&gt;" : ">","&nbsp;" : " ","&iexcl;" : "¡","&cent;" : "¢","&pound;" : " £","&curren;" : "¤","&yen;" : "¥","&brvbar;" : "¦","&sect;" : "§","&uml;" : "¨","&copy;" : "©","&ordf;" : " ª","&laquo" : "«","&not" : "¬","&reg" : "®","&macr" : "¯","&deg" : "°","&plusmn" : "±","&sup2; " : "²","&sup3" : "³","&acute" : "´","&micro" : "µ","&para" : "¶","&middot" : "·","&cedil" : "¸","&sup1" : "¹","&ordm" : "º","&raquo" : "»&","frac14" : "¼","&frac12" : "½","&frac34" : "¾","&iquest" : "¿","&times" : "×","&divide" : "÷","&ETH" : "Ð","&eth" : "ð","&THORN" : "Þ","&thorn" : "þ","&AElig" : "Æ","&aelig" : "æ","&OElig" : "Œ","&oelig" : "œ","&Aring" : "Å","&Oslash" : "Ø","&Ccedil" : "Ç","&ccedil" : "ç","&szlig" : "ß","&Ntilde;" : "Ñ","&ntilde;":"ñ",]

    func stringByDecodingXMLEntities() -> String {

        guard let _ = self.range(of: "&", options: [.literal]) else {
            return self
        }

        var result = ""

        let scanner = Scanner(string: self)
        scanner.charactersToBeSkipped = nil

        let boundaryCharacterSet = CharacterSet(charactersIn: " \t\n\r;")

        repeat {
            var nonEntityString: NSString? = nil

            if scanner.scanUpTo("&", into: &nonEntityString) {
                if let s = nonEntityString as? String {
                    result.append(s)
                }
            }

            if scanner.isAtEnd {
                break
            }

            var didBreak = false
            for (k,v) in String.mappings {
                if scanner.scanString(k, into: nil) {
                    result.append(v)
                    didBreak = true
                    break
                }
            }

            if !didBreak {

                if scanner.scanString("&#", into: nil) {

                    var gotNumber = false
                    var charCodeUInt: UInt32 = 0
                    var charCodeInt: Int32 = -1
                    var xForHex: NSString? = nil

                    if scanner.scanString("x", into: &xForHex) {
                        gotNumber = scanner.scanHexInt32(&charCodeUInt)
                    }
                    else {
                        gotNumber = scanner.scanInt32(&charCodeInt)
                    }

                    if gotNumber {
                        let newChar = String(format: "%C", (charCodeInt > -1) ? charCodeInt : charCodeUInt)
                        result.append(newChar)
                        scanner.scanString(";", into: nil)
                    }
                    else {
                        var unknownEntity: NSString? = nil
                        scanner.scanUpToCharacters(from: boundaryCharacterSet, into: &unknownEntity)
                        let h = xForHex ?? ""
                        let u = unknownEntity ?? ""
                        result.append("&#\(h)\(u)")
                    }
                }
                else {
                    scanner.scanString("&", into: nil)
                    result.append("&")
                }
            }

        } while (!scanner.isAtEnd)

        return result
    }
}
Xzya
sumber