Eliminar etiquetas HTML de un NSString en el iPhone


106

Hay un par de formas diferentes de eliminar HTML tagsun archivo NSStringin Cocoa.

Una forma es convertir la cadena en un NSAttributedStringy luego tomar el texto renderizado.

Otra forma es usar NSXMLDocument's- objectByApplyingXSLTStringmétodo para aplicar una XSLTtransformación que lo haga.

Desafortunadamente, el iPhone no es compatible con NSAttributedStringo NSXMLDocument. Hay demasiados casos extremos y HTMLdocumentos mal formados para que me sienta cómodo usando expresiones regulares o NSScanner. ¿Alguien tiene una solución para esto?

Una sugerencia ha sido simplemente buscar caracteres de etiqueta de apertura y cierre, este método no funcionará excepto en casos muy triviales.

Por ejemplo, estos casos (del capítulo del libro de recetas de Perl sobre el mismo tema) romperían este método:

<IMG SRC = "foo.gif" ALT = "A > B">

<!-- <A comment> -->

<script>if (a<b && a>c)</script>

<![INCLUDE CDATA [ >>>>>>>>>>>> ]]>

Podría agregar un poco de lógica para tener en cuenta las comillas y los apóstrofos ... CDATA requeriría un poco más de trabajo, pero el objetivo de HTML es que el analizador puede ignorar las etiquetas desconocidas; si trata TODAS las etiquetas como desconocidas, entonces solo debería obtener texto sin formato.
Ben Gottlieb

Me gustaría comentar que una expresión regular buena (pero básica) definitivamente no se romperá con sus ejemplos. Ciertamente no si puede garantizar un XHTML bien formado. Sé que dijiste que no puedes, pero me pregunto por qué ;-)
Jake

1
Hay una buena respuesta para esta pregunta. Aplanar HTML usando Objective c
vipintj

Desafortunadamente, usar NSScanner es muy lento.
steipete

Aún más desafortunadamente, el ejemplo de NSScanner vinculado solo funciona para html trivial. Falla en todos los casos de prueba que mencioné en mi publicación.
lfalin

Respuestas:


309

Una solución rápida y "sucia" (elimina todo entre <y>), funciona con iOS> = 3.2:

