Objective-C / Cocoa Touch에서 HTML 문자 디코딩


103

우선, 나는 이것을 발견했습니다. Objective C HTML escape / unescape 이지만 작동하지 않습니다.

내 인코딩 된 문자 (RSS 피드, btw에서 가져옴)는 다음과 같습니다. &

인터넷 전체를 검색하고 관련 토론을 찾았지만 특정 인코딩에 대한 수정 사항이 없어서 16 진수 문자라고 생각합니다.


3
이 댓글은 원래 질문이 나온 지 6 개월 후이므로 답과 해결책을 찾는이 질문을 우연히 발견 한 사람들을위한 것입니다. 아주 비슷한 질문이 최근에 올라 왔는데 내가 stackoverflow.com/questions/2254862/에 대답했습니다… 그것은 RegexKitLite와 Blocks를 사용하여 검색을 수행 &#...;하고 그에 상응하는 문자로 문자열을 대체 합니다.
johne 2010

구체적으로 "작동하지 않는"것은 무엇입니까? 이 질문에는 이전 질문과 중복되지 않는 내용이 없습니다.
Peter Hosey

십진수입니다. 16 진수는 8.
kennytm

10 진수와 16 진수의 차이점은 10 진수는 10 진수이고 16 진수는 16 진수입니다. “38”은 각 염기에서 다른 숫자입니다. 10 진법에서는 3x10 + 8x1 = 38이고 16 진법에서는 3x16 + 8x1 = 56입니다. 더 높은 자릿수는베이스의 더 높은 거듭 제곱입니다. 가장 낮은 정수는 기본 0 (= 1), 다음 높은 숫자는 기본 1 (= 기본), 다음은 기본 ** 2 (= 기본 * 기본) 등입니다. 이것은 직장에서의 지수입니다.
Peter Hosey

답변:


46

이를 문자 엔티티 참조 라고 합니다. 형식을 취할 때 숫자 엔티티 참조&#<number>; 라고 합니다. 기본적으로 대체되어야하는 바이트의 문자열 표현입니다. 의 경우 ISO-8859-1 문자 인코딩 체계에서 값이 38 인 문자를 나타냅니다 .&#038;&

앰퍼샌드가 RSS로 인코딩되어야하는 이유는 예약 된 특수 문자이기 때문입니다.

해야 할 일은 문자열을 파싱하고 엔티티를과 사이의 값 &#과 일치하는 바이트로 바꾸는 것 ;입니다. 객관적인 C에서이 작업을 수행하는 좋은 방법은 없지만 이 스택 오버플로 질문 이 도움이 될 수 있습니다.

편집 : 약 2 년 전에 이에 대한 답변 이후 몇 가지 훌륭한 솔루션이 있습니다. 아래 @Michael Waterfall의 답변을 참조하십시오.


2
+1 나는 똑같은 대답 제출에 대해 단지 없었다 (더 적은, 같은 링크를 포함하여!)
e.James

"기본적으로 대체되어야하는 바이트의 문자열 표현입니다." 캐릭터와 더 비슷합니다. 이것은 데이터가 아니라 텍스트입니다. 텍스트를 데이터로 변환 할 때 문자 및 인코딩에 따라 문자가 여러 바이트를 차지할 수 있습니다.
Peter Hosey

답장을 보내 주셔서 감사합니다. "ISO-8859-1 문자 인코딩 체계에서 값이 38 인 문자, 즉 &"를 나타냅니다. 확실합니까? 이 유형의 문자표에 대한 링크가 있습니까? 내가 기억하는 것은 작은 따옴표이기 때문입니다.
treznik


& amp; 또는 & copy; 기호?
vokilam 2013

162

HTML에 대한NSString 카테고리를 확인하십시오 . 사용 가능한 방법은 다음과 같습니다.

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

3
야, 뛰어난 기능. 귀하의 stringByDecodingXMLEntities 메서드가 내 하루를 만들었습니다! 감사!
브라이언 Moeskau

3
문제 없습니다;) 유용하다는 것을 알게되어 기쁩니다!
Michael Waterfall

4
몇 시간 검색 후 나는 이것이 실제로 작동하는 유일한 방법이라는 것을 알고 있습니다. NSString은 이것을 할 수있는 문자열 메소드에 대해 기한이 지났습니다. 잘 했어.
Adam Eberbach 2011 년

1
Michael의 라이선스에서 (2)가 내 사용 사례에 비해 너무 제한적이라는 것을 알았으므로 Nikita의 솔루션을 사용했습니다. Google 도구 상자의 Apache-2.0 라이선스 파일 3 개를 포함하는 것이 저에게 효과적입니다.
jaime 2011

