Categoría para traducir entidades html en NSString

Recientemente desarrollando para el iOS, me encontré con la necesidad de eliminar las entidades html de una cadena de texto.

Para el que no lo sepa, las entidades dentro de HTML, o en cualquier lenguage de marcas, son lo mas parecido a una secuencia de escape, es decir, una cadena que representa a un caracter que originalmente no esta permitido. Por ejemplo, y como parece obvio, si queremos poner el caracter ‘<‘ en HTML, no podemos ponerlo directamente, pues forma parte del propio lenguage. Por ello, si queremos escribir dicho carácter tendremos que poner <.

La principal característica de las entidades de un lenguage de marcas, es que comienzan con ‘&’ y acaban con ‘;’.

Dentro de la API de manejo de cadenas de UIKit, existe un método para codificar URLs, que aunque pueda parecer similar, no lo es. Pues las URLs no tienen nada que ver con el lenguage HTML, solo nos indican la forma de acceder a los documentos que estan hechos con HTML😉

Dichos métodos son, stringByAddingPercentEscapesUsingEncoding: y stringByReplacingPercentEscapesUsingEncoding:, y sustituyen los caracteres especiales de las URLs por sus equivalentes “escapados” usando porcentajes, como por ejemplo el símbolo del $ la convertirá en %24.

Pero sin embargo no hay ninguna función para transformar las entidades en sus caracteres equivalentes.

La primera solución que se nos puede ocurrir es usar el mensaje stringByReplacingOccurrencesOfString:withString: por cada entidad. Y aunque funciona, es bastante penalizador desde el punto de vista del rendimiento, ya que si tenemos el siguiente código.

NSString* cad = [NSString stringWithString:@"&lt;&apos;&amp;&apos;&gt;"];
	NSLog(@"%@", [[[[cad stringByReplacingOccurrencesOfString:@"&lt;" withString:@"<"]
				  stringByReplacingOccurrencesOfString:@"&gt;" withString:@">"]
				  stringByReplacingOccurrencesOfString:@"&amp;" withString:@"&"]
				  stringByReplacingOccurrencesOfString:@"&apos;" withString:@"'"]);

Recorrerá la cadena entera tantas veces como entidades queramos sustituir, lo que para cadenas largas puede ser un gasto bastante grande e innecesario.

Después de buscar por internet, no encontré nada que me convenciera, asi que tome el camino del medio, y hice una categoria de NSString, que recorre la cadena y sustituye las ocurrencias, de las entidades por sus caracteres.

Al ser una categoria, no hace falta mas que incluir el fichero .h y llamarlo sobre cualquier objeto NSString.

@interface NSString (RemoveHtmlEntities)

- (NSString*) stringByRemovingHtmlEntities;

@end

@implementation NSString (RemoveHtmlEntities)

- (NSString*) stringByRemovingHtmlEntities {
	NSMutableString* newStr = [[NSMutableString alloc] init];
	NSUInteger i = 0;
	unichar c = 0;
	NSUInteger strLen = [self length];
	while (i < strLen) {
		c = [self characterAtIndex:i];
		if ( c == '&' ) {
			if ( [self rangeOfString:@"amp;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (5>(strLen-i))?strLen-i:5)].location != NSNotFound ) {
				[newStr appendFormat:@"&"];
				i += 5;
			} else if ([self rangeOfString:@"lt;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (4>(strLen-i))?strLen-i:4)].location != NSNotFound) {
				[newStr appendFormat:@"<"];
				i += 4;
			} else if ([self rangeOfString:@"gt;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (4>(strLen-i))?strLen-i:4)].location != NSNotFound) {
				[newStr appendFormat:@">"];
				i += 4;
			} else if ([self rangeOfString:@"quot;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (6>(strLen-i))?strLen-i:6)].location != NSNotFound) {
				[newStr appendFormat:@"\""];
				i += 6;
			} else if ([self rangeOfString:@"apos;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (6>(strLen-i))?strLen-i:6)].location != NSNotFound) {
				[newStr appendFormat:@"'"];
				i += 6;
			} else {
				[newStr appendFormat:@"&"];
				i++;
			}
		} else {
			[newStr appendFormat:@"%c", c];
			i++;
		}
	}
	return [newStr autorelease];
}

@end

Como veis, solo transforma las entidades que necesitaba en el momento de desarrollo, pero es trivial añadir mas clausulas “else if” al código😉

Un posible uso del código sería

	NSString* cad = [NSString stringWithString:@"&lt;&apos;&amp;&apos;&gt;"];
        NSLog(@"%@", [cad stringByRemovingHtmlEntities]); 
       // Imprime <'&'>

4 respuestas a Categoría para traducir entidades html en NSString

  1. […] Actualizados : Categoría para traducir entidades html en NSString Juego de la Vida en un canvas de […]

  2. Mariano dice:

    Ouch, suena pesado hacer un if anidado tan grande😦
    ¿No podrias hacerlo con expresiones regulares?(si estas trabajando en >3.2, obvio)

  3. robjperez dice:

    Hola,

    Lo primero, gracias por el comentario🙂

    Realmente no es un if anidado, lo que hace es mas parecido a switch, es decir, si entra por alguna de las alternativas, no va a evaluar las demás, y ademas solo se entra si se encuentra un & en el escaneo de la cadena.

    Sin duda, como propones, otra alternativa sería usar expresiones regulares, pero como dices, necesitas iOS 4.0, y supongo que por debajo debe estar hecho de una forma similar. En el fondo solo recorre la cadena una vez.

    Saludos.🙂

  4. Armando dice:

    Hola! Gracias por el comentario y el código.

    Me ha ayudado puesto que necesitaba exactamente esto.🙂 Pero le he detectado un bug que te digo como corregir.

    Esta linea:

    [newStr appendFormat:@”%c”, c];

    Destroza el formato del char. Si lleva un acento o cualquier símbolo en UTF8 (por ejemplo) se lo carga.

    La solución es crear una string temporal:

    // Transform unichar to string
    NSString* uString = [NSString stringWithCharacters:&c length:1];

    [newStr appendFormat:uString];

    Con esto queda soluciado.😉

    Gracias de nuevo

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: