Giải mã ký tự HTML trong Objective-C / Cocoa Touch


103

Trước hết, tôi thấy điều này: Objective C HTML thoát / unescape , nhưng nó không hoạt động với tôi.

Các ký tự được mã hóa của tôi (đến từ nguồn cấp dữ liệu RSS, btw) trông như thế này: &

Tôi đã tìm kiếm trên mạng và tìm thấy các cuộc thảo luận có liên quan, nhưng không có bản sửa lỗi nào cho bảng mã cụ thể của tôi, tôi nghĩ chúng được gọi là các ký tự thập lục phân.


3
Nhận xét này là sáu tháng sau câu hỏi ban đầu, vì vậy những người tình cờ gặp câu hỏi này tìm kiếm câu trả lời và giải pháp sẽ nhiều hơn. Một câu hỏi tương tự mới xuất hiện gần đây mà tôi đã trả lời stackoverflow.com/questions/2254862/… Nó sử dụng RegexKitLite và Blocks để thực hiện tìm kiếm và thay thế &#...;trong một chuỗi bằng ký tự tương đương của nó.
johne

Cụ thể là gì "không hoạt động"? Tôi không thấy bất kỳ điều gì trong câu hỏi này không trùng lặp với câu hỏi trước đó.
Peter Hosey

Đó là số thập phân. Hệ thập lục phân là 8.
kennytm

Sự khác biệt giữa thập phân và thập lục phân là số thập phân đó là cơ số 10, trong khi hệ thập lục phân là cơ số 16. “38” là một số khác nhau trong mỗi cơ sở; trong cơ số 10, nó là 3 × 10 + 8 × 1 = ba mươi tám, trong khi trong cơ số 16, nó là 3 × 16 + 8 × 1 = năm mươi sáu. Các chữ số cao hơn là (bội số của) lũy thừa cao hơn của cơ số; chữ số nguyên thấp nhất là cơ số 0 (= 1), chữ số cao hơn tiếp theo là cơ số 1 ​​(= cơ số), chữ số tiếp theo là cơ số ** 2 (= cơ số * cơ số), v.v. Đây là phép tính lũy thừa.
Peter Hosey

Câu trả lời:


46

Chúng được gọi là Tham chiếu Thực thể Nhân vật . Khi chúng ở dạng &#<number>;chúng được gọi là tham chiếu thực thể số . Về cơ bản, đó là một biểu diễn chuỗi của byte cần được thay thế. Trong trường hợp của &#038;, nó đại diện cho ký tự có giá trị 38 trong lược đồ mã hóa ký tự ISO-8859-1, nghĩa là &.

Lý do ký hiệu và phải được mã hóa trong RSS là nó là một ký tự đặc biệt dành riêng.

Những gì bạn cần làm là phân tích cú pháp chuỗi và thay thế các thực thể bằng một byte khớp với giá trị giữa &#;. Tôi không biết bất kỳ cách tuyệt vời nào để thực hiện việc này trong mục tiêu C, nhưng câu hỏi tràn ngăn xếp này có thể giúp ích một số.

Chỉnh sửa: Kể từ khi trả lời câu hỏi này hai năm trước, có một số giải pháp tuyệt vời; xem câu trả lời của @Michael Waterfall bên dưới.


2
+1 Tôi vừa định gửi cùng một câu trả lời (bao gồm các liên kết giống nhau, không hơn không kém!)
e.James

"Về cơ bản, đó là một biểu diễn chuỗi của byte cần được thay thế." Giống nhân vật hơn. Đây là văn bản, không phải dữ liệu; khi chuyển đổi văn bản thành dữ liệu, ký tự có thể chiếm nhiều byte, tùy thuộc vào ký tự và bảng mã.
Peter Hosey

Cảm ơn vi đa trả lơi. Bạn đã nói "nó đại diện cho ký tự có giá trị 38 trong lược đồ mã hóa ký tự ISO-8859-1, là &". Bạn có chắc chắn về điều đó không? Bạn có liên kết đến bảng ký tự kiểu này không? Bởi vì từ những gì tôi nhớ lại đó là một câu trích dẫn duy nhất.
treznik