10
ARC에 대한 코드 업데이트가 편리 할 것입니다. Xcode는 빌드시 수많은 ARC 오류 및 경고를 던지고 있습니다.
Matej

52

Daniel의 작품은 기본적으로 매우 훌륭하며 몇 가지 문제를 해결했습니다.

  1. NSSCanner의 건너 뛰는 문자를 제거했습니다 (그렇지 않으면 두 개의 연속 엔티티 사이의 공백이 무시됩니다.

    [스캐너 setCharactersToBeSkipped : nil];

  2. 분리 된 '&'기호가있을 때 구문 분석을 수정했습니다 (이에 대한 '올바른'출력이 무엇인지 모르겠습니다. 방금 파이어 폭스와 비교했습니다).

예 :

    &#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 2010

이것은 훌륭하게 작동했습니다. 불행히도 가장 높은 등급의 답변 코드는 ARC 문제로 인해 더 이상 작동하지 않지만 작동합니다.
Ted Kulp

@TedKulp 잘 작동하며 파일 당 ARC를 비활성화하면됩니다. stackoverflow.com/questions/6646052/...
카일

할 수만 있다면 두 번 엄지 손가락을 치켜들겠습니다.
Kibitz503

여전히 2016+에서이 문제를 방문해 사람들을위한 스위프트 번역 : stackoverflow.com/a/35303635/1153630
최대 Chuquimia

46

iOS 7부터는 NSAttributedStringwith NSHTMLTextDocumentType속성 을 사용하여 기본적으로 HTML 문자를 디코딩 할 수 있습니다 .

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 2014 년

1
아니, 누군가가 BG 스레드에 인코딩 최고의 싶어하지 않을 경우, O
badeleux

4
이것은 엔터티를 디코딩하는 데 효과적이지만 인코딩되지 않은 대시도 엉망으로 만듭니다.
Andrew

이것은 주 스레드에서 강제로 발생합니다. 그래서 당신은 아마 당신이 할 필요가 없다면 이것을하고 싶지 않을 것입니다.
Keith Smiley

UITableView의 문제 일 때 GUI를 중단합니다. 따라서 올바르게 작동하지 않습니다.
Asif Bilal 2015

35

아무도 가장 간단한 옵션 중 하나 인 Mac 용 Google 도구 상자 를 언급하지 않는 것 같습니다
(이름에도 불구하고 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 2011 년

@ 보 루트-t [mystring에 gtm_stringByUnescapingFromHTML]
니키타 리박

2
이 세 파일 만 포함하도록 선택했기 때문에 arc와 호환되도록하려면이 작업을 수행해야했습니다. code.google.com/p/google-toolbox-for-mac/wiki/ARC_Compatibility
jaime

나는 이것이 지금까지 가장 간단하고 가벼운 솔루션입니다 '라고해야
lensovet

이 작업이 완전히 작동하기를 바랍니다. 내 문자열에서 많은 것을 건너 뛰는 것 같습니다.
Joseph Toronto

17

나는 이것을 GitHub 또는 다른 것에 게시해야한다. 이것은 NSString의 범주에 속 NSScanner하고 구현에 사용 되며 일반적인 기호 엔티티뿐만 아니라 16 진수 및 10 진수 문자 엔티티를 모두 처리합니다.

또한 이 코드를 사용하는 내 출시 된 앱 에서 중요한 것으로 판명 된 잘못된 형식의 문자열 (& 뒤에 잘못된 문자 시퀀스가 ​​오는 경우)을 비교적 우아하게 처리 합니다.

- (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가 해결 한 몇 가지 문제가 있습니다. 공유해 주셔서 감사합니다!
Michael Waterfall

& micro;와 같은 XML 엔티티를 디코딩하여 lambda, mu, nu, pi 기호를 표시하는 방법을 알고 있습니까? ... 요법 ????
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

실제로 Michael Waterfall의 훌륭한 MWFeedParser 프레임 워크 (그의 답변 참조)는 ARC 지원으로 업데이트 한 rmchaara에 의해 포크되었습니다!

여기 Github 에서 찾을 수 있습니다.

정말 훌륭하게 작동하고 stringByDecodingHTMLEntities 메서드를 사용했으며 완벽하게 작동합니다.


그러면 ARC 문제가 해결되지만 몇 가지 경고가 표시됩니다. 무시해도 안전한가요?
Robert J. Clegg 2013 년

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"로있는 경우 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.