Swift에서 HTML 엔티티를 어떻게 디코딩합니까?


121

사이트에서 JSON 파일을 가져오고 수신 된 문자열 중 하나는 다음과 같습니다.

The Weeknd ‘King Of The Fall’ [Video Premiere] | @TheWeeknd | #SoPhi

같은 &#8216것을 올바른 문자 로 어떻게 변환 할 수 있습니까?

이를 시연하기 위해 Xcode Playground를 만들었습니다.

import UIKit

var error: NSError?
let blogUrl: NSURL = NSURL.URLWithString("http://sophisticatedignorance.net/api/get_recent_summary/")
let jsonData = NSData(contentsOfURL: blogUrl)

let dataDictionary = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &error) as NSDictionary

var a = dataDictionary["posts"] as NSArray

println(a[0]["title"])

답변:


157

이 답변은 Swift 5.2 및 iOS 13.4 SDK에 대해 마지막으로 수정되었습니다.


이를 수행하는 간단한 방법은 없지만 사용할 수 있습니다. NSAttributedString 마법을 사용하여이 프로세스를 가능한 한 쉽게 만들 수 있습니다 (이 방법은 모든 HTML 태그도 제거 할 것입니다).

주 스레드에서만 초기화NSAttributedString 해야합니다 . WebKit을 사용하여 HTML을 구문 분석하므로 요구 사항이 있습니다.

// This is a[0]["title"] in your case
let encodedString = "The Weeknd <em>&#8216;King Of The Fall&#8217;</em>"

guard let data = htmlEncodedString.data(using: .utf8) else {
    return
}

let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
    .documentType: NSAttributedString.DocumentType.html,
    .characterEncoding: String.Encoding.utf8.rawValue
]

guard let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) else {
    return
}

// The Weeknd ‘King Of The Fall’
let decodedString = attributedString.string
extension String {

    init?(htmlEncodedString: String) {

        guard let data = htmlEncodedString.data(using: .utf8) else {
            return nil
        }

        let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ]

        guard let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) else {
            return nil
        }

        self.init(attributedString.string)

    }

}

let encodedString = "The Weeknd <em>&#8216;King Of The Fall&#8217;</em>"
let decodedString = String(htmlEncodedString: encodedString)

54
뭐? 확장이되는 의미 새로운 기능을 제공하기 위해 기존의 유형을 확장 할 수 있습니다.
akashivskyy

4
나는 당신이 말하려는 것을 이해하지만 확장을 부정하는 것은 갈 길이 아닙니다.
akashivskyy

1
@akashivskyy : 비 ASCII 문자로 올바르게 작동하려면 NSCharacterEncodingDocumentAttribute를 추가해야합니다 . stackoverflow.com/a/27898167/1187415를 비교하십시오 .
Martin R

13
이 방법은 매우 무거운이며, tableviews 권장되지 않는다 또는있는 GridViews
귀도 Lodetti

1
이것은 대단합니다! 메인 스레드를 차단하지만 백그라운드 스레드에서 실행할 수있는 방법이 있습니까?
MMV

78

@akashivskyy의 대답은 훌륭하며 NSAttributedStringHTML 엔티티를 디코딩 하는 데 활용하는 방법을 보여줍니다 . 한 가지 가능한 단점은 (그가 말했듯이) 모든 HTML 마크 업도 제거 된다는 것입니다.

<strong> 4 &lt; 5 &amp; 3 &gt; 2</strong>

된다

4 < 5 & 3 > 2

OS X에는 CFXMLCreateStringByUnescapingEntities()다음과 같은 작업이 있습니다.

let encoded = "<strong> 4 &lt; 5 &amp; 3 &gt; 2 .</strong> Price: 12 &#x20ac;.  &#64; "
let decoded = CFXMLCreateStringByUnescapingEntities(nil, encoded, nil) as String
println(decoded)
// <strong> 4 < 5 & 3 > 2 .</strong> Price: 12.  @ 

그러나 이것은 iOS에서 사용할 수 없습니다.

다음은 순수한 Swift 구현입니다. 이 같은 문자 엔터티 참조를 디코딩 &lt;사전을 사용하여, 모든 숫자 문자 엔터티 좋아 &#64하거나 &#x20ac. (모든 252 개의 HTML 엔티티를 명시 적으로 나열하지는 않았습니다.)