vi.wikipedia.org/wiki/ISO/IEC_8859-1#ISO-8859-1 hoặc chỉ cần nhập & # 038; vào google.
Matt Bridges

và những gì về & amp; hoặc & sao chép; ký hiệu?
vokilam

162

Kiểm tra danh mục NSString của tôi cho HTML . Dưới đây là các phương pháp có sẵn:

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

3
Dude, chức năng tuyệt vời. Phương pháp stringByDecodingXMLEntities của bạn đã làm nên ngày của tôi! Cảm ơn!
Brian Moeskau

3
Không sao;) Rất vui vì bạn thấy nó hữu ích!
Michael Waterfall

4
Sau vài giờ tìm kiếm, tôi biết rằng đây là cách duy nhất để làm điều đó thực sự hiệu quả. NSString đã quá hạn cho một phương thức chuỗi có thể thực hiện việc này. Làm tốt.
Adam Eberbach

1
Tôi nhận thấy (2) trên giấy phép của Michael quá hạn chế đối với trường hợp sử dụng của tôi, vì vậy tôi đã sử dụng giải pháp của Nikita. Bao gồm ba tệp được cấp phép Apache-2.0 từ hộp công cụ của google hoạt động tuyệt vời đối với tôi.
jaime

10
Mã bản cập nhật cho ARC sẽ được thuận tiện .. Xcode là ném tấn lỗi ARC và cảnh báo về xây dựng
Matej

52

