Objective-C / Cocoa Touch'ta HTML karakter kod çözme


103

Her şeyden önce şunu buldum: Objective C HTML escape / unescape , ancak benim için çalışmıyor.

Kodlanmış karakterlerim (bir RSS beslemesinden gelir, btw) şöyle görünür: &

Ağın her yerinde arama yaptım ve ilgili tartışmalar buldum, ancak özel kodlamam için bir düzeltme yok, sanırım bunlara onaltılık karakterler deniyor.


3
Bu yorum, asıl sorudan altı ay sonradır, bu nedenle bu soruya bir cevap ve çözüm arayanlar için daha çok rastlanır. Kısa süre önce çok benzer bir soru geldi ve stackoverflow.com/questions/2254862/… 'yi yanıtladım &#...;. Bir dizedeki eşdeğer karakterle arama yapmak ve onu değiştirmek için RegexKitLite ve Blocks'u kullanıyor .
johne

Özellikle "işe yaramayan" nedir? Bu soruda daha önceki sorunun aynısı olmayan hiçbir şey görmüyorum.
Peter Hosey

Ondalık. Onaltılıktır 8.
kennytm

Ondalık ve onaltılık arasındaki fark, bu ondalık taban-10 iken, onaltılık taban-16'dır. “38” her tabanda farklı bir sayıdır; 10 tabanında, 3 × 10 + 8 × 1 = otuz sekiz, oysa taban-16'da 3 × 16 + 8 × 1 = elli altı. Daha yüksek basamaklar, tabanın (katları) daha yüksek güçleridir; en düşük tam rakam 0 tabanıdır (= 1), sonraki yüksek rakam 1 tabanıdır (= taban), sonraki basamak ** 2 tabanıdır (= taban * taban), vb. Bu, işyerindeki üslemedir.
Peter Hosey

Yanıtlar:


46

Bunlara Karakter Varlık Referansları denir . Formunu &#<number>;aldıklarında sayısal varlık referansları olarak adlandırılır . Temel olarak, ikame edilmesi gereken baytın bir dize temsilidir. Durumunda, &#038;ISO-8859-1 karakter kodlama şemasında 38 değerine sahip karakteri temsil eder, yani &.

Ve işaretinin RSS'de kodlanmasının nedeni, ayrılmış bir özel karakter olmasıdır.

Ne yapmanız gereken dizeyi ayrıştırmak ve aralarında değerle eşleşen bir byte ile varlıkları değiştirin &#ve ;. Hedef C'de bunu yapmanın harika bir yolunu bilmiyorum, ancak bu yığın taşması sorusu biraz yardımcı olabilir.

Düzenleme: Bunu yaklaşık iki yıl önce yanıtladığından beri bazı harika çözümler var; @Michael Şelalesi'nin aşağıdaki cevabına bakınız.


2
+1 Tam olarak aynı cevabı göndermek üzereydim (aynı bağlantılar dahil, daha az değil!)
e.James

"Temel olarak, değiştirilmesi gereken baytın bir dize temsilidir." Daha çok karakter gibi. Bu metin, veri değil; Metni veriye dönüştürdükten sonra karakter, karaktere ve kodlamaya bağlı olarak çok sayıda baytı işgal edebilir.
Peter Hosey

Cevap için teşekkürler. "ISO-8859-1 karakter kodlama düzeninde 38 değerine sahip karakteri temsil ediyor, bu &" dediniz. Bundan emin misin? Bu tür bir karakter tablosuna bir bağlantınız var mı? Çünkü hatırladığım kadarıyla bu tek bir alıntıydı.
treznik


peki ya & amp; veya & kopyala; semboller?
vokilam

162

Benim göz atın HTML NSString kategorisini . İşte mevcut yöntemler:

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

3
Dostum, mükemmel fonksiyonlar. StringByDecodingXMLEntities yönteminiz beni çok mutlu etti! Teşekkürler!
Brian Moeskau

3
Sorun değil;) Yararlı bulduğunuza sevindim!
Michael Şelalesi

4
Birkaç saatlik aramadan sonra, gerçekten işe yarayan bunu yapmanın tek yolunun bu olduğunu biliyorum. NSString, bunu yapabilen bir dize yöntemi için gecikmiş durumda. Aferin.
Adam Eberbach

1
Michael'ın lisansında (2) kullanım durumum için çok kısıtlayıcı buldum, bu yüzden Nikita'nın çözümünü kullandım. Google araç kutusundan üç Apache-2.0 lisanslı dosya eklemek benim için harika çalışıyor.
jaime

10
ARC için kod güncellemesi kullanışlı olurdu .. Xcode, derlemede tonlarca ARC hatası ve uyarı atıyor
Matej

52

Daniel'in kitabı temelde çok güzel ve orada birkaç sorunu çözdüm:

  1. NSSCanner için atlama karakteri kaldırıldı (aksi takdirde iki sürekli varlık arasındaki boşluklar göz ardı edilirdi

    [tarayıcı setCharactersToBeSkipped: nil];

  2. izole edilmiş '&' sembolleri olduğunda ayrıştırmayı düzeltti (bunun için 'doğru' çıktının ne olduğundan emin değilim, sadece firefox ile karşılaştırdım):

Örneğin

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

işte değiştirilmiş 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;
}

