HTML-Zeichendecodierung in Objective-C / Cocoa Touch

103

Zunächst fand ich Folgendes: Ziel C HTML Escape / Unescape , aber es funktioniert nicht für mich.

Meine verschlüsselten Zeichen (stammen übrigens aus einem RSS-Feed) sehen folgendermaßen aus: &

Ich habe im ganzen Netz gesucht und verwandte Diskussionen gefunden, aber keine Lösung für meine spezielle Codierung. Ich denke, sie werden als hexadezimale Zeichen bezeichnet.

treznik
quelle
3
Dieser Kommentar ist sechs Monate nach der ursprünglichen Frage, also eher für diejenigen, die über diese Frage stolpern und nach einer Antwort und einer Lösung suchen. Eine sehr ähnliche Frage wurde erst kürzlich gestellt, die ich auf stackoverflow.com/questions/2254862/… beantwortet habe. Sie verwendet RegexKitLite und Blocks, um die Suche &#...;in einer Zeichenfolge durch das entsprechende Zeichen zu ersetzen .
Johne
Was konkret "funktioniert nicht"? Ich sehe in dieser Frage nichts, was kein Duplikat dieser früheren Frage ist.
Peter Hosey
Es ist dezimal. Hexadezimal ist 8.
Kennytm
Der Unterschied zwischen Dezimal und Hexadezimal besteht darin, dass die Dezimalstelle Basis 10 ist, während Hexadezimal Basis 16 ist. "38" ist eine andere Zahl in jeder Basis; in Basis 10 ist es 3 × 10 + 8 × 1 = achtunddreißig, während es in Basis 16 3 × 16 + 8 × 1 = sechsundfünfzig ist. Höhere Ziffern sind (Vielfache) der höheren Potenzen der Basis; Die niedrigste ganze Ziffer ist Basis 0 (= 1), die nächsthöhere Ziffer ist Basis 1 (= Basis), die nächste ist Basis ** 2 (= Basis * Basis) usw. Dies ist Exponentation bei der Arbeit.
Peter Hosey

Antworten:

46

Diese werden als Zeichenentitätsreferenzen bezeichnet . Wenn sie die Form haben, werden &#<number>;sie als numerische Entitätsreferenzen bezeichnet . Grundsätzlich ist es eine Zeichenfolgendarstellung des Bytes, das ersetzt werden soll. Im Fall von &#038;repräsentiert es das Zeichen mit dem Wert 38 im Zeichenkodierungsschema ISO-8859-1 &.

Der Grund, warum das kaufmännische Und in RSS codiert werden muss, ist ein reserviertes Sonderzeichen.

Sie müssen lediglich die Zeichenfolge analysieren und die Entitäten durch ein Byte ersetzen, das dem Wert zwischen &#und entspricht ;. Ich kenne keine großartigen Möglichkeiten, dies in Ziel C zu tun, aber diese Frage zum Stapelüberlauf könnte hilfreich sein.

Bearbeiten: Seit der Beantwortung vor zwei Jahren gibt es einige großartige Lösungen; Siehe die Antwort von @Michael Waterfall unten.

Matt Bridges
quelle
2
+1 Ich wollte gerade die exakt gleiche Antwort einreichen (einschließlich der gleichen Links, nicht weniger!)
e.James
"Im Grunde ist es eine Zeichenfolgendarstellung des Bytes, das ersetzt werden sollte." Eher wie Charakter. Dies ist Text, keine Daten; Beim Konvertieren des Textes in Daten kann das Zeichen abhängig vom Zeichen und der Codierung mehrere Bytes belegen.
Peter Hosey
Danke für die Antwort. Sie sagten, "es repräsentiert das Zeichen mit dem Wert 38 im ISO-8859-1-Zeichencodierungsschema, das & ist." Bist du dir da sicher? Haben Sie einen Link zu einer Zeichentabelle dieses Typs? Denn soweit ich mich erinnere, war das ein einfaches Zitat.
Treznik
en.wikipedia.org/wiki/ISO/IEC_8859-1#ISO-8859-1 oder geben Sie einfach & # 038; in Google.
Matt Bridges
und was ist mit & amp; oder & copy; Symbole?
Vokilam
162

Überprüfen Sie meine NSString-Kategorie für HTML . Hier sind die verfügbaren Methoden:

- (NSString *)stringByConvertingHTMLToPlainText;
- (NSString *)stringByDecodingHTMLEntities;
- (NSString *)stringByEncodingHTMLEntities;
- (NSString *)stringWithNewLinesAsBRs;
- (NSString *)stringByRemovingNewLinesAndWhitespace;
Michael Wasserfall
quelle
3
Alter, ausgezeichnete Funktionen. Ihre stringByDecodingXMLEntities-Methode hat meinen Tag gemacht! Vielen Dank!
Brian Moeskau
3
Kein Problem;) Schön, dass du es nützlich fandest!
Michael Wasserfall
4
Nach ein paar Stunden Suche weiß ich, dass dies der einzige Weg ist, der wirklich funktioniert. NSString ist für eine String-Methode, die dies kann, überfällig. Gut gemacht.
Adam Eberbach
1
Ich fand (2) in Michaels Lizenz zu restriktiv für meinen Anwendungsfall, also habe ich Nikitas Lösung verwendet. Das Einschließen von drei Apache-2.0-lizenzierten Dateien aus der Google Toolbox funktioniert für mich hervorragend.
Jaime
10
Code-Update für ARC wäre praktisch. Xcode wirft eine
Matej
52

