การถอดรหัสอักขระ HTML ใน Objective-C / Cocoa Touch


103

ก่อนอื่นฉันพบสิ่งนี้: Objective C HTML escape / unescapeแต่มันไม่ได้ผลสำหรับฉัน

อักขระที่เข้ารหัสของฉัน (มาจาก RSS feed, btw) มีลักษณะดังนี้: &

ฉันค้นหาทั่วเน็ตและพบการสนทนาที่เกี่ยวข้อง แต่ไม่มีการแก้ไขสำหรับการเข้ารหัสเฉพาะของฉันฉันคิดว่าพวกเขาเรียกว่าอักขระเลขฐานสิบหก


3
ความคิดเห็นนี้ใช้เวลาหกเดือนหลังจากคำถามเดิมดังนั้นจึงเป็นเรื่องที่มากกว่าสำหรับผู้ที่พบคำถามนี้โดยมองหาคำตอบและวิธีแก้ปัญหา คำถามที่คล้ายกันเกิดขึ้นเมื่อไม่นานมานี้ที่ฉันตอบstackoverflow.com/questions/2254862/… มันใช้ RegexKitLite และ Blocks เพื่อค้นหาและแทนที่&#...;ในสตริงด้วยอักขระที่เท่ากัน
johne

อะไรที่“ ใช้ไม่ได้” โดยเฉพาะ ฉันไม่เห็นสิ่งใดในคำถามนี้ที่ไม่ซ้ำกับคำถามก่อนหน้านั้น
Peter Hosey

มันเป็นทศนิยม เลขฐานสิบหกคือ8.
kennytm

ความแตกต่างระหว่างเลขฐานสิบและเลขฐานสิบหกที่เป็นเลขฐานสิบคือฐาน 10 ในขณะที่เลขฐานสิบหกคือฐาน 16 “ 38” เป็นจำนวนที่แตกต่างกันในแต่ละฐาน ในฐาน 10 มันคือ 3 × 10 + 8 × 1 = สามสิบแปดในขณะที่ฐาน 16 มันคือ 3 × 16 + 8 × 1 = ห้าสิบหก ตัวเลขที่สูงกว่าคือ (ทวีคูณ) กำลังที่สูงขึ้นของฐาน ตัวเลขทั้งหมดต่ำสุดคือฐาน0 (= 1) ตัวเลขที่สูงกว่าถัดไปคือฐาน 1 (= ฐาน) ตัวเลขถัดไปคือฐาน ** 2 (= ฐาน * ฐาน) ฯลฯ นี่คือเลขชี้กำลังในการทำงาน
Peter Hosey

คำตอบ:


46

เหล่านี้จะเรียกว่าตัวละคร Entity อ้างอิง เมื่อพวกเขาจะใช้รูปแบบของ&#<number>;พวกเขาจะเรียกอ้างอิงนิติบุคคลที่เป็นตัวเลข โดยทั่วไปแล้วจะเป็นการแสดงสตริงของไบต์ที่ควรแทนที่ ในกรณีที่&#038;จะแสดงถึงตัวละครที่มีมูลค่า 38 ใน ISO-8859-1 &อักขระการเข้ารหัสรูปแบบซึ่งเป็น

เหตุผลที่ต้องเข้ารหัสเครื่องหมายแอมเพอร์แซนด์ใน RSS เนื่องจากเป็นอักขระพิเศษที่สงวนไว้

สิ่งที่คุณต้องทำคือการแยกสตริงและแทนที่หน่วยงานที่มีการจับคู่ไบต์ค่าระหว่างและ&# ;ฉันไม่รู้วิธีที่ยอดเยี่ยมในการทำสิ่งนี้ในวัตถุประสงค์ C แต่คำถามล้นสแต็กนี้อาจช่วยได้บ้าง

แก้ไข: ตั้งแต่ตอบคำถามนี้เมื่อสองปีก่อนมีวิธีแก้ปัญหาที่ยอดเยี่ยม ดูคำตอบของ @Michael Waterfall ด้านล่าง


2
+1 ฉันกำลังจะส่งคำตอบเดียวกันทุก
ประการ

“ โดยพื้นฐานแล้วมันเป็นการแสดงสตริงของไบต์ที่ควรจะแทนที่” เหมือนตัวละคร นี่คือข้อความไม่ใช่ข้อมูล เมื่อแปลงข้อความเป็นข้อมูลอักขระอาจใช้หลายไบต์ขึ้นอยู่กับอักขระและการเข้ารหัส
Peter Hosey

