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.