Câu chuyện của Daniel về cơ bản là rất hay và tôi đã khắc phục một số vấn đề ở đó:

  1. đã loại bỏ ký tự bỏ qua cho NSSCanner (nếu không khoảng cách giữa hai thực thể liên tục sẽ bị bỏ qua

    [máy quét setCharactersToBeSkipped: nil];

  2. đã sửa lỗi phân tích cú pháp khi có các ký hiệu '&' bị cô lập (tôi không chắc đâu là đầu ra 'chính xác' cho điều này, tôi chỉ so sánh nó với firefox):

ví dụ

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

đây là mã đã sửa đổi:

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

Đây phải là câu trả lời chắc chắn cho câu hỏi !! Cảm ơn!
boliva

Điều này làm việc tuyệt vời. Rất tiếc, mã của câu trả lời được xếp hạng cao nhất không hoạt động nữa do sự cố ARC, nhưng điều này thì có.
Ted Kulp

@TedKulp nó hoạt động tốt, bạn chỉ cần tắt ARC cho mỗi tệp. stackoverflow.com/questions/6646052/…
Kyle

Tôi sẽ thích bạn hai lần nếu tôi có thể.
Kibitz503

Swift dịch cho mọi người vẫn ghé thăm câu hỏi này trong 2016+: stackoverflow.com/a/35303635/1153630
Max Chuquimia

46

Kể từ iOS 7, bạn có thể giải mã các ký tự HTML nguyên bản bằng cách sử dụng một NSAttributedStringvới NSHTMLTextDocumentTypethuộc tính:

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

Chuỗi phân bổ đã giải mã bây giờ sẽ được hiển thị dưới dạng:  & & <> ™ © ♥ ♣ ♠ ♦.

Lưu ý: Điều này sẽ chỉ hoạt động nếu được gọi trên luồng chính.


6
câu trả lời tốt nhất nếu bạn không cần hỗ trợ iOS 6 trở lên
jcesarmobile

1
không, không phải là tốt nhất nếu ai đó muốn mã hóa nó trên thread bg; O
badeleux

4
Điều này hoạt động để giải mã một thực thể, nhưng nó cũng làm rối một dấu gạch ngang không được mã hóa.
Andrew

Điều này buộc phải xảy ra trên luồng chính. Vì vậy, bạn có thể không muốn làm điều này nếu bạn không cần thiết.
Keith Smiley,

Nó chỉ treo GUI khi có vấn đề với UITableView. Do đó, không hoạt động chính xác.
Asif Bilal

35

Dường như không ai đề cập đến một trong những tùy chọn đơn giản nhất: Hộp công cụ của Google dành cho Mac
(Mặc dù có tên, tính năng này cũng hoạt động trên 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;

Và tôi chỉ phải bao gồm ba tệp trong dự án: tiêu đề, triển khai và GTMDefines.h.


Tôi đã bao gồm ba tập lệnh này, nhưng làm thế nào tôi có thể sử dụng nó bây giờ?
Borut Tomazin

@ borut-t [myString gtm_stringByUnescapingFromHTML]
Nikita Rybak

2
Tôi đã chọn chỉ bao gồm ba tệp đó, vì vậy tôi cần phải làm điều này để làm cho nó tương thích với arc: code.google.com/p/google-toolbox-for-mac/wiki/ARC_Compatibility
jaime

tôi có thể nói đây là giải pháp đơn giản nhất và nhẹ nhất cho đến nay
lensovet

Tôi ước tôi có thể làm cho điều này hoạt động hoàn toàn. Nó dường như bỏ qua nhiều người trong số họ trong chuỗi của tôi.
Joseph Toronto

17

Tôi phải đăng cái này trên GitHub hoặc cái gì đó. Điều này nằm trong một danh mục của NSString, sử dụng NSScannerđể triển khai và xử lý cả các thực thể ký tự số thập phân và thập phân cũng như các thực thể ký hiệu thông thường.

Ngoài ra, nó xử lý các chuỗi không đúng định dạng (khi bạn có & theo sau là một chuỗi ký tự không hợp lệ) tương đối duyên dáng, điều này hóa ra rất quan trọng trong ứng dụng đã phát hành của tôi sử dụng mã này.

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

Đoạn mã rất hữu ích, tuy nhiên, nó có một vài vấn đề đã được Walty giải quyết. Cám ơn vì đã chia sẻ!
Thác nước Michael

bạn có biết cách hiển thị các ký hiệu lambda, mu, nu, pi bằng cách giải mã các thực thể XML của chúng như & micro; ... Vân vân ????
chinthakad

Bạn nên tránh sử dụng gotos làm kiểu mã khủng khiếp của nó. Bạn nên thay thế dòng goto finish;bằng break;.
Stunner

4

Đây là cách tôi thực hiện bằng cách sử dụng 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;  

}

Hy vọng điều này sẽ giúp một ai đó.


4

bạn có thể chỉ sử dụng chức năng này để giải quyết vấn đề này.

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

Đây là phiên bản Swift của câu trả lời của Walty Yeung :

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

    func stringByDecodingXMLEntities() -> String {

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

        var result = ""

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

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

        repeat {
            var nonEntityString: NSString? = nil

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

            if scanner.atEnd {
                break
            }

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

            if !didBreak {

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

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

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

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

        } while (!scanner.atEnd)

        return result
    }
}

1

Trên thực tế, khuôn khổ MWFeedParser tuyệt vời của Michael Waterfall (được gọi là câu trả lời của anh ấy) đã được chia nhỏ bởi rmchaara, người đã cập nhật nó với sự hỗ trợ của ARC!

Bạn có thể tìm thấy nó trong Github tại đây

Nó thực sự hoạt động tuyệt vời, tôi đã sử dụng phương thức stringByDecodingHTMLEntities và hoạt động hoàn hảo.


Điều đó khắc phục sự cố ARC - nhưng đưa ra một số cảnh báo. Tôi nghĩ rằng nó là an toàn để bỏ qua chúng?
Robert J. Clegg.

0

Như thể bạn cần một giải pháp khác! Cách này khá đơn giản và khá hiệu quả:

@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

Nếu bạn có Tham chiếu Thực thể Ký tự dưới dạng một chuỗi, ví dụ: @"2318"bạn có thể trích xuất một Chuỗi NS được mã hóa với ký tự unicode chính xác bằng cách sử dụng 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

Swift 3 phiên bản câu trả lời của Jugale

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
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.