Dekodowanie znaków HTML w Objective-C / Cocoa Touch


103

Przede wszystkim znalazłem to: Cel C HTML escape / unescape , ale nie działa dla mnie.

Moje zakodowane znaki (przy okazji pochodzą z kanału RSS) wyglądają następująco: &

Przeszukałem całą sieć i znalazłem powiązane dyskusje, ale nie poprawiłem mojego konkretnego kodowania, myślę, że nazywa się je znakami szesnastkowymi.


3
Ten komentarz pojawił się sześć miesięcy po pierwotnym pytaniu, więc jest bardziej dla tych, którzy natkną się na to pytanie, szukając odpowiedzi i rozwiązania. Niedawno pojawiło się bardzo podobne pytanie, na które odpowiedziałem stackoverflow.com/questions/2254862/ ... Używa RegexKitLite i Blocks do wyszukiwania i zastępowania ciągu &#...;w ciągu odpowiadającym mu znakiem.
— johne

Co konkretnie „nie działa”? Nie widzę w tym pytaniu niczego, co nie jest duplikatem tego wcześniejszego pytania.
— Peter Hosey

To jest dziesiętne. Szesnastkowy to 8.
— kennytm

Różnica między liczbą dziesiętną a szesnastkową to podstawa-10, podczas gdy szesnastkowa to podstawa-16. „38” to inna liczba w każdej bazie; w przypadku podstawy 10 to 3 × 10 + 8 × 1 = trzydzieści osiem, podczas gdy w przypadku podstawy-16 to 3 × 16 + 8 × 1 = pięćdziesiąt sześć. Wyższe cyfry to (wielokrotności) wyższe potęgi podstawy; najniższa cała cyfra to podstawa 0 (= 1), następna wyższa cyfra to podstawa 1 (= podstawa), następna to podstawa ** 2 (= podstawa * podstawa) itd. To jest potęga w działaniu.
— Peter Hosey

Odpowiedzi:


46

Nazywa się to odniesieniami do jednostek postaci . Kiedy przyjmują formę &#<number>;, nazywane są numerycznymi odniesieniami do bytów . Zasadniczo jest to ciąg znaków reprezentujący bajt, który należy podstawić. W przypadku &#038;, reprezentuje znak o wartości 38 w schemacie kodowania znaków ISO-8859-1, czyli &.

Powodem, dla którego znak ampersand musi być zakodowany w formacie RSS, jest zastrzeżony znak specjalny.

To, co musisz zrobić, to przeanalizować ciąg i zastąpić jednostki bajtem pasującym do wartości między &#a ;. Nie znam żadnych świetnych sposobów na zrobienie tego w celu C, ale pytanie o przepełnienie stosu może być pomocne.

Edycja: Od czasu odpowiedzi na to pytanie jakieś dwa lata temu pojawiło się kilka świetnych rozwiązań; zobacz odpowiedź @Michael Waterfall poniżej.


2
+1 Miałem właśnie
— udzielić

„Zasadniczo jest to ciąg znaków reprezentujący bajt, który należy podstawić”. Bardziej jak charakter. To jest tekst, a nie dane; po konwersji tekstu na dane znak może zajmować wiele bajtów, w zależności od znaku i kodowania.
— Peter Hosey

Dziękuję za odpowiedź. Powiedziałeś, że „reprezentuje znak o wartości 38 w schemacie kodowania znaków ISO-8859-1, czyli &”. Czy jesteś tego pewien? Czy masz link do tablicy znaków tego typu? Z tego, co pamiętam, był to pojedynczy cytat.
— treznik

en.wikipedia.org/wiki/ISO/IEC_8859-1#ISO-8859-1 lub po prostu wpisz & # 038; do google.
— Matt Bridges,

a co z & amp; lub & copy; symbolika?
— vokilam

162

Sprawdź moją kategorię NSString dla HTML . Oto dostępne metody:

- (NSString *)stringByConvertingHTMLToPlainText;
- (NSString *)stringByDecodingHTMLEntities;
- (NSString *)stringByEncodingHTMLEntities;
- (NSString *)stringWithNewLinesAsBRs;
- (NSString *)stringByRemovingNewLinesAndWhitespace;

3
Koleś, doskonałe funkcje. Twoja metoda stringByDecodingXMLEntities zrobiła mój dzień! Dzięki!
— Brian Moeskau

3
Żaden problem;) Cieszę się, że to przydatne!
— Michael Waterfall

4
Po kilku godzinach poszukiwań wiem, że to jedyny sposób, który naprawdę działa. NSString jest spóźniony w przypadku metody łańcuchowej, która może to zrobić. Dobra robota.
— Adam Eberbach

1
Uważam, że (2) na licencji Michaela jest zbyt restrykcyjne dla mojego przypadku, więc użyłem rozwiązania Nikity. Dołączenie trzech plików na licencji Apache-2.0 z zestawu narzędzi Google działa świetnie.
— jaime

10
Aktualizacja kodu dla ARC byłaby przydatna. Xcode generuje mnóstwo błędów i ostrzeżeń ARC w kompilacji
— Matej

52