스위프트 4 :

// Mapping from XML/HTML character entity reference to character
// From http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
private let characterEntities : [ Substring : Character ] = [
    // XML predefined entities:
    "&quot;"    : "\"",
    "&amp;"     : "&",
    "&apos;"    : "'",
    "&lt;"      : "<",
    "&gt;"      : ">",

    // HTML character entity references:
    "&nbsp;"    : "\u{00a0}",
    // ...
    "&diams;"   : "♦",
]

extension String {

    /// Returns a new string made by replacing in the `String`
    /// all HTML character entity references with the corresponding
    /// character.
    var stringByDecodingHTMLEntities : String {

        // ===== Utility functions =====

        // Convert the number in the string to the corresponding
        // Unicode character, e.g.
        //    decodeNumeric("64", 10)   --> "@"
        //    decodeNumeric("20ac", 16) --> "€"
        func decodeNumeric(_ string : Substring, base : Int) -> Character? {
            guard let code = UInt32(string, radix: base),
                let uniScalar = UnicodeScalar(code) else { return nil }
            return Character(uniScalar)
        }

        // Decode the HTML character entity to the corresponding
        // Unicode character, return `nil` for invalid input.
        //     decode("&#64;")    --> "@"
        //     decode("&#x20ac;") --> "€"
        //     decode("&lt;")     --> "<"
        //     decode("&foo;")    --> nil
        func decode(_ entity : Substring) -> Character? {

            if entity.hasPrefix("&#x") || entity.hasPrefix("&#X") {
                return decodeNumeric(entity.dropFirst(3).dropLast(), base: 16)
            } else if entity.hasPrefix("&#") {
                return decodeNumeric(entity.dropFirst(2).dropLast(), base: 10)
            } else {
                return characterEntities[entity]
            }
        }

        // ===== Method starts here =====

        var result = ""
        var position = startIndex

        // Find the next '&' and copy the characters preceding it to `result`:
        while let ampRange = self[position...].range(of: "&") {
            result.append(contentsOf: self[position ..< ampRange.lowerBound])
            position = ampRange.lowerBound

            // Find the next ';' and copy everything from '&' to ';' into `entity`
            guard let semiRange = self[position...].range(of: ";") else {
                // No matching ';'.
                break
            }
            let entity = self[position ..< semiRange.upperBound]
            position = semiRange.upperBound

            if let decoded = decode(entity) {
                // Replace by decoded character:
                result.append(decoded)
            } else {
                // Invalid entity, copy verbatim:
                result.append(contentsOf: entity)
            }
        }
        // Copy remaining characters to `result`:
        result.append(contentsOf: self[position...])
        return result
    }
}

예:

let encoded = "<strong> 4 &lt; 5 &amp; 3 &gt; 2 .</strong> Price: 12 &#x20ac;.  &#64; "
let decoded = encoded.stringByDecodingHTMLEntities
print(decoded)
// <strong> 4 < 5 & 3 > 2 .</strong> Price: 12.  @

스위프트 3 :

// Mapping from XML/HTML character entity reference to character
// From http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
private let characterEntities : [ String : Character ] = [
    // XML predefined entities:
    "&quot;"    : "\"",
    "&amp;"     : "&",
    "&apos;"    : "'",
    "&lt;"      : "<",
    "&gt;"      : ">",

    // HTML character entity references:
    "&nbsp;"    : "\u{00a0}",
    // ...
    "&diams;"   : "♦",
]

extension String {

