Remover tags HTML de um NSString no iPhone

Existem algumas maneiras diferentes de remover HTML tags de um NSString em Cocoa .

Uma maneira é processar a cadeia em um NSAttributedString e, em seguida, pegue o texto processado.

Outra maneira é usar NSXMLDocument's método – NSXMLDocument's objectByApplyingXSLTString para aplicar uma transformação XSLT que o faça.

Infelizmente, o iPhone não suporta NSAttributedString ou NSXMLDocument . Existem muitos casos de borda e documentos HTML malformados para que eu me sinta confortável usando o regex ou o NSScanner . Alguém tem uma solução para isso?

Uma sugestão foi simplesmente procurar abrir e fechar caracteres de tag, este método não funcionará, exceto em casos muito triviais.

Por exemplo, esses casos (do capítulo Perl Cookbook sobre o mesmo assunto) quebrariam esse método:

  B"> <!--  --> if (ac) >>>>>>>>>>> ]]> 

Uma solução rápida e “suja” (remove tudo entre ), funciona com 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; } 

Eu tenho isso declarado como uma categoria de NSString.

Esta categoria NSString usa o NSXMLParser para remover com precisão qualquer tag HTML de um NSString . Este é um único arquivo .m e .h que pode ser incluído em seu projeto facilmente.

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

Você tira o html fazendo o seguinte:

Importe o header:

 #import "NSString_stripHtml.h" 

E então chame stripHtml:

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

Isso também funciona com HTML malformado que tecnicamente não é XML .

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

isso é bom para mim

usa isto

 NSString *myregex = @"<[^>]*>"; //regex to remove any html tag NSString *htmlString = @"bla bla"; NSString *stringWithoutHTML = [hstmString stringByReplacingOccurrencesOfRegex:myregex withString:@""]; 

não se esqueça de include isso no seu código: #import “RegexKitLite.h” aqui está o link para baixar esta API: http://regexkit.sourceforge.net/#Downloads

Dê uma olhada no NSXMLParser. É um analisador do estilo SAX. Você deve ser capaz de usá-lo para detectar tags ou outros elementos indesejados no documento XML e ignorá-los, capturando apenas texto puro.

Você pode usar como abaixo

 -(void)myMethod { NSString* htmlStr = @"html"; 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; } 

Aqui está uma solução mais eficiente que a resposta aceita:

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

A categoria NSString acima usa uma expressão regular para encontrar todas as tags correspondentes, faz uma cópia da string original e, finalmente, remove todas as tags no lugar, iterando-as na ordem inversa. É mais eficiente porque:

  • A expressão regular é inicializada apenas uma vez.
  • Uma única cópia da string original é usada.

Isso NSScanner bem o suficiente para mim, mas uma solução usando o NSScanner pode ser mais eficiente.

Como a resposta aceita, esta solução não aborda todos os casos de fronteira solicitados pelo @lfalin. Isso exigiria uma análise muito mais cara, que o caso de uso médio provavelmente não precisa.

Sem um loop (pelo menos do nosso 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:@""]; } 
 #import "RegexKitLite.h" string text = [html stringByReplacingOccurrencesOfRegex:@"<[^>]+>" withString:@""] 

Se você deseja obter o conteúdo sem as tags html da página da web (documento HTML), use esse código dentro do método delegado UIWebViewDidfinishLoading .

  NSString *myText = [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.textContent"]; 
 NSAttributedString *str=[[NSAttributedString alloc] initWithData:[trimmedString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]} documentAttributes:nil error:nil]; 

Eu estendi a resposta por m.kocikowski e tentei torná-la um pouco mais eficiente usando um NSMutableString. Eu também o estruturei para uso em uma class estática de Utils (eu sei que uma categoria é provavelmente o melhor design), e removi o autorelease para que ele compila em um projeto ARC.

Incluído aqui no caso de alguém achar útil.

.h

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

.m

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

Eu imagino que o caminho mais seguro seria simplesmente analisar <> s, não? Faça um loop por toda a string e copie qualquer coisa que não esteja entre

Esta é a modernização da resposta m.kocikowski que remove os espaços em branco:

 @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 

A seguir está a resposta aceita, mas em vez de categoria, é um método auxiliar simples com uma cadeia passada para ela. (obrigado 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; } 

Se você estiver disposto a usar o framework Three20 , ele terá uma categoria no NSString que adiciona o método stringByRemovingHTMLTags. Veja NSStringAdditions.h no subprojeto Three20Core.

Estendendo isso mais das respostas de m.kocikowski e Dan J com mais explicações para iniciantes

1 # Primeiro você tem que criar categorias object-c para tornar o código utilizável em qualquer class.

.h

 @interface NSString (NAME_OF_CATEGORY) - (NSString *)stringByStrippingHTML; @end 

.m

 @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 # Então, basta importar o arquivo .h da class de categoria que você acabou de criar, por exemplo:

 #import "NSString+NAME_OF_CATEGORY.h" 

3 # Chamando o método.

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

resultado é NSString eu quero tirar as tags de.

Eu tenho seguindo a resposta aceita por m.kocikowski e modificado é um pouco para fazer uso de um autoreleasepool para limpar todas as seqüências de caracteres temporárias que são criadas por stringByReplacingCharactersInRange

No comentário para este método, ele afirma, / * Substitua caracteres no intervalo pela string especificada, retornando uma nova string. * /

Portanto, dependendo do tamanho do seu XML, você pode estar criando uma enorme pilha de novas cadeias de autorelease que não são limpas até o final do próximo @autoreleasepool. Se você não tem certeza de quando isso pode acontecer ou se uma ação do usuário pode desencadear repetidamente muitas chamadas para esse método antes, você pode apenas agrupar isso em um @autoreleasepool. Estes podem até ser nesteds e usados ​​dentro de loops sempre que possível.

A referência da Apple em @autoreleasepool declara isso … “Se você escrever um loop que cria muitos objects temporários. Você pode usar um bloco de pool autorelease dentro do loop para descartar esses objects antes da próxima iteração. Usando um bloco de pool de autorelease no loop ajuda a reduzir o consumo máximo de memory do aplicativo. ” Eu não usei no loop, mas pelo menos este método se limpa agora mesmo.

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

Aqui está a versão 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(" ", withString: " ") copy = copy.stringByReplacingOccurrencesOfString("&", withString: "&") return copy } 

Outro caminho:

Interface:

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

Implementação

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

Realização

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

ou simples

NSString *myClearStr = [self stringByStrippingHTML:rudeStr];

Uma resposta atualizada para @ m.kocikowski que funciona em versões recentes do iOS.

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

}

Versão rápida de @ m.kocikowski Resposta:

 extension String { func isEmptyHTMLContent() -> Bool { var r = Range(self.startIndex..]+>", options: .regularExpression) { r = range str = str.replacingCharacters(in: r, with: "") } str = str.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines) return str.isEmpty } } 

PS: funciona para mim

Aqui está uma postagem no blog que discute algumas bibliotecas disponíveis para remover HTML http://sugarmaplesoftware.com/25/strip-html-tags/ Observe os comentários em que outras soluções são oferecidas.