ขอบคุณสำหรับการตอบกลับ. คุณบอกว่า "แสดงถึงอักขระที่มีค่า 38 ในโครงร่างการเข้ารหัสอักขระ ISO-8859-1 ซึ่งก็คือ &" คุณแน่ใจหรือไม่? คุณมีลิงค์ไปยังตารางอักขระประเภทนี้หรือไม่? เพราะจากสิ่งที่ฉันจำได้ว่าเป็นคำพูดเดียว
treznik

en.wikipedia.org/wiki/ISO/IEC_8859-1#ISO-8859-1หรือเพียงพิมพ์ & # 038; ลงใน Google
Matt Bridges

แล้ว & amp; หรือ & copy; สัญลักษณ์?
vokilam

162

ตรวจสอบของฉันประเภทการ NSString เพื่อใช้ HTML วิธีการมีดังนี้:

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

3
เพื่อนฟังก์ชั่นดีเยี่ยม วิธี stringByDecodingXMLEntities ของคุณทำให้วันของฉัน! ขอบคุณ!
Brian Moeskau

3
ไม่มีปัญหา;) ดีใจที่คุณพบว่ามีประโยชน์!
น้ำตกไมเคิล

4
หลังจากค้นหาไม่กี่ชั่วโมงฉันรู้ว่านี่เป็นวิธีเดียวที่จะทำได้จริง NSString เกินกำหนดสำหรับเมธอดสตริงที่สามารถทำได้ ทำได้ดี.
Adam Eberbach

1
ฉันพบว่า (2) ในใบอนุญาตของ Michael เข้มงวดเกินไปสำหรับกรณีการใช้งานของฉันดังนั้นฉันจึงใช้วิธีแก้ปัญหาของ Nikita การรวมไฟล์ที่ได้รับอนุญาต Apache-2.0 สามไฟล์จากกล่องเครื่องมือของ Google เหมาะสำหรับฉัน
jaime

10
การอัปเดตโค้ดสำหรับ ARC จะเป็นประโยชน์ .. Xcode มีข้อผิดพลาด ARC และคำเตือนมากมายในการสร้าง
Matej

52