    /// Returns a new string made by replacing in the `String`
    /// all HTML character entity references with the corresponding
    /// character.
    var stringByDecodingHTMLEntities : String {

        // ===== Utility functions =====

        // Convert the number in the string to the corresponding
        // Unicode character, e.g.
        //    decodeNumeric("64", 10)   --> "@"
        //    decodeNumeric("20ac", 16) --> "€"
        func decodeNumeric(_ string : String, base : Int) -> Character? {
            guard let code = UInt32(string, radix: base),
                let uniScalar = UnicodeScalar(code) else { return nil }
            return Character(uniScalar)
        }

        // Decode the HTML character entity to the corresponding
        // Unicode character, return `nil` for invalid input.
        //     decode("&#64;")    --> "@"
        //     decode("&#x20ac;") --> "€"
        //     decode("&lt;")     --> "<"
        //     decode("&foo;")    --> nil
        func decode(_ entity : String) -> Character? {

            if entity.hasPrefix("&#x") || entity.hasPrefix("&#X"){
                return decodeNumeric(entity.substring(with: entity.index(entity.startIndex, offsetBy: 3) ..< entity.index(entity.endIndex, offsetBy: -1)), base: 16)
            } else if entity.hasPrefix("&#") {
                return decodeNumeric(entity.substring(with: entity.index(entity.startIndex, offsetBy: 2) ..< entity.index(entity.endIndex, offsetBy: -1)), base: 10)
            } else {
                return characterEntities[entity]
            }
        }

        // ===== Method starts here =====

        var result = ""
        var position = startIndex

        // Find the next '&' and copy the characters preceding it to `result`:
        while let ampRange = self.range(of: "&", range: position ..< endIndex) {
            result.append(self[position ..< ampRange.lowerBound])
            position = ampRange.lowerBound

            // Find the next ';' and copy everything from '&' to ';' into `entity`
            if let semiRange = self.range(of: ";", range: position ..< endIndex) {
                let entity = self[position ..< semiRange.upperBound]
                position = semiRange.upperBound

                if let decoded = decode(entity) {
                    // Replace by decoded character:
                    result.append(decoded)
                } else {
                    // Invalid entity, copy verbatim:
                    result.append(entity)
                }
            } else {
                // No matching ';'.
                break
            }
        }
        // Copy remaining characters to `result`:
        result.append(self[position ..< endIndex])
        return result
    }
}

스위프트 2 :

// Mapping from XML/HTML character entity reference to character
// From http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
private let characterEntities : [ String : Character ] = [
    // XML predefined entities:
    "&quot;"    : "\"",
    "&amp;"     : "&",
    "&apos;"    : "'",
    "&lt;"      : "<",
    "&gt;"      : ">",

    // HTML character entity references:
    "&nbsp;"    : "\u{00a0}",
    // ...
    "&diams;"   : "♦",
]

extension String {

    /// Returns a new string made by replacing in the `String`
    /// all HTML character entity references with the corresponding
    /// character.
    var stringByDecodingHTMLEntities : String {

        // ===== Utility functions =====

        // Convert the number in the string to the corresponding
        // Unicode character, e.g.
        //    decodeNumeric("64", 10)   --> "@"
        //    decodeNumeric("20ac", 16) --> "€"
        func decodeNumeric(string : String, base : Int32) -> Character? {
            let code = UInt32(strtoul(string, nil, base))
            return Character(UnicodeScalar(code))
        }

        // Decode the HTML character entity to the corresponding
        // Unicode character, return `nil` for invalid input.
        //     decode("&#64;")    --> "@"
        //     decode("&#x20ac;") --> "€"
        //     decode("&lt;")     --> "<"
        //     decode("&foo;")    --> nil
        func decode(entity : String) -> Character? {

            if entity.hasPrefix("&#x") || entity.hasPrefix("&#X"){
                return decodeNumeric(entity.substringFromIndex(entity.startIndex.advancedBy(3)), base: 16)
            } else if entity.hasPrefix("&#") {
                return decodeNumeric(entity.substringFromIndex(entity.startIndex.advancedBy(2)), base: 10)
            } else {
                return characterEntities[entity]
            }
        }

        // ===== Method starts here =====

        var result = ""
        var position = startIndex

        // Find the next '&' and copy the characters preceding it to `result`:
        while let ampRange = self.rangeOfString("&", range: position ..< endIndex) {
            result.appendContentsOf(self[position ..< ampRange.startIndex])
            position = ampRange.startIndex

            // Find the next ';' and copy everything from '&' to ';' into `entity`
            if let semiRange = self.rangeOfString(";", range: position ..< endIndex) {
                let entity = self[position ..< semiRange.endIndex]
                position = semiRange.endIndex

                if let decoded = decode(entity) {
                    // Replace by decoded character:
                    result.append(decoded)
                } else {
                    // Invalid entity, copy verbatim:
                    result.appendContentsOf(entity)
                }
            } else {
                // No matching ';'.
                break
            }
        }
        // Copy remaining characters to `result`:
        result.appendContentsOf(self[position ..< endIndex])
        return result
    }
}

