utf8 para mostrar especiales cotejamiento caracteres acentos json swift html-entities

json - para - ¿Cómo decodifico entidades HTML en Swift?



utf8 php mysql (22)

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 }

Extraigo un archivo JSON de un sitio y una de las cadenas recibidas es:

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

¿Cómo puedo convertir cosas como &#8216 en los caracteres correctos?

He creado un Xcode Playground para demostrarlo:

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


Versión Swift 3.0 con conversión de tamaño de fuente real

Normalmente, si convierte directamente html a una cadena atribuida, el tamaño de la fuente aumenta. Puede intentar convertir una cadena html en una cadena atribuida y viceversa para ver la diferencia.

En cambio, aquí está la conversión del tamaño real que asegura que el tamaño de la fuente no cambie, aplicando la proporción de 0.75 en todas las fuentes

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


Versión 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 } } }


Eche un vistazo a HTMLString: una biblioteca escrita en Swift que permite que su programa agregue y elimine entidades HTML en cadenas

Para completar, copié las características principales del sitio:

  • Agrega entidades para codificaciones ASCII y UTF-8 / UTF-16
  • Elimina más de 2100 entidades con nombre (como &)
  • Admite eliminar entidades decimales y hexadecimales
  • Diseñado para admitir grupos de grafemas extendidos Swift (→ 100% a prueba de emoji)
  • Unidad completamente probada
  • Rápido
  • Documentado
  • Compatible con Objective-C

Estaba buscando una utilidad Swift 3.0 pura para escapar / no escapar de las referencias de caracteres HTML (es decir, para aplicaciones Swift del lado del servidor en macOS y Linux) pero no encontré ninguna solución integral, así que escribí mi propia implementación: https://github.com/IBM-Swift/swift-html-entities

El paquete, HTMLEntities , funciona con referencias de caracteres con nombre HTML4, así como referencias de caracteres numéricos hexadecimales / dec, y reconocerá referencias de caracteres numéricos especiales según la especificación W3 HTML5 (es decir, € debe estar sin escape como el signo Euro (unicode U+20AC ) y NO como el carácter unicode para U+0080 , y ciertos rangos de referencias de caracteres numéricos deben reemplazarse con el carácter de reemplazo U+FFFD al desempaquetar).

Ejemplo de uso:

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

Y para el ejemplo de 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 "

Editar: HTMLEntities ahora admite referencias de caracteres con nombre HTML5 a partir de la versión 2.0.0. También se implementa el análisis que cumple con las especificaciones.


Este sería mi enfoque. Puede agregar el diccionario de entidades de gist.github.com/mwaterfall/25b4a6a06dc3309d9555 menciona 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 } }

Ejemplos utilizados:

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

O

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


La respuesta de @ akashivskyy es excelente y demuestra cómo utilizar NSAttributedString para decodificar entidades HTML. Una posible desventaja (como él dijo) es que también se elimina todo el marcado HTML, por lo que

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

se convierte

4 < 5 & 3 > 2

En OS X hay CFXMLCreateStringByUnescapingEntities() que hace el trabajo:

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

pero esto no está disponible en iOS.

Aquí hay una implementación pura de Swift. Decodifica referencias de entidades de caracteres como &lt; usando un diccionario y todas las entidades de caracteres numéricos como &#64 o &#x20ac . (Tenga en cuenta que no enumeré todas las 252 entidades HTML explícitamente).

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

Ejemplo:

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


NSData dataRes = (valor nsdata)

var resString = NSString (datos: dataRes, codificación: NSUTF8StringEncoding)


No hay una forma directa de hacerlo, pero puede usar la magia NSAttributedString para hacer que este proceso sea lo más sencillo posible (tenga en cuenta que este método también eliminará todas las etiquetas HTML):

let encodedString = "The Weeknd <em>&#8216;King Of The Fall&#8217;</em>" // encodedString should = a[0]["title"] in your case 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) else { return nil } let decodedString = attributedString.string // The Weeknd ‘King Of The Fall’

Recuerde inicializar NSAttributedString solo desde el hilo principal . Utiliza algo de magia WebKit debajo, por lo tanto, el requisito.

Puede crear su propia extensión de String para aumentar la reutilización:

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) else { return nil } self.init(attributedString.string) } } let encodedString = "The Weeknd <em>&#8216;King Of The Fall&#8217;</em>" let decodedString = String(htmlEncodedString: encodedString)


Respuesta actualizada trabajando en 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) }


Swift4

Realmente me gusta la solución que usa documentAttributes, sin embargo, puede ser lenta para analizar archivos y / o uso en celdas de vista de tabla. No puedo creer que Apple no proporcione una solución decente para esto.

Como solución alternativa, encontré en GitHub esta extensión de cadena que funciona perfecta y rápidamente para la decodificación.

Entonces, para situaciones en las que la respuesta dada es lenta, vea la solución que sugiere en este enlace: gist.github.com/mwaterfall/25b4a6a06dc3309d9555 Nota: no analiza las etiquetas HTML.


Versión Swift 2 de la extensión de @ akashivskyy,

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


Versión Swift 3 de la extensión de @ akashivskyy ,

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


Versión var calculada de la respuesta de @yishus

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


C objetivo

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


Elegant Swift 4 Solution

Si quieres una cuerda

myString = String(htmlString: encodedString)

Agregue esta extensión a su proyecto

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

Si desea una cadena NSAttributedString con negrita, cursiva, enlaces, etc.

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

Agregue esta extensión a su proyecto

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


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


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

Uso simple

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


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 }


Swift 4

  • Extensión de cadena calculada var
  • Sin guardia extra / hacer / atrapar, etc.
  • Devuelve las cadenas originales si falla la decodificación

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


Swift 4:

La solución total que finalmente funcionó para mí con código html y caracteres de nueva línea y comillas simples

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

Uso:

let yourStringEncoded = yourStringWithHtmlcode.htmlDecoded

Luego tuve que aplicar algunos filtros más para deshacerme de las single quotes (por ejemplo: don''t, hasn''t, It''s etc.) y nuevos caracteres de línea como /n

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


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’