Bu sorunun kesin cevabı olmalı !! Teşekkürler!
boliva

Bu harika çalıştı. Maalesef en yüksek puan alan yanıtlayıcının kodu, ARC sorunları nedeniyle artık çalışmıyor, ancak bu çalışıyor.
Ted Kulp

@TedKulp gayet iyi çalışıyor, sadece dosya başına ARC'yi devre dışı bırakmanız gerekiyor. stackoverflow.com/questions/6646052/…
Kyle

Yapabilseydim seni iki kez yaşarım.
Kibitz503

2016 ve sonrasında hala bu soruyu ziyaret eden kişiler için hızlı çeviri: stackoverflow.com/a/35303635/1153630
Max Chuquimia

46

İOS 7'den itibaren, NSAttributedStringşu NSHTMLTextDocumentTypeözniteliğe sahip bir kullanarak HTML karakterlerinin kodunu yerel olarak çözebilirsiniz :

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

Kodu çözülen ilişkilendirilmiş dizi şimdi şu şekilde görüntülenecektir:  & & <> ™ © ♥ ♣ ♠ ♦.

Not: Bu, yalnızca ana iş parçacığında çağrılırsa çalışır.


6
iOS 6 ve daha eski sürümleri desteklemeniz gerekmiyorsa en iyi yanıt
jcesarmobile

1
hayır, birisi onu bg iş parçacığına kodlamak isterse en iyisi değil; O
badeleux

4
Bu, bir varlığın kodunu çözmek için çalıştı, ancak aynı zamanda kodlanmamış bir kısa çizgi de bozdu.
Andrew

Bu, ana iş parçacığında olmak zorunda. Yani mecbur değilseniz muhtemelen bunu yapmak istemezsiniz.
Keith Smiley

UITableView söz konusu olduğunda sadece GUI'yi kilitler. Bu nedenle, düzgün çalışmıyor.
Asif Bilal

35

Hiç kimse en basit seçeneklerden birinden bahsetmiyor gibi görünüyor: Mac için Google Araç Kutusu
(İsmine rağmen, bu iOS'ta da çalışıyor.)

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;

Ve projeye sadece üç dosya eklemek zorunda kaldım: başlık, uygulama ve GTMDefines.h.


Bu üç komut dosyasını ekledim, ancak şimdi nasıl kullanabilirim?
Borut Tomazin

@ borut-t [myString gtm_stringByUnescapingFromHTML]
Nikita Rybak

2
Yalnızca bu üç dosyayı dahil etmeyi seçtim, bu yüzden onu arc ile uyumlu hale getirmek için bunu yapmam gerekiyordu: code.google.com/p/google-toolbox-for-mac/wiki/ARC_Compatibility
jaime

Bunun şu ana kadarki en basit ve en hafif çözüm olduğunu söylemeliyim
lensovet

Keşke bunu tamamen çalıştırabilseydim. Dizelerimde çoğunu atlıyor gibi görünüyor.
Joseph Toronto

17

Bunu GitHub'a falan göndermeliyim. Bu, NSString kategorisine girer NSScanner, uygulama için kullanır ve hem onaltılık hem de ondalık sayısal karakter varlıklarını ve olağan sembolik olanları işler.

Ayrıca, hatalı biçimlendirilmiş dizeleri (bir & ardından geçersiz bir karakter dizisine sahip olduğunuzda) nispeten zarif bir şekilde işler ve bu, bu kodu kullanan yayınlanmış uygulamamda çok önemli olduğu ortaya çıktı .

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

Çok kullanışlı bir kod parçası, ancak Walty tarafından ele alınan birkaç sorunu var. Paylaşım için teşekkürler!
Michael Şelalesi

& micro gibi XML varlıklarının kodunu çözerek lambda, mu, nu, pi sembollerini göstermenin bir yolunu biliyor musunuz? ... vb ????
chinthakad

gotoKötü kod stili olarak s kullanmaktan kaçınmalısınız . Satırı goto finish;ile değiştirmelisiniz break;.
Stunner

4

Bunu RegexKitLite çerçevesini kullanarak yapmamın yolu :

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

}

Umarım bu birine yardımcı olur.


4

Bu sorunu çözmek için yalnızca bu işlevi kullanabilirsiniz.

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

Walty Yeung'un cevabının bir Swift versiyonu :

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

Aslında, Michael Waterfall'un harika MWFeedParser çerçevesi (cevabına atıfta bulunulur), ARC desteği ile güncelleyen rmchaara tarafından çatallandı!

Github'da burada bulabilirsiniz

Gerçekten harika çalışıyor, stringByDecodingHTMLEntities yöntemini kullandım ve kusursuz çalışıyor.


Bu ARC sorunlarını düzeltir - ancak bazı uyarılar getirir. Onları görmezden gelmek güvenli mi?
Robert J. Clegg

0

Sanki başka bir çözüme ihtiyacınız varmış gibi! Bu oldukça basit ve oldukça etkilidir:

@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

Karakter Varlık Referansına bir karakter dizisi olarak @"2318"sahipseniz , örneğin , yeniden kodlanmış bir NSString'i kullanarak doğru unicode karakteriyle çıkartabilirsiniz 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

Jugale'nin cevabının Swift 3 versiyonu

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
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.