10
훌륭합니다, Martin 감사합니다! 다음은 HTML 엔티티의 전체 목록이 포함 된 확장입니다. gist.github.com/mwaterfall/25b4a6a06dc3309d9555 또한 대체로 인한 거리 오프셋을 제공하기 위해 약간 수정했습니다. 이를 통해 이러한 대체의 영향을받을 수있는 모든 문자열 속성 또는 엔티티 (예 : Twitter 엔티티 인덱스)를 올바르게 조정할 수 있습니다.
Michael Waterfall

3
@MichaelWaterfall과 Martin은 훌륭합니다! 매력처럼 작동합니다! Swift 2의 확장 기능을 업데이트합니다 . pastebin.com/juHRJ6au 감사합니다!
Santiago

1
이 답변을 Swift 2와 호환되도록 변환하고 사용하기 쉽도록 StringExtensionHTML 이라는 CocoaPod에 덤프했습니다 . Santiago의 Swift 2 버전은 컴파일 시간 오류를 수정하지만 strtooul(string, nil, base)전체를 제거하면 코드가 숫자 문자 엔티티와 함께 ​​작동하지 않고 인식되지 않는 엔티티에 대해 오류가 발생합니다 (정상적으로 실패하는 대신).
Adela Chang

1
@AdelaChang : 사실 저는 이미 2015 년 9 월에 Swift 2로 제 답변을 변환했습니다. 여전히 Swift 2.2 / Xcode 7.3으로 경고없이 컴파일됩니다. 아니면 Michael의 버전을 언급하고 있습니까?
Martin R

1
감사합니다.이 답변으로 내 문제를 해결했습니다. NSAttributedString을 사용하여 심각한 성능 문제가 발생했습니다.
Andrea Mugnaini

27

@akashivskyy 확장 프로그램Swift 3 버전 ,

extension String {
    init(htmlEncodedString: String) {
        self.init()
        guard let encodedData = htmlEncodedString.data(using: .utf8) else {
            self = htmlEncodedString
            return
        }

        let attributedOptions: [String : Any] = [
            NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
            NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue
        ]

        do {
            let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
            self = attributedString.string
        } catch {
            print("Error: \(error)")
            self = htmlEncodedString
        }
    }
}

잘 작동합니다. 원래 대답은 이상한 충돌을 일으켰습니다. 업데이트 해주셔서 감사합니다!
Geoherna 2016

프랑스어 문자의 경우 I는 사용 UTF16에이
세바스티앙 레미

23

스위프트 4


  • 문자열 확장 계산 변수
  • 여분의 가드없이, do, catch 등 ...
  • 디코딩이 실패하면 원래 문자열을 반환합니다.

extension String {
    var htmlDecoded: String {
        let decoded = try? NSAttributedString(data: Data(utf8), options: [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ], documentAttributes: nil).string

        return decoded ?? self
    }
}

1
와 ! Swift 4에서 바로 사용할 수 있습니다!. 사용법 // let encode = "The Weeknd & # 8216; King Of The Fall & # 8217;" let finalString = encode.htmlDecoded
Naishta

2
이 답변의 단순함이 마음에 듭니다. 그러나 메인 스레드에서 실행을 시도하기 때문에 백그라운드에서 실행하면 충돌이 발생합니다.
Jeremy Hicks

14

@akashivskyy 확장Swift 2 버전 ,

 extension String {
     init(htmlEncodedString: String) {
         if let encodedData = htmlEncodedString.dataUsingEncoding(NSUTF8StringEncoding){
             let attributedOptions : [String: AnyObject] = [
            NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
            NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
        ]

             do{
                 if let attributedString:NSAttributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil){
                     self.init(attributedString.string)
                 }else{
                     print("error")
                     self.init(htmlEncodedString)     //Returning actual string if there is an error
                 }
             }catch{
                 print("error: \(error)")
                 self.init(htmlEncodedString)     //Returning actual string if there is an error
             }

         }else{
             self.init(htmlEncodedString)     //Returning actual string if there is an error
         }
     }
 }

