Swift'de HTML varlıklarının kodunu nasıl çözerim?


121

Bir siteden bir JSON dosyası çekiyorum ve alınan dizelerden biri:

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

Böyle şeyleri &#8216doğru karakterlere nasıl dönüştürebilirim ?

Göstermek için bir Xcode Playground yaptım:

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"])

Yanıtlar:


157

Bu cevap en son Swift 5.2 ve iOS 13.4 SDK için revize edildi.


Bunu yapmanın kolay bir yolu yoktur, ancak NSAttributedStringbu işlemi olabildiğince zahmetsiz hale getirmek için sihir kullanabilirsiniz (bu yöntemin tüm HTML etiketlerini de kaldıracağı konusunda uyarınız).

Yalnızca ana iş parçacığından başlatmayıNSAttributedString unutmayın . Altındaki HTML'yi ayrıştırmak için WebKit'i kullanır, bu nedenle gereklilik.

// 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
Ne? Uzantılar, yeni işlevsellik sağlamak için mevcut türleri genişletmek içindir .
akashivskyy

4
Ne demeye çalıştığını anlıyorum, ancak uzantıları reddetmek yapılacak yol değil.
akashivskyy

1
@akashivskyy: Bunun ASCII olmayan karakterlerle düzgün çalışmasını sağlamak için bir NSCharacterEncodingDocumentAttribute eklemeniz gerekir, stackoverflow.com/a/27898167/1187415 ile karşılaştırın .
Martin R.

13
Bu yöntem son derece ağırdır ve
tablo görünümlerinde

1
Bu harika! Ana iş parçacığını engellemesine rağmen, arka planda çalıştırmanın herhangi bir yolu var mı?
MMV

78

@ akashivskyy'nin cevabı harika ve NSAttributedStringHTML varlıklarının kodunu çözmek için nasıl kullanılacağını gösteriyor . Olası bir dezavantaj (belirttiği gibi), tüm HTML işaretlemelerinin de kaldırılmasıdır.

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

olur

4 < 5 & 3 > 2

OS X'te CFXMLCreateStringByUnescapingEntities()işi yapan vardır:

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.  @ 

ancak bu iOS'ta mevcut değildir.

İşte saf bir Swift uygulaması. &lt;Sözlük kullanmak gibi karakter varlıklarının ve &#64veya gibi tüm sayısal karakter varlıklarının kodunu çözer &#x20ac. (252 HTML varlığını açıkça listelemediğimi unutmayın.)

Swift 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
    }
}

Misal:

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.  @

Swift 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
    }
}

Swift 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
Bu harika, teşekkürler Martin! HTML varlıklarının tam listesini içeren uzantı: gist.github.com/mwaterfall/25b4a6a06dc3309d9555 Ayrıca, değiştirmeler tarafından yapılan mesafe uzaklıklarını sağlamak için biraz uyarladım. Bu, bu değiştirmelerden etkilenebilecek herhangi bir dize özniteliğinin veya varlığın doğru şekilde ayarlanmasına izin verir (örneğin, Twitter varlık indeksleri).
Michael Şelale

3
@MichaelWaterfall ve Martin bu muhteşem! tıkır tıkır çalışıyor! Swift 2 için uzantıyı güncelliyorum pastebin.com/juHRJ6au Teşekkürler!
Santiago

1
Bu yanıtı Swift 2 ile uyumlu hale getirdim ve kullanım kolaylığı için StringExtensionHTML adlı bir CocoaPod'a döktüm . Santiago'nun Swift 2 sürümünün derleme zamanı hatalarını düzelttiğini, ancak strtooul(string, nil, base)kodun tamamen çıkarılmasının kodun sayısal karakter varlıkları ile çalışmamasına ve tanımadığı bir varlık söz konusu olduğunda (zerafetle başarısız olmak yerine) çökmesine neden olacağını unutmayın.
Adela Chang

1
@AdelaChang: Aslında cevabımı Eylül 2015'te Swift 2'ye dönüştürmüştüm. Hala Swift 2.2 / Xcode 7.3 ile uyarılmadan derleniyor. Yoksa Michael'ın versiyonundan mı bahsediyorsunuz?
Martin R

1
Teşekkürler, bu cevapla sorunlarımı çözdüm: NSAttributedString kullanarak ciddi performans sorunları yaşadım.
Andrea Mugnaini

27

@ Akashivskyy'nin uzantısının Swift 3 sürümü ,

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

Harika çalışıyor. Orijinal cevap tuhaf kazalara neden oluyordu. Güncelleme için teşekkürler!
Geoherna

Fransız karakterler için utf16
Sébastien

23

Swift 4


  • Dize uzantısı hesaplanan değişken
  • Ekstra koruma olmadan, yap, yakala vb.
  • Kod çözme başarısız olursa orijinal dizeleri döndürür

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
Vaov ! Swift 4 için kutudan çıktığı gibi çalışır!. Kullanım // let encoded = "The Weeknd & # 8216; Sonbaharın Kralı & # 8217;" let finalString = encoded.htmlDecoded
Naishta