Der von Daniel ist im Grunde sehr nett und ich habe dort ein paar Probleme behoben:

  1. Das überspringende Zeichen für NSSCanner wurde entfernt (andernfalls werden Leerzeichen zwischen zwei fortlaufenden Entitäten ignoriert

    [scanner setCharactersToBeSkipped: nil];

  2. Die Analyse wurde korrigiert, wenn isolierte '&' Symbole vorhanden sind (ich bin nicht sicher, was die 'richtige' Ausgabe dafür ist, ich habe sie nur mit Firefox verglichen):

z.B

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

Hier ist der geänderte Code:

- (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
quelle
Dies sollte die eindeutige Antwort auf die Frage sein !! Vielen Dank!
Boliva
Das hat super geklappt. Leider funktioniert der Code der am besten bewerteten Antwort aufgrund von ARC-Problemen nicht mehr, aber das funktioniert.
Ted Kulp
@ TedKulp es funktioniert einwandfrei, Sie müssen nur ARC pro Datei deaktivieren. stackoverflow.com/questions/6646052/…
Kyle
Ich würde dich zweimal Daumen hoch, wenn ich könnte.
Kibitz503
Schnelle Übersetzung für Leute, die diese Frage 2016+ noch besuchen: stackoverflow.com/a/35303635/1153630
Max Chuquimia
46

Ab iOS 7 können Sie HTML-Zeichen nativ dekodieren, indem Sie ein NSAttributedStringmit dem NSHTMLTextDocumentTypeAttribut verwenden:

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

Die dekodierte zugeordnete Zeichenfolge wird nun wie folgt angezeigt:  & & <> ™ © ♥ ♣ ♠ ♦.

Hinweis: Dies funktioniert nur, wenn es im Hauptthread aufgerufen wird.

Bryan Luby
quelle
6
Beste Antwort, wenn Sie iOS 6 und älter nicht unterstützen müssen
jcesarmobile
1
nein, nicht das beste, wenn jemand es auf bg thread codieren will; O
badeleux
4
Dies funktionierte zum Dekodieren einer Entität, brachte aber auch einen nicht codierten Bindestrich durcheinander.
Andrew
Dies muss im Haupt-Thread geschehen. Sie möchten dies wahrscheinlich nicht tun, wenn Sie es nicht müssen.
Keith Smiley
Es hängt nur die GUI, wenn es um UITableView geht. Daher funktioniert es nicht richtig.
Asif Bilal
35

Niemand scheint eine der einfachsten Optionen zu erwähnen: Google Toolbox für Mac
(Trotz des Namens funktioniert dies auch unter 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;

Und ich musste nur drei Dateien in das Projekt aufnehmen: Header, Implementierung und GTMDefines.h.

Nikita Rybak
quelle
Ich habe diese drei Skripte eingefügt, aber wie kann ich sie jetzt verwenden?
Borut Tomazin
@ borut-t [myString gtm_stringByUnescapingFromHTML]
Nikita Rybak
2
Ich habe mich dafür entschieden, nur diese drei Dateien einzuschließen
jaime
Ich muss sagen, dies ist die mit Abstand einfachste und leichteste Lösung
Lensovet
Ich wünschte, ich könnte das komplett zum Laufen bringen. Es scheint viele von ihnen in meinen Saiten zu überspringen.
Joseph Toronto
17

Ich sollte das auf GitHub posten oder so. Dies gehört zu einer Kategorie von NSString, wird NSScannerfür die Implementierung verwendet und behandelt sowohl hexadezimale und dezimale numerische Zeichenentitäten als auch die üblichen symbolischen.

Außerdem werden fehlerhafte Zeichenfolgen (wenn Sie ein & gefolgt von einer ungültigen Zeichenfolge haben) relativ elegant behandelt, was sich in meiner veröffentlichten App , die diesen Code verwendet , als entscheidend herausstellte .

- (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
quelle
Sehr nützlicher Code, jedoch gibt es einige Probleme, die von Walty behoben wurden. Danke für das Teilen!
Michael Wasserfall
Kennen Sie eine Möglichkeit, Lambda, Mu, Nu, Pi-Symbole anzuzeigen, indem Sie ihre XML-Entitäten wie & micro; ... ect ????
Chinthakad
Sie sollten vermeiden, gotos als schrecklichen Codestil zu verwenden. Sie sollten die Leitung goto finish;durch ersetzen break;.
Stunner
4

So mache ich es mit dem RegexKitLite- Framework:

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

}}

Hoffe das wird jemandem helfen.

Realzucker
quelle
4

Sie können nur diese Funktion verwenden, um dieses Problem zu lösen.

+ (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
quelle
2

Hier ist eine schnelle Version von Walty Yeungs Antwort :

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
quelle
1

Tatsächlich wurde das großartige MWFeedParser-Framework von Michael Waterfall (auf seine Antwort verwiesen) von rmchaara gegabelt, der es mit ARC-Unterstützung aktualisiert hat!

Sie finden es in Github hier

Es funktioniert wirklich gut, ich habe die stringByDecodingHTMLEntities-Methode verwendet und funktioniert einwandfrei.

angelos.p
quelle
Das behebt die ARC-Probleme - führt jedoch einige Warnungen ein. Ich denke, es ist sicher, sie zu ignorieren?
Robert J. Clegg
0

Als ob Sie eine andere Lösung brauchen! Dieser ist ziemlich einfach und ziemlich effektiv:

@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
quelle
0

Wenn Sie die @"2318"Zeichenentitätsreferenz als Zeichenfolge haben, können Sie z. B. einen neu codierten NSString mit dem richtigen Unicode-Zeichen extrahieren 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
quelle
0

Swift 3-Version von Jugales Antwort

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
quelle