이 코드는 불완전하며 피해야합니다. 오류가 제대로 처리되지 않습니다. 실제로 오류 코드가 있으면 충돌합니다. 오류가있을 때 최소한 nil을 반환하도록 코드를 업데이트해야합니다. 또는 원래 문자열로 초기화 할 수 있습니다. 결국 오류를 처리해야합니다. 그렇지 않습니다. 와!
oyalhi

9

Swift 4 버전

extension String {

    init(htmlEncodedString: String) {
        self.init()
        guard let encodedData = htmlEncodedString.data(using: .utf8) else {
            self = htmlEncodedString
            return
        }

        let attributedOptions: [NSAttributedString.DocumentReadingOptionKey : Any] = [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ]

        do {
            let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
            self = attributedString.string
        } 
        catch {
            print("Error: \(error)")
            self = htmlEncodedString
        }
    }
}

이것을 사용하려고하면 "Error Domain = NSCocoaErrorDomain Code = 259"파일이 올바른 형식이 아니기 때문에 열 수 없습니다. ""라는 메시지가 표시됩니다. 메인 스레드에서 전체 do catch를 실행하면 사라집니다. NSAttributedString 문서를 확인한 결과 "HTML 임포터가 백그라운드 스레드에서 호출되지 않아야합니다 (즉, 옵션 사전에 html 값이있는 documentType이 포함되어 있음). 주 스레드와 동기화를 시도하고 실패하고 시간 초과. "
MickeDG

8
의하시기 바랍니다 rawValue구문 NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.documentType.rawValue)과는 NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.characterEncoding.rawValue)끔찍하다. 로 교체 .documentType하고.characterEncoding
vadian

@MickeDG-이 오류를 해결하기 위해 정확히 무엇을했는지 설명해 주시겠습니까? 나는 산발적으로 그것을 얻고있다.
Ross Barbish

@RossBarbish-죄송합니다. 로스는 너무 오래 전 일이어서 세부 사항을 기억할 수 없습니다. 위의 주석에서 내가 제안한 것을 시도해 보았습니까? 즉, 메인 스레드에서 전체 do catch를 실행 했습니까?
MickeDG

7
extension String{
    func decodeEnt() -> String{
        let encodedData = self.dataUsingEncoding(NSUTF8StringEncoding)!
        let attributedOptions : [String: AnyObject] = [
            NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
            NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
        ]
        let attributedString = NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil, error: nil)!

        return attributedString.string
    }
}

let encodedString = "The Weeknd &#8216;King Of The Fall&#8217;"

let foo = encodedString.decodeEnt() /* The Weeknd ‘King Of The Fall’ */

Re "The Weeknd" : "The Weekend"가 아닙니까?
Peter Mortensen

구문 강조 표시가 이상해 보입니다. 특히 마지막 줄의 주석 부분이 이상합니다. 고칠 수 있습니까?
Peter Mortensen

"The Weeknd"는 가수이고, 그렇습니다. 그의 이름의 철자입니다.
wLc

5

HTML 문자 참조 (즉, macOS와 Linux의 서버 측 Swift 앱)에서 이스케이프 또는 이스케이프 해제 할 순수한 Swift 3.0 유틸리티를 찾고 있었지만 포괄적 인 솔루션을 찾지 못했기 때문에 자체 구현을 작성했습니다. https : //github.com/IBM-Swift/swift-html-entities