2
Bu cevabın basitliğini seviyorum. Ancak, arka planda çalıştırıldığında çökmelere neden olur çünkü ana iş parçacığı üzerinde çalışmaya çalışır.
Jeremy Hicks

14

@ Akashivskyy'nin uzantısının Swift 2 sürümü ,

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

Bu kod eksiktir ve kesinlikle kaçınılmalıdır. Hata düzgün bir şekilde ele alınmıyor. Gerçekte olduğu zaman bir hata kodu çöker. Bir hata olduğunda kodunuzu en azından sıfır olarak güncellemelisiniz. Ya da sadece orijinal dizeyle başlatabilirsiniz. Sonunda hatayı halletmelisiniz. Durum böyle değil. Vaov!
oyalhi

9

Swift 4 Sürümü

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

Bunu kullanmaya çalıştığımda "Error Domain = NSCocoaErrorDomain Code = 259" Dosya doğru biçimde olmadığı için açılamadı. "" Mesajı alıyorum. Ana iş parçacığının tamamını yakalarsam bu kaybolur. Bunu NSAttributedString belgelerini kontrol ederken buldum: "HTML içe aktarıcı bir arka plan iş parçacığından çağrılmamalıdır (yani, seçenekler sözlüğü html değerine sahip documentType içerir). Ana iş parçacığı ile senkronize etmeye çalışacak, başarısız olacak ve zaman aşımı."
MickeDG

8
Lütfen rawValuesözdizimi NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.documentType.rawValue)ve NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.characterEncoding.rawValue)korkunç. İle değiştirin .documentTypeve.characterEncoding
vadian

@MickeDG - Lütfen bu hatayı çözmek için tam olarak ne yaptığınızı açıklar mısınız? Düzensiz anlıyorum.
Ross Barbish

@RossBarbish - Üzgünüm Ross, bu çok uzun zaman önceydi, detayları hatırlayamıyorum. Yukarıdaki yorumda önerdiğimi denediniz mi, yani ana iş parçacığının tamamını yakalamak için mi?
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’ */

Yeniden "The Weeknd" : "The Weekend" değil mi?
Peter Mortensen

Sözdizimi vurgulaması, özellikle son satırın yorum kısmı tuhaf görünüyor. Tamir edebilir misin?
Peter Mortensen

"The Weeknd" bir şarkıcı ve evet, adı bu şekilde yazılıyor.
wLc

5

HTML karakter referanslarından (yani hem macOS hem de Linux'taki sunucu tarafı Swift uygulamaları için) kaçmak / geri çekmek için saf bir Swift 3.0 yardımcı programı arıyordum, ancak kapsamlı bir çözüm bulamadım, bu yüzden kendi uygulamamı yazdım: https: //github.com/IBM-Swift/swift-html-entities

Paket, HTMLEntitiesHTML4 adlandırılmış karakter referansları ve onaltılık / onluk sayısal karakter referansları ile çalışır ve W3 HTML5 spesifikasyonuna göre özel sayısal karakter referanslarını tanıyacaktır (yani &#x80;, Euro işareti (unicode U+20AC) ve unicode olarak DEĞİL karakter için U+0080ve belirli sayısal karakter başvurusu aralıkları, çıkış karakteri kaldırıldığında yeni karakterle değiştirilmelidir U+FFFD).

Kullanım örneği:

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>"

Ve OP'nin örneği için:

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 "

Düzenleme: HTMLEntitiesartık 2.0.0 sürümünden itibaren HTML5 adlı karakter referanslarını desteklemektedir. Spesifikasyon uyumlu ayrıştırma da uygulanır.


1
Bu, her zaman işe yarayan en genel yanıttır ve ana başlıkta çalıştırılmasını gerektirmez. Bu, en karmaşık HTML çıkışlı unicode dizeleriyle (örneğin (&nbsp;͡&deg;&nbsp;͜ʖ&nbsp;͡&deg;&nbsp;)) bile çalışacaktır , oysa diğer yanıtların hiçbiri bunu başaramaz.
Stéphane Copin

5

Swift 4:

Sonunda benim için HTML kodu, satırsonu karakterleri ve tek tırnak işaretleriyle çalışan toplam çözüm

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

Kullanımı:

let yourStringEncoded = yourStringWithHtmlcode.htmlDecoded

Sonra kurtulmak için biraz daha filtreler uygulamak zorunda tek tırnak (örneğin yapma , sahip değil , bu değil , vb) ve yeni satır karakterleri gibi \n:

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

Bu, aslında bu diğer cevabın bir kopyasıdır . Yaptığınız tek şey, yeterince açık olan bazı kullanımlar eklemek.
rmaddy

biri bu cevaba olumlu oy verdi ve gerçekten yararlı buldu, bu size ne anlatıyor?
Naishta

@Naishta Herkesin farklı fikirleri olduğunu ve sorun olmadığını söylüyor
Josh Wolff

3

Bu benim yaklaşımım olacaktır. Varlıklar sözlüğünü https://gist.github.com/mwaterfall/25b4a6a06dc3309d9555 Michael Waterfall bahsinden ekleyebilirsiniz .

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

Kullanılan örnekler:

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

VEYA

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

1
Bundan pek hoşlanmadım ama daha iyi bir şey bulamadım, bu yüzden bu, Swift 2.0 için Michael Waterfall çözümünün güncellenmiş bir sürümü. Github.com/jrmgx/3f9f1d330b295cf6b1c6
jrmgx

3

Elegant Swift 4 Çözümü

Bir ip istiyorsan,

myString = String(htmlString: encodedString)

bu uzantıyı projenize ekleyin:

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

Kalın, italik, bağlantılar vb. İçeren bir NSAttributedString istiyorsanız,

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

bu uzantıyı projenize ekleyin:

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'un cevabının hesaplanmış var versiyonu

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

Swift 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
}

Bir açıklama sırayla olacaktır. Örneğin, önceki Swift 4 cevaplarından farkı nedir?
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
} 