โดยพื้นฐานแล้ว Daniel นั้นดีมากและฉันได้แก้ไขปัญหาบางอย่างที่นั่น:

  1. ลบอักขระการข้ามสำหรับ NSSCanner (มิฉะนั้นช่องว่างระหว่างสองเอนทิตีต่อเนื่องจะถูกละเว้น

    [เครื่องสแกน setCharactersToBeSkipped: nil];

  2. แก้ไขการแยกวิเคราะห์เมื่อมีสัญลักษณ์ '&' ที่แยก (ฉันไม่แน่ใจว่าผลลัพธ์ที่ 'ถูกต้อง' สำหรับสิ่งนี้คืออะไรฉันเพิ่งเปรียบเทียบกับ firefox):

เช่น

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

นี่คือรหัสที่แก้ไข:

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

นี่น่าจะเป็นคำตอบของคำถาม !! ขอบคุณ!
boliva

สิ่งนี้ได้ผลดี น่าเสียดายที่รหัสคำตอบที่ได้รับคะแนนสูงสุดไม่ทำงานอีกต่อไปเนื่องจากปัญหา ARC แต่สิ่งนี้ทำได้
Ted Kulp

@TedKulp มันใช้งานได้ดีคุณเพียงแค่ปิดการใช้งาน ARC ต่อไฟล์ stackoverflow.com/questions/6646052/…
Kyle

ฉันจะยกนิ้วให้คุณสองครั้งถ้าทำได้
Kibitz503

คำแปลอย่างรวดเร็วสำหรับผู้ที่ยังคงตอบคำถามนี้ในปี 2559+: stackoverflow.com/a/35303635/1153630
Max Chuquimia

46

ในฐานะของ iOS 7 คุณสามารถถอดรหัสตัวอักษร HTML กำเนิดโดยใช้NSAttributedStringกับNSHTMLTextDocumentTypeแอตทริบิวต์:

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

สตริงแอททริบิวต์ที่ถอดรหัสจะแสดงเป็น:  & & <> ™©♥♣♠♦

หมายเหตุ:จะใช้ได้เฉพาะเมื่อถูกเรียกในเธรดหลัก


6
คำตอบที่ดีที่สุดหากคุณไม่ต้องการรองรับ iOS 6 ขึ้นไป
jcesarmobile

1
ไม่ไม่ใช่สิ่งที่ดีที่สุดหากมีคนต้องการเข้ารหัสบน bg thread O
badeleux

4
วิธีนี้ใช้ได้ผลกับการถอดรหัสเอนทิตี แต่ก็ทำให้เส้นประที่ไม่ได้เข้ารหัสสับสน
Andrew

สิ่งนี้ถูกบังคับให้เกิดขึ้นบนเธรดหลัก ดังนั้นคุณอาจไม่ต้องการทำสิ่งนี้ถ้าคุณไม่จำเป็นต้องทำ
Keith Smiley

เพียงแค่แฮงค์ GUI เมื่อเป็นเรื่องของ UITableView ดังนั้นทำงานไม่ถูกต้อง
Asif Bilal

35

ดูเหมือนจะไม่มีใครพูดถึงหนึ่งในตัวเลือกที่ง่ายที่สุด นั่นคือGoogle Toolbox สำหรับ Mac
(แม้จะมีชื่อ แต่ก็ใช้ได้กับ 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;

และฉันต้องรวมไฟล์เพียงสามไฟล์ในโครงการ: ส่วนหัวการใช้งานและGTMDefines.h.


ฉันได้รวมสามสคริปต์นี้แล้ว แต่ฉันจะใช้มันตอนนี้ได้อย่างไร?
Borut Tomazin

@ borut-t [myString gtm_stringByUnescapingFromHTML]
Nikita Rybak

2
ฉันเลือกที่จะรวมเฉพาะไฟล์ทั้งสามนี้ดังนั้นฉันจึงต้องทำสิ่งนี้เพื่อให้เข้ากันได้กับ arc: code.google.com/p/google-toolbox-for-mac/wiki/ARC_Compatibility
jaime

ฉันต้องบอกว่านี่เป็นวิธีแก้ปัญหาที่ง่ายและมีน้ำหนักเบาที่สุดเท่าที่ผ่านมา
lensovet

ฉันหวังว่าฉันจะทำให้สิ่งนี้ทำงานได้อย่างสมบูรณ์ ดูเหมือนว่าจะข้ามผ่านหลายคนในสตริงของฉัน
Joseph Toronto

17

ฉันควรจะโพสต์สิ่งนี้บน GitHub หรืออะไรสักอย่าง สิ่งนี้อยู่ในหมวดหมู่ของ NSString ใช้NSScannerสำหรับการนำไปใช้งานและจัดการทั้งเอนทิตีอักขระตัวเลขฐานสิบหกและฐานสิบรวมทั้งรายการสัญลักษณ์ตามปกติ

นอกจากนี้ยังจัดการสตริงที่มีรูปแบบไม่ถูกต้อง (เมื่อคุณมี & ตามด้วยลำดับอักขระที่ไม่ถูกต้อง) ค่อนข้างสง่างามซึ่งกลายเป็นสิ่งสำคัญในแอปที่เปิดตัวของฉันที่ใช้รหัสนี้

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

โค้ดที่มีประโยชน์มาก แต่ก็มีปัญหาสองสามอย่างที่ Walty ได้รับการแก้ไข ขอบคุณสำหรับการแบ่งปัน!
น้ำตกไมเคิล

คุณรู้วิธีแสดงสัญลักษณ์ lambda, mu, nu, pi โดยการถอดรหัสเอนทิตี XML เช่น & micro; ... ฯลฯ ????
chinthakad

คุณควรหลีกเลี่ยงการใช้gotos เป็นรูปแบบโค้ดที่แย่มาก คุณควรเปลี่ยนสายกับgoto finish;break;
Stunner

4

นี่คือวิธีที่ฉันทำโดยใช้กรอบ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;  

}

หวังว่านี่จะช่วยใครบางคนได้


4

คุณสามารถใช้ฟังก์ชันนี้เพื่อแก้ปัญหานี้ได้

+ (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เวอร์ชัน Swift :

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

จริงๆแล้วเฟรมเวิร์ก MWFeedParser ที่ยอดเยี่ยมของ Michael Waterfall (อ้างถึงคำตอบของเขา) ถูกแยกโดย rmchaara ที่อัปเดตด้วยการสนับสนุน ARC!

คุณสามารถค้นหาได้ใน Github ที่นี่

มันใช้งานได้ดีจริงๆฉันใช้วิธี stringByDecodingHTMLEntities และทำงานได้อย่างไม่มีที่ติ


ซึ่งช่วยแก้ปัญหา ARC แต่แนะนำคำเตือนบางอย่าง ฉันคิดว่ามันปลอดภัยที่จะเพิกเฉยต่อพวกเขา?
Robert J.Clegg

0

ราวกับว่าคุณต้องการทางออกอื่น! อันนี้ค่อนข้างง่ายและมีประสิทธิภาพมาก:

@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

หากคุณมีการอ้างอิงเอนทิตีอักขระเป็นสตริงเช่น@"2318"คุณสามารถแยก NSString ที่เข้ารหัสด้วยอักขระยูนิโคดที่ถูกต้องโดยใช้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 เวอร์ชัน Swift 3

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
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.