패키지 HTMLEntities는 HTML4 명명 된 문자 참조 및 16 진수 / 12 진수 문자 참조와 함께 작동하며 W3 HTML5 사양에 따라 특수 숫자 참조를 인식합니다 (즉 , 유니 코드가 아닌 &#x80;유로 기호 (유니 코드 U+20AC) 로 이스케이프 처리되지 않아야 함). 에 대한 문자 U+0080및 특정 범위의 숫자 문자 참조는 U+FFFD이스케이프 해제시 대체 문자로 대체되어야합니다 ).

사용 예 :

import HTMLEntities

// encode example
let html = "<script>alert(\"abc\")</script>"

print(html.htmlEscape())
// Prints ”&lt;script&gt;alert(&quot;abc&quot;)&lt;/script&gt;"

// decode example
let htmlencoded = "&lt;script&gt;alert(&quot;abc&quot;)&lt;/script&gt;"

print(htmlencoded.htmlUnescape())
// Prints<script>alert(\"abc\")</script>"

그리고 OP의 예 :

print("The Weeknd &#8216;King Of The Fall&#8217; [Video Premiere] | @TheWeeknd | #SoPhi ".htmlUnescape())
// prints "The Weeknd ‘King Of The Fall’ [Video Premiere] | @TheWeeknd | #SoPhi "

편집 : HTMLEntities이제 버전 2.0.0부터 HTML5 명명 된 문자 참조를 지원합니다. 사양을 준수하는 구문 분석도 구현됩니다.


1
이것은 항상 작동하는 가장 일반적인 대답이며 주 스레드에서 실행할 필요가 없습니다. 이것은 가장 복잡한 HTML 이스케이프 유니 코드 문자열 (예 :)에서도 작동 (&nbsp;͡&deg;&nbsp;͜ʖ&nbsp;͡&deg;&nbsp;)하지만 다른 답변은 그것을 관리하지 않습니다.
Stéphane Copin 17.11.

5

스위프트 4 :

HTML 코드와 줄 바꿈 문자 및 작은 따옴표로 마침내 나를 위해 일한 토탈 솔루션

extension String {
    var htmlDecoded: String {
        let decoded = try? NSAttributedString(data: Data(utf8), options: [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
            ], documentAttributes: nil).string

        return decoded ?? self
    }
}

용법:

let yourStringEncoded = yourStringWithHtmlcode.htmlDecoded

그런 다음 작은 따옴표 (예 : do n't , has n't , It 's 등)와 \n다음 과 같은 새 줄 문자를 제거하기 위해 몇 가지 필터를 더 적용해야했습니다 .

var yourNewString = String(yourStringEncoded.filter { !"\n\t\r".contains($0) })
yourNewString = yourNewString.replacingOccurrences(of: "\'", with: "", options: NSString.CompareOptions.literal, range: nil)

이것은 본질적으로 다른 답변 의 사본입니다 . 당신이 한 것은 충분히 명백한 사용법을 추가하는 것뿐입니다.
rmaddy

어떤 사람이이 답변을 찬성하여 정말 유용하다고 생각했습니다.
Naishta

@Naishta 그것은 모두가 다른 의견을 가지고 있고 괜찮다는 것을 알려줍니다
Josh Wolff

3

이것이 내 접근 방식입니다. https://gist.github.com/mwaterfall/25b4a6a06dc3309d9555 Michael Waterfall에서 언급 한 엔티티 사전을 추가 할 수 있습니다 .

extension String {
    func htmlDecoded()->String {

        guard (self != "") else { return self }

        var newStr = self

        let entities = [
            "&quot;"    : "\"",
            "&amp;"     : "&",
            "&apos;"    : "'",
            "&lt;"      : "<",
            "&gt;"      : ">",
        ]

        for (name,value) in entities {
            newStr = newStr.stringByReplacingOccurrencesOfString(name, withString: value)
        }
        return newStr
    }
}

사용 된 예 :

let encoded = "this is so &quot;good&quot;"
let decoded = encoded.htmlDecoded() // "this is so "good""

또는

let encoded = "this is so &quot;good&quot;".htmlDecoded() // "this is so "good""

1
나는 이것이별로 마음에 들지 않지만 아직 더 나은 것을 찾지 못했기 때문에 이것은 Swift 2.0 gist.github.com/jrmgx/3f9f1d330b295cf6b1c6
jrmgx

3

우아한 Swift 4 솔루션

문자열을 원하면

myString = String(htmlString: encodedString)

이 확장을 프로젝트에 추가하십시오.

extension String {

    init(htmlString: String) {
        self.init()
        guard let encodedData = htmlString.data(using: .utf8) else {
            self = htmlString
            return
        }

        let attributedOptions: [NSAttributedString.DocumentReadingOptionKey : Any] = [
           .documentType: NSAttributedString.DocumentType.html,
           .characterEncoding: String.Encoding.utf8.rawValue
        ]

        do {
            let attributedString = try NSAttributedString(data: encodedData,
                                                          options: attributedOptions,
                                                          documentAttributes: nil)
            self = attributedString.string
        } catch {
            print("Error: \(error.localizedDescription)")
            self = htmlString
        }
    }
}

굵은 체, 이탤릭체, 링크 등의 NSAttributedString을 원한다면,

textField.attributedText = try? NSAttributedString(htmlString: encodedString)

이 확장을 프로젝트에 추가하십시오.

extension NSAttributedString {

    convenience init(htmlString html: String) throws {
        try self.init(data: Data(html.utf8), options: [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
            ], documentAttributes: nil)
    }

}

2

@yishus '대답 의 계산 된 var 버전

public extension String {
    /// Decodes string with HTML encoding.
    var htmlDecoded: String {
        guard let encodedData = self.data(using: .utf8) else { return self }

        let attributedOptions: [String : Any] = [
            NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
            NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue]

        do {
            let attributedString = try NSAttributedString(data: encodedData,
                                                          options: attributedOptions,
                                                          documentAttributes: nil)
            return attributedString.string
        } catch {
            print("Error: \(error)")
            return self
        }
    }
}

1

스위프트 4

func decodeHTML(string: String) -> String? {

    var decodedString: String?

    if let encodedData = string.data(using: .utf8) {
        let attributedOptions: [NSAttributedString.DocumentReadingOptionKey : Any] = [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ]

        do {
            decodedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil).string
        } catch {
            print("\(error.localizedDescription)")
        }
    }

    return decodedString
}

설명이 필요합니다. 예를 들어 이전 Swift 4 답변과 어떻게 다른가요?
Peter Mortensen

1

Swift 4.1 이상

var htmlDecoded: String {


    let attributedOptions: [NSAttributedString.DocumentReadingOptionKey : Any] = [

        NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html,
        NSAttributedString.DocumentReadingOptionKey.characterEncoding : String.Encoding.utf8.rawValue
    ]


    let decoded = try? NSAttributedString(data: Data(utf8), options: attributedOptions
        , documentAttributes: nil).string

    return decoded ?? self
} 

설명이 필요합니다. 예를 들어, 이전 답변과 어떻게 다릅니 까? 어떤 Swift 4.1 기능이 사용됩니까? Swift 4.1에서만 작동하고 이전 버전에서는 작동하지 않습니까? 아니면 Swift 4.1 이전에 작동할까요?
Peter Mortensen

1

스위프트 4

extension String {
    var replacingHTMLEntities: String? {
        do {
            return try NSAttributedString(data: Data(utf8), options: [
                .documentType: NSAttributedString.DocumentType.html,
                .characterEncoding: String.Encoding.utf8.rawValue
            ], documentAttributes: nil).string
        } catch {
            return nil
        }
    }
}

간단한 사용법

let clean = "Weeknd &#8216;King Of The Fall&#8217".replacingHTMLEntities ?? "default value"

나는 이미 내 힘이 옵션으로 풀렸다 고 불평하는 사람들을들을 수있다. HTML 문자열 인코딩을 연구하고 있고 Swift 선택 사항을 처리하는 방법을 모르는 경우 너무 앞서있는 것입니다.
quemeful

네, 있습니다 ( 11 월 1 일 22:37에 편집 되었고 "간단한 사용법"을 이해하기 훨씬 더 어렵게 만들었습니다)
quemeful

1

스위프트 4

documentAttributes를 사용하는 솔루션이 정말 마음에 듭니다. 그러나 파일을 구문 분석하거나 테이블보기 셀에서 사용하기에는 너무 느릴 수 있습니다. 나는 애플이 이에 대한 적절한 해결책을 제공하지 않는다는 것을 믿을 수 없다.

해결 방법으로 완벽하게 작동하고 디코딩 속도가 빠른 GitHub에서이 문자열 확장을 찾았습니다.

따라서 주어진 대답이 느린 상황에 대해서는이 링크에서 제안하는 솔루션을 참조하십시오. https://gist.github.com/mwaterfall/25b4a6a06dc3309d9555

참고 : HTML 태그를 구문 분석하지 않습니다.


1

Swift 3에서 작동하는 업데이트 된 답변

extension String {
    init?(htmlEncodedString: String) {
        let encodedData = htmlEncodedString.data(using: String.Encoding.utf8)!
        let attributedOptions = [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]

        guard let attributedString = try? NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil) else {
            return nil
        }
        self.init(attributedString.string)
   }

0

목표 -C

+(NSString *) decodeHTMLEnocdedString:(NSString *)htmlEncodedString {
    if (!htmlEncodedString) {
        return nil;
    }

    NSData *data = [htmlEncodedString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *attributes = @{NSDocumentTypeDocumentAttribute:     NSHTMLTextDocumentType,
                             NSCharacterEncodingDocumentAttribute:     @(NSUTF8StringEncoding)};
    NSAttributedString *attributedString = [[NSAttributedString alloc]     initWithData:data options:attributes documentAttributes:nil error:nil];
    return [attributedString string];
}

0

실제 글꼴 크기 변환이 가능한 Swift 3.0 버전

일반적으로 HTML 콘텐츠를 속성이있는 문자열로 직접 변환하면 글꼴 크기가 늘어납니다. HTML 문자열을 속성이있는 문자열로 변환 한 후 다시 돌아가서 차이점을 확인할 수 있습니다.

대신 모든 글꼴에 0.75 비율을 적용하여 글꼴 크기가 변경되지 않도록 하는 실제 크기 변환 은 다음과 같습니다.

extension String {
    func htmlAttributedString() -> NSAttributedString? {
        guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
        guard let attriStr = try? NSMutableAttributedString(
            data: data,
            options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
            documentAttributes: nil) else { return nil }
        attriStr.beginEditing()
        attriStr.enumerateAttribute(NSFontAttributeName, in: NSMakeRange(0, attriStr.length), options: .init(rawValue: 0)) {
            (value, range, stop) in
            if let font = value as? UIFont {
                let resizedFont = font.withSize(font.pointSize * 0.75)
                attriStr.addAttribute(NSFontAttributeName,
                                         value: resizedFont,
                                         range: range)
            }
        }
        attriStr.endEditing()
        return attriStr
    }
}

0

스위프트 4

extension String {

    mutating func toHtmlEncodedString() {
        guard let encodedData = self.data(using: .utf8) else {
            return
        }

        let attributedOptions: [NSAttributedString.DocumentReadingOptionKey : Any] = [
            NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.documentType.rawValue): NSAttributedString.DocumentType.html,
            NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.characterEncoding.rawValue): String.Encoding.utf8.rawValue
        ]

        do {
            let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
            self = attributedString.string
        }
        catch {
            print("Error: \(error)")
        }
    }

의하시기 바랍니다 rawValue구문 NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.documentType.rawValue)과는 NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.characterEncoding.rawValue)끔찍하다. 로 교체 .documentType하고.characterEncoding
vadian