Bir açıklama sırayla olacaktır. Örneğin, önceki cevaplardan farkı nedir? Hangi Swift 4.1 özellikleri kullanılıyor? Sadece Swift 4.1'de mi çalışıyor, önceki sürümlerde değil mi? Yoksa Swift 4.1'den önce mi çalışır, örneğin Swift 4.0'da mı?
Peter Mortensen

1

Swift 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
        }
    }
}

Basit Kullanım

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

İnsanların isteğe bağlı olarak askere alınmamış gücümden şikayet ettiğini duyabiliyorum. HTML dize kodlamasını araştırıyorsanız ve Swift opsiyonları ile nasıl başa çıkacağınızı bilmiyorsanız, kendinizden çok ileride olursunuz.
2017

evet, vardı ( 1 Kasım 22:
37'de

1

Swift 4

DocumentAttributes kullanan çözümü gerçekten seviyorum. Ancak, dosyaları ayrıştırmak ve / veya tablo görünümü hücrelerinde kullanmak çok yavaş olabilir. Apple'ın bunun için iyi bir çözüm sağlamadığına inanamıyorum.

Bir çözüm olarak, mükemmel çalışan ve kod çözme için hızlı olan bu Dize Uzantısını GitHub'da buldum.

Dolayısıyla, verilen cevabın yavaşlayacağı durumlar için , bu bağlantıdaki çözüm önerisine bakın: https://gist.github.com/mwaterfall/25b4a6a06dc3309d9555

Not: HTML etiketlerini ayrıştırmaz.


1

Swift 3'te çalışan güncellenmiş cevap

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

Objective-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

Gerçek yazı tipi boyutu dönüştürme özelliğine sahip Swift 3.0 sürümü

Normalde, HTML içeriğini doğrudan ilişkilendirilmiş bir dizeye dönüştürürseniz, yazı tipi boyutu artar. Bir HTML dizesini ilişkilendirilmiş bir dizeye dönüştürmeyi deneyebilir ve farkı görmek için tekrar geri dönebilirsiniz.

Bunun yerine, tüm yazı tiplerine 0,75 oranını uygulayarak yazı tipi boyutunun değişmemesini sağlayan gerçek boyut dönüşümü burada verilmiştir :

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

Swift 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)")
        }
    }

Lütfen rawValuesözdizimi NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.documentType.rawValue)ve NSAttributedString.DocumentReadingOptionKey(rawValue: NSAttributedString.DocumentAttributeKey.characterEncoding.rawValue)korkunç. Bunu .documentTypeve ile değiştirin.characterEncoding
vadian

Bu çözümün performansı korkunç. Ayrı caes için sorun olmayabilir, dosyaların ayrıştırılması önerilmez.
Vincent

0

Programınızın Strings'e HTML öğeleri eklemesine ve kaldırmasına olanak tanıyan, Swift ile yazılmış bir kitaplık olan HTMLString'e bir göz atın.

Tamlık için, sitenin ana özelliklerini kopyaladım:

  • ASCII ve UTF-8 / UTF-16 kodlamaları için varlıklar ekler
  • 2100'den fazla adlandırılmış varlığı kaldırır (& gibi)
  • Ondalık ve onaltılık varlıkların kaldırılmasını destekler
  • Swift Extended Grapheme Kümelerini desteklemek için tasarlandı (→% 100 emojiye dayanıklı)
  • Tamamen ünite test edildi
  • Hızlı
  • belgeli
  • Objective-C ile uyumlu

0

Swift 5.1 Sürümü

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

Ayrıca, tarihi, görüntüleri, meta verileri, başlığı ve açıklamayı çıkarmak istiyorsanız, şu addaki bölmemi kullanabilirsiniz:

] [1].

Okunabilirlik kiti


Swift 5.0, Swift 4.1, Swift 4.0 vb. Gibi önceki sürümlerde çalışmasını sağlamayan nedir?
Peter Mortensen

CollectionViews kullanarak dizenin kodunu çözerken bir hata buldum
Tung Vu Duc

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.