Ten autorstwa Daniela jest w zasadzie bardzo fajny i naprawiłem tam kilka problemów:

  1. usunięto znak pomijania dla NSSCanner (w przeciwnym razie spacje między dwoma ciągłymi jednostkami byłyby ignorowane

    [skaner setCharactersToBeSkipped: nil];

  2. naprawiono parsowanie, gdy występują izolowane symbole `` & '' (nie jestem pewien, jakie jest `` prawidłowe '' wyjście dla tego, właśnie porównałem to z firefoxem):

na przykład

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

oto zmodyfikowany kod:

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

To powinna być ostateczna odpowiedź na pytanie !! Dzięki!
— boliva

To działało świetnie. Niestety kod najwyżej ocenionej odpowiedzi już nie działa z powodu problemów z ARC, ale tak jest.
— Ted Kulp

@TedKulp działa dobrze, wystarczy wyłączyć ARC dla każdego pliku. stackoverflow.com/questions/6646052/…
— Kyle

Gdybym mógł, dałbym ci dwa razy kciuk.
— Kibitz503,

Szybkie tłumaczenie dla osób nadal odwiedzających to pytanie w 2016+: stackoverflow.com/a/35303635/1153630
— Max Chuquimia

46

Począwszy od iOS 7, możesz dekodować znaki HTML natywnie, używając an NSAttributedStringz NSHTMLTextDocumentTypeatrybutem:

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

Zdekodowany przypisany ciąg będzie teraz wyświetlany jako:  & & <> ™ © ♥ ♣ ♠ ♦.

Uwaga: zadziała tylko wtedy, gdy zostanie wywołany w głównym wątku.


6
najlepsza odpowiedź, jeśli nie potrzebujesz obsługi iOS 6 i starszych
— jcesarmobile

1
nie, nie najlepiej, jeśli ktoś chce zakodować to na wątku bg; O
— badeleux

4
To działało w przypadku dekodowania jednostki, ale powodowało również zepsucie niekodowanego myślnika.
— Andrew,

Jest to wymuszone w głównym wątku. Więc prawdopodobnie nie chcesz tego robić, jeśli nie musisz.
— Keith Smiley

Po prostu zawiesza GUI, gdy chodzi o UITableView. Dlatego nie działa poprawnie.
— Asif Bilal

35

Wydaje się, że nikt nie wspomina o jednej z najprostszych opcji: Google Toolbox for Mac
(pomimo nazwy działa to również na 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;

W projekcie musiałem uwzględnić tylko trzy pliki: nagłówek, implementację i GTMDefines.h.


Dołączyłem te trzy skrypty, ale jak mogę ich teraz użyć?
— Borut Tomazin

@ borut-t [myString gtm_stringByUnescapingFromHTML]
— Nikita Rybak

2
Zdecydowałem się dołączyć tylko te trzy pliki, więc musiałem to zrobić, aby był zgodny z arc: code.google.com/p/google-toolbox-for-mac/wiki/ARC_Compatibility
— jaime

Muszę powiedzieć, że jest to zdecydowanie najprostsze i
— najlżejsze

Chciałbym móc to całkowicie zadziałać. Wydaje się, że przeskakuje wiele z nich w moich strunach.
— Joseph Toronto,

17

Powinienem opublikować to na GitHubie czy coś. To należy do kategorii NSString, używa NSScannerdo implementacji i obsługuje zarówno szesnastkowe, jak i dziesiętne numeryczne jednostki znakowe, a także zwykłe symboliczne.

Ponadto stosunkowo dobrze radzi sobie ze zniekształconymi ciągami znaków (gdy występuje &, po którym występuje nieprawidłowa sekwencja znaków), co okazało się kluczowe w mojej wydanej aplikacji, która używa tego kodu.

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

Bardzo przydatny fragment kodu, jednak zawiera kilka problemów, które zostały rozwiązane przez Walty. Dzięki za udostępnienie!
— Michael Waterfall

czy znasz sposób wyświetlania symboli lambda, mu, nu, pi poprzez dekodowanie ich jednostek XML, takich jak & micro; ... ect ????
— chinthakad

Powinieneś unikać używania gotos jako jego okropnego stylu kodu. Należy zamienić linię goto finish;z break;.
— Stunner

4

Oto sposób, w jaki robię to przy użyciu frameworka 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;  

}

Mam nadzieję, że to komuś pomoże.


4

możesz użyć tej funkcji, aby rozwiązać ten problem.

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

2

Oto szybka wersja odpowiedzi Walty Yeunga :

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

1

Właściwie świetny framework MWFeedParser Michaela Waterfall (odniósł się do jego odpowiedzi) został rozwidlony przez rmchaara, który zaktualizował go z obsługą ARC!

Możesz go znaleźć na Github tutaj

Naprawdę działa świetnie, użyłem metody stringByDecodingHTMLEntities i działa bez zarzutu.


To rozwiązuje problemy z ARC - ale wprowadza pewne ostrzeżenia. Myślę, że można je bezpiecznie zignorować?
— Robert J. Clegg

0

Jakbyś potrzebował innego rozwiązania! Ten jest dość prosty i dość skuteczny:

@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

0

Jeśli masz odniesienie do jednostki znakowej jako ciąg, np. @"2318"Możesz wyodrębnić przekodowany ciąg NSString z poprawnym znakiem Unicode za pomocą strtoul;

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

0

Szybka 3 wersja odpowiedzi Jugale'a

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
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.