이 솔루션의 성능은 끔찍합니다. 별도의 CA에 대해 괜찮을 수 있으며 파일 구문 분석은 권장되지 않습니다.
Vincent

0

프로그램이 Strings에서 HTML 엔티티를 추가 및 제거 할 수 있도록하는 Swift로 작성된 라이브러리 인 HTMLString을 살펴보십시오.

완전성을 위해 사이트의 주요 기능을 복사했습니다.

  • ASCII 및 UTF-8 / UTF-16 인코딩을위한 엔티티 추가
  • 2100 개 이상의 명명 된 엔티티 (예 : &) 제거
  • 10 진수 및 16 진수 항목 제거 지원
  • Swift Extended Grapheme Clusters를 지원하도록 설계됨 (→ 100 % 이모 지 방지)
  • 완전 단위 테스트
  • 빠른
  • 문서화
  • Objective-C와 호환

0

Swift 5.1 버전

import UIKit

extension String {

    init(htmlEncodedString: String) {
        self.init()
        guard let encodedData = htmlEncodedString.data(using: .utf8) else {
            self = htmlEncodedString
            return
        }

        let attributedOptions: [NSAttributedString.DocumentReadingOptionKey : Any] = [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ]

        do {
            let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
            self = attributedString.string
        } 
        catch {
            print("Error: \(error)")
            self = htmlEncodedString
        }
    }
}

또한 날짜, 이미지, 메타 데이터, 제목 및 설명을 추출하려면 다음 이름의 내 포드를 사용할 수 있습니다.

][1].

가독성 키트


이전 버전 인 Swift 5.0, Swift 4.1, Swift 4.0 등에서 작동하지 않는 것은 무엇입니까?
Peter Mortensen

collectionViews를 사용하여 문자열을 디코딩 할 때 오류를 발견했습니다
Tung Vu Duc

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.