-(NSString *) stringByStrippingHTML {
  NSRange r;
  NSString *s = [[self copy] autorelease];
  while ((r = [s rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
    s = [s stringByReplacingCharactersInRange:r withString:@""];
  return s;
}

Tengo esto declarado como una categoría de NSString.


4
@James Para usar el método publicado en la solución. Tienes que crear una categoría para NSString. Busque "Categoría Objective-C" en Google. Luego, agrega ese método en el archivo my el prototipo en el archivo h. Cuando todo está configurado, para usarlo todo lo que tiene que hacer es tener un objeto de cadena (Ejemplo: NSString * myString = ...) y llamar a ese método en su objeto de cadena (NSString * strippedString = [myString stringByStrippingHTML]; ).
Roberto

3
+1 Excelente uso para expresiones regulares, pero desafortunadamente no cubre muchos casos.
matm

3
De hecho, rápido y sucio .... Esta función causa una gran pérdida de memoria en mi aplicación ... Bueno, en su defensa, estoy usando grandes cantidades de datos ....
EZFrag

5
En mi aplicación, esta solución causó problemas de rendimiento. Cambié a una solución con NSScanner en lugar de NSRegularExpressionSearch. Ahora los problemas de rendimiento se han ido
carmen_munich

2
Es muy muy muy memoria y consume mucho tiempo. ¡Úselo solo con pequeñas cantidades de html!
ullstrm

29

Esta NSStringcategoría utiliza NSXMLParserpara eliminar con precisión cualquier HTMLetiqueta de un NSString. Se trata de un único .my .harchivos que se pueden incluir en su proyecto fácilmente.

https://gist.github.com/leighmcculloch/1202238

Luego te desnudas htmlhaciendo lo siguiente:

Importar el encabezado:

#import "NSString_stripHtml.h"

Y luego llame a stripHtml:

NSString* mystring = @"<b>Hello</b> World!!";
NSString* stripped = [mystring stripHtml];
// stripped will be = Hello World!!

Esto también funciona con malformados HTMLque técnicamente no lo son XML.


3
Si bien la expresión regular (como dijo m.kocikowski) es rápida y sucia, esta es más robusta. Cadena de ejemplo: @ "Mi prueba <span font = \" font> name \ "> cadena html". Esta respuesta devuelve: Mi cadena html de prueba. La expresión regular devuelve: My test name "> html string. Si bien esto no es tan común, es más robusto.
DonnaLea

1
Excepto si tiene una cadena como "S&P 500", eliminará todo después del signo comercial y simplemente devolverá la cadena "S".
Joshua Gross

11
UITextView *textview= [[UITextView alloc]initWithFrame:CGRectMake(10, 130, 250, 170)];
NSString *str = @"This is <font color='red'>simple</font>";
[textview setValue:str forKey:@"contentToHTMLString"];
textview.textAlignment = NSTextAlignmentLeft;
textview.editable = NO;
textview.font = [UIFont fontWithName:@"vardana" size:20.0];
[UIView addSubview:textview];

funciona bien para mi


1
Tengo un problema de codificación con esta solución
KIDdAe

Probablemente la mejor solución, pero es inútil para un UILabel :-(
Zeb

9

Puedes usar como a continuación

-(void)myMethod
 {

 NSString* htmlStr = @"<some>html</string>";
 NSString* strWithoutFormatting = [self stringByStrippingHTML:htmlStr];

 }

 -(NSString *)stringByStrippingHTML:(NSString*)str
 {
   NSRange r;
   while ((r = [str rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location     != NSNotFound)
  {
     str = [str stringByReplacingCharactersInRange:r withString:@""];
 }
  return str;
 }

8

utilizar este

NSString *myregex = @"<[^>]*>"; //regex to remove any html tag

NSString *htmlString = @"<html>bla bla</html>";
NSString *stringWithoutHTML = [hstmString stringByReplacingOccurrencesOfRegex:myregex withString:@""];

no olvide incluir esto en su código: #import "RegexKitLite.h" aquí está el enlace para descargar esta API: http://regexkit.sourceforge.net/#Downloads


7

Eche un vistazo a NSXMLParser. Es un analizador de estilo SAX. Debería poder usarlo para detectar etiquetas u otros elementos no deseados en el documento XML e ignorarlos, capturando solo texto puro.


6

Aquí hay una solución más eficiente que la respuesta aceptada:

- (NSString*)hp_stringByRemovingTags
{
    static NSRegularExpression *regex = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        regex = [NSRegularExpression regularExpressionWithPattern:@"<[^>]+>" options:kNilOptions error:nil];
    });

    // Use reverse enumerator to delete characters without affecting indexes
    NSArray *matches =[regex matchesInString:self options:kNilOptions range:NSMakeRange(0, self.length)];
    NSEnumerator *enumerator = matches.reverseObjectEnumerator;

    NSTextCheckingResult *match = nil;
    NSMutableString *modifiedString = self.mutableCopy;
    while ((match = [enumerator nextObject]))
    {
        [modifiedString deleteCharactersInRange:match.range];
    }
    return modifiedString;
}

La NSStringcategoría anterior usa una expresión regular para encontrar todas las etiquetas coincidentes, hace una copia de la cadena original y finalmente elimina todas las etiquetas en su lugar iterando sobre ellas en orden inverso. Es más eficiente porque:

  • La expresión regular se inicializa solo una vez.
  • Se utiliza una única copia de la cadena original.

Esto funcionó bastante bien para mí, pero el uso de una solución NSScannerpodría ser más eficiente.

Al igual que la respuesta aceptada, esta solución no aborda todos los casos fronterizos solicitados por @lfalin. Esos requerirían un análisis mucho más costoso que el caso de uso promedio probablemente no necesite.


5

Sin bucle (al menos de nuestro lado):

- (NSString *)removeHTML {

    static NSRegularExpression *regexp;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        regexp = [NSRegularExpression regularExpressionWithPattern:@"<[^>]+>" options:kNilOptions error:nil];
    });

    return [regexp stringByReplacingMatchesInString:self
                                            options:kNilOptions
                                              range:NSMakeRange(0, self.length)
                                       withTemplate:@""];
}

Esta debería ser la respuesta aceptada. El actual es ridículamente derrochador.
Adlai Holler

5
NSAttributedString *str=[[NSAttributedString alloc] initWithData:[trimmedString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]} documentAttributes:nil error:nil];

Cuando tenemos los metadatos con etiquetas HTML y queremos aplicar esas etiquetas, esa vez debemos aplicar el código anterior para lograr el resultado deseado.
Pavan Sisodio


3

Extendí la respuesta de m.kocikowski y traté de hacerla un poco más eficiente utilizando NSMutableString. También lo estructuré para usarlo en una clase de Utils estática (aunque sé que una Categoría es probablemente el mejor diseño), y eliminé el lanzamiento automático para que se compile en un proyecto ARC.

Incluido aquí por si alguien lo encuentra útil.

.h

+ (NSString *)stringByStrippingHTML:(NSString *)inputString;

.metro

+ (NSString *)stringByStrippingHTML:(NSString *)inputString 
{
  NSMutableString *outString;

  if (inputString)
  {
    outString = [[NSMutableString alloc] initWithString:inputString];

    if ([inputString length] > 0)
    {
      NSRange r;

      while ((r = [outString rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
      {
        [outString deleteCharactersInRange:r];
      }      
    }
  }

  return outString; 
}

Este método es útil pero, si necesito no quitar alguna etiqueta como el enlace <a>, ¿quién puedo actualizar este método para cumplir con esto?
wod

@wod, luego simplemente cambie la expresión regular a <(?>/?)(?!a).+?>esto eliminará todas las etiquetas, excepto las etiquetas de apertura <a> y cierre </a>.
Ashoor

3

Si desea obtener el contenido sin las etiquetas html de la página web (documento HTML), utilice este código dentro del método UIWebViewDidfinishLoading delegado .

  NSString *myText = [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.textContent"];

<br> está siendo reemplazado por nada ... lo cual es indeseable.
Nishant

2

Me imagino que la forma más segura sería analizar para <> s, ¿no? Recorra toda la cadena y copie todo lo que no esté entre <> sa una nueva cadena.


2

Esta es la modernización de la respuesta de m.kocikowski que elimina los espacios en blanco:

@implementation NSString (StripXMLTags)

- (NSString *)stripXMLTags
{
    NSRange r;
    NSString *s = [self copy];
    while ((r = [s rangeOfString:@"<[^>]+>\\s*" options:NSRegularExpressionSearch]).location != NSNotFound)
        s = [s stringByReplacingCharactersInRange:r withString:@""];
    return s;
}

@end

2

la siguiente es la respuesta aceptada, pero en lugar de la categoría, es un método de ayuda simple con una cadena pasada. (gracias m.kocikowski)

-(NSString *) stringByStrippingHTML:(NSString*)originalString {
    NSRange r;
    NSString *s = [originalString copy];
    while ((r = [s rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
        s = [s stringByReplacingCharactersInRange:r withString:@""];
    return s;
}

2

Aquí está la versión rápida:

func stripHTMLFromString(string: String) -> String {
  var copy = string
  while let range = copy.rangeOfString("<[^>]+>", options: .RegularExpressionSearch) {
    copy = copy.stringByReplacingCharactersInRange(range, withString: "")
  }
  copy = copy.stringByReplacingOccurrencesOfString("&nbsp;", withString: " ")
  copy = copy.stringByReplacingOccurrencesOfString("&amp;", withString: "&")
  return copy
}

El hombre, que stringByReplacingOccurrencesOfStringusa fuera del ciclo es una codificación porcentual y debe corregirse de una manera correcta.
Vyachaslav Gerchicov

0

Si está dispuesto a usar el marco Three20 , tiene una categoría en NSString que agrega el método stringByRemovingHTMLTags. Consulte NSStringAdditions.h en el subproyecto Three20Core.


26
Por el amor de Dios, no uses Three20 para nada. El framework más hinchado y mal comentado jamás.
kompozer

0

Ampliando esto más de las respuestas de m.kocikowski y Dan J con más explicación para los novatos

1 # Primero tienes que crear categorías-c-objetivo para que el código sea utilizable en cualquier clase.

.h

@interface NSString (NAME_OF_CATEGORY)

- (NSString *)stringByStrippingHTML;

@end

.metro

@implementation NSString (NAME_OF_CATEGORY)

- (NSString *)stringByStrippingHTML
{
NSMutableString *outString;
NSString *inputString = self;

if (inputString)
{
    outString = [[NSMutableString alloc] initWithString:inputString];

    if ([inputString length] > 0)
    {
        NSRange r;

        while ((r = [outString rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
        {
            [outString deleteCharactersInRange:r];
        }
    }
}

return outString;
}

@end

2 # Luego simplemente importe el archivo .h de la clase de categoría que acaba de crear, por ejemplo

#import "NSString+NAME_OF_CATEGORY.h"

3 # Llamar al método.

NSString* sub = [result stringByStrippingHTML];
NSLog(@"%@", sub);

El resultado es NSString del que quiero quitar las etiquetas.


0

Seguí la respuesta aceptada por m.kocikowski y la modifiqué ligeramente para hacer uso de un autoreleasepool para limpiar todas las cadenas temporales creadas por stringByReplacingCharactersInRange

En el comentario de este método dice, / * Reemplaza los caracteres en el rango con la cadena especificada, devolviendo una nueva cadena. * /

Por lo tanto, dependiendo de la longitud de su XML, es posible que esté creando una gran pila de nuevas cadenas de liberación automática que no se limpian hasta el final del siguiente @autoreleasepool. Si no está seguro de cuándo puede suceder eso o si la acción de un usuario podría desencadenar repetidamente muchas llamadas a este método antes, puede resumir esto en un @autoreleasepool. Estos incluso se pueden anidar y usar dentro de bucles siempre que sea posible.

La referencia de Apple en @autoreleasepool dice lo siguiente ... "Si escribe un bucle que crea muchos objetos temporales. Puede usar un bloque de grupo de liberación automática dentro del bucle para deshacerse de esos objetos antes de la siguiente iteración. Usar un bloque de grupo de liberación automática en el bucle ayuda a reducir la huella máxima de memoria de la aplicación ". No lo he usado en el ciclo, pero al menos este método se limpia después de sí mismo ahora.

- (NSString *) stringByStrippingHTML {
    NSString *retVal;
    @autoreleasepool {
        NSRange r;
        NSString *s = [[self copy] autorelease];
        while ((r = [s rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound) {
            s = [s stringByReplacingCharactersInRange:r withString:@""];
        }
        retVal = [s copy];
    } 
    // pool is drained, release s and all temp 
    // strings created by stringByReplacingCharactersInRange
    return retVal;
}

0

Otra forma:

Interfaz:

-(NSString *) stringByStrippingHTML:(NSString*)inputString;

Implementación

(NSString *) stringByStrippingHTML:(NSString*)inputString
{ 
NSAttributedString *attrString = [[NSAttributedString alloc] initWithData:[inputString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} documentAttributes:nil error:nil];
NSString *str= [attrString string]; 

//you can add here replacements as your needs:
    [str stringByReplacingOccurrencesOfString:@"[" withString:@""];
    [str stringByReplacingOccurrencesOfString:@"]" withString:@""];
    [str stringByReplacingOccurrencesOfString:@"\n" withString:@""];

    return str;
}

Realización

cell.exampleClass.text = [self stringByStrippingHTML:[exampleJSONParsingArray valueForKey: @"key"]];

o simple

NSString *myClearStr = [self stringByStrippingHTML:rudeStr];


este método es eliminar etiquetas html. pero quiero analizar la cadena html. qué hacer
Krutarth Patel

me salvé la solución time.Nice
Krutarth Patel

0

Una respuesta actualizada para @ m.kocikowski que funciona en versiones recientes de iOS.

-(NSString *) stringByStrippingHTMLFromString:(NSString *)str {
NSRange range;
while ((range = [str rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
    str = [str stringByReplacingCharactersInRange:range withString:@""];
return str;

}


-3

Aquí hay una publicación de blog que analiza un par de bibliotecas disponibles para eliminar HTML http://sugarmaplesoftware.com/25/strip-html-tags/ Tenga en cuenta los comentarios donde se ofrecen otras soluciones.


Este es el conjunto exacto de comentarios al que me vinculé en mi pregunta como un ejemplo de lo que no funcionaría.
lfalin
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.