Remover tags HTML de um NSString no iPhone

106

Há um par de maneiras diferentes para remover HTML tagsa partir de um NSStringno Cocoa.

Uma maneira é renderizar a string em um NSAttributedStringe então capturar o texto renderizado.

Outra maneira é usar o método NSXMLDocument's- objectByApplyingXSLTStringpara aplicar uma XSLTtransformação que faça isso.

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

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

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

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

<!-- <A comment> -->

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

<![INCLUDE CDATA [ >>>>>>>>>>>> ]]>
lfalin
fonte
Você poderia adicionar um pouco de lógica para levar aspas e apóstrofos em consideração ... CDATA daria um pouco mais de trabalho, mas o ponto principal do HTML é que tags desconhecidas podem ser ignoradas pelo analisador; se você tratar TODAS as tags como desconhecidas, deverá obter apenas o texto bruto.
Ben Gottlieb
Eu gostaria de comentar que uma expressão regular boa (mas básica) definitivamente não quebrará em seus exemplos. Certamente não se você puder garantir um XHTML bem formado. Eu sei que você disse que não pode, mas me pergunto por quê ;-)
Jake,
1
Existe uma boa resposta para esta pergunta. Achatar HTML usando Objective c
vipintj
Infelizmente, usar o NSScanner é extremamente lento.
steipete
Ainda mais infelizmente, o exemplo NSScanner vinculado funciona apenas para html trivial. Ele falha em todos os casos de teste que mencionei em meu post.
lfalin

Respostas:

309

Uma solução rápida e "suja" (remove tudo entre <e>), 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 declarei isso como uma categoria do NSString.

m.kocikowski
fonte
4
@James Para usar o método postado na solução. Você tem que criar uma categoria para NSString. Pesquise "Categoria Objective-C" no Google. Em seguida, você adiciona esse método no arquivo m, e o protótipo no arquivo h. Quando tudo estiver configurado, para usá-lo, tudo o que você precisa fazer é ter um objeto string (Exemplo: NSString * myString = ...) e chamar esse método em seu objeto string (NSString * strippedString = [myString stringByStrippingHTML]; )
Roberto
3
+1 Ótimo uso para expressões regulares, mas infelizmente não cobre muitos casos.
matm
3
Realmente rápido e sujo ... Esta função causa um grande vazamento de memória em meu aplicativo ... Bem, em sua defesa, estou usando grandes quantidades de dados ...
EZFrag
5
No meu aplicativo esta solução causou problemas de desempenho. Mudei para uma solução com NSScanner, em vez NSRegularExpressionSearch. Agora os problemas de desempenho acabaram
carmen_munich
2
É muito, muito, muito muito demorado e demorado. Use apenas com pequenas quantidades de html!
ullstrm
29

Esta NSStringcategoria usa o NSXMLParserpara remover com precisão quaisquer HTMLtags de um NSString. Este é um único .me .hde arquivos que podem ser incluídos em seu projeto facilmente.

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

Em seguida, você tira a roupa htmlfazendo o seguinte:

Importe o cabeçalho:

#import "NSString_stripHtml.h"

E então chame stripHtml:

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

Isso também funciona com malformados HTMLque tecnicamente não são XML.

Leigh McCulloch
fonte
3
Embora a expressão regular (como dito por m.kocikowski) seja rápida e suja, ela é mais robusta. String de exemplo: @ "My test <span font = \" font> name \ "> string html". Esta resposta retorna: My test html string. A expressão regular retorna: Meu nome de teste "> string html. Embora não seja tão comum, é apenas mais robusto.
DonnaLea,
1
Exceto se você tiver uma string como "S&P 500", ele removerá tudo após o E comercial e apenas retornará a string "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 bem para mim

MANCHIKANTI KRISHNAKISHORE
fonte
1
Tive um problema de codificação com esta solução
KIDdAe
Provavelmente a melhor solução, mas é inútil para um UILabel :-(
Zeb
9

Você pode usar como abaixo

-(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;
 }
Kirtikumar A.
fonte
8

usa isto

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

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

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

Mohamed AHDIDOU
fonte
7

Dê uma olhada em NSXMLParser. É um analisador de 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.

Colin Barrett
fonte
6

Esta é uma solução mais eficiente do 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 NSStringcategoria 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 local, iterando sobre elas na ordem reversa. É mais eficiente porque:

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

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

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

hpique
fonte
5

Sem 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:@""];
}
Rémy
fonte
Esta deve ser a resposta aceita. O atual é um desperdício ridículo.
Adlai Holler de
5
NSAttributedString *str=[[NSAttributedString alloc] initWithData:[trimmedString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]} documentAttributes:nil error:nil];
Pavan Sisode
fonte
Quando tivermos os metadados com tags HTML e quisermos aplicar essas tags, devemos aplicar o código acima para obter a saída desejada.
Pavan Sisode
4
#import "RegexKitLite.h"

string text = [html stringByReplacingOccurrencesOfRegex:@"<[^>]+>" withString:@""]
Jim Liu
fonte
2
HTML não é uma linguagem regular, então você não deve tentar analisá-la / removê-la com uma expressão regular. stackoverflow.com/questions/1732348/…
csaunders
3

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 classe de Utils estática (eu sei que uma categoria é provavelmente o melhor design) e removi o autorelease para que seja compilado em um projeto ARC.

Incluído aqui, caso alguém o considere ú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; 
}
Dan J
fonte
Este método é útil, mas, se eu precisar remover alguma tag, como link <a> quem posso atualizar este método para cumprir isso
wod
@wod então apenas altere o regex para <(?>/?)(?!a).+?>remover todas as tags, exceto as tags de abertura <a> e de fechamento </a>.
Ashoor
3

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

  NSString *myText = [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.textContent"];
Biranchi
fonte
<br> está sendo substituído por nada ... o que é indesejável.
Nishant
2

Eu imagino que a maneira mais segura seria apenas analisar para <> s, não? Faça um loop por toda a string e copie qualquer coisa que não esteja entre <> s para uma nova string.

Ben Gottlieb
fonte
2

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
digipeople
fonte
2

a seguir está a resposta aceita, mas em vez da categoria, é um método auxiliar simples com string passada para ele. (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;
}
tmr
fonte
2

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("&nbsp;", withString: " ")
  copy = copy.stringByReplacingOccurrencesOfString("&amp;", withString: "&")
  return copy
}
JohnVanDijk
fonte
Cara, o stringByReplacingOccurrencesOfStringuso fora do ciclo é codificação percentual e deve ser corrigido de forma correta.
Vyachaslav Gerchicov
0

Se você deseja usar o framework Three20 , ele tem uma categoria em NSString que adiciona o método stringByRemovingHTMLTags. Consulte NSStringAdditions.h no subprojeto Three20Core.

Jarnoan
fonte
26
Pelo amor de Deus, não use o Three20 para nada. Estrutura mais inchada e com comentários ruins de todos os tempos.
kompozer de
0

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

1 # Primeiro você precisa criar c-categorias objetivas para tornar o código utilizável em qualquer classe.

.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 # Em seguida, basta importar o arquivo .h da classe 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);

o resultado é NSString de onde quero retirar as tags.

Ashoor
fonte
0

Segui a resposta aceita por m.kocikowski e modifiquei um pouco para usar um autoreleasepool para limpar todas as strings temporárias criadas por stringByReplacingCharactersInRange

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

Portanto, dependendo do comprimento de seu XML, você pode estar criando uma grande pilha de novas strings de autorelease que não são limpas até o final do próximo @autoreleasepool. Se você não tiver certeza de quando isso pode acontecer ou se uma ação do usuário puder disparar repetidamente muitas chamadas para este método antes, você pode simplesmente encerrar em um @autoreleasepool. Eles podem até mesmo ser aninhados e usados ​​em loops quando possível.

A referência da Apple em @autoreleasepool declara isso ... "Se você escrever um loop que cria muitos objetos temporários. Você pode usar um bloco de pool de liberação automática dentro do loop para descartar esses objetos antes da próxima iteração. Usando um bloco de pool de liberação automática no loop ajuda a reduzir a pegada máxima de memória do aplicativo. " Eu não usei no loop, mas pelo menos este método se limpa sozinho agora.

- (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;
}
jcpennypincher
fonte
0

Outra maneira:

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];

Nik Kov
fonte
este método está removendo tags html. mas eu quero analisar string html. o que fazer
Krutarth Patel
economizou meu tempo. boa solução
Krutarth Patel
0

Uma resposta atualizada para @ m.kocikowski que funciona nas 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;

}

Ahmed Awad
fonte
-3

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

micco
fonte
Este é o conjunto exato de comentários que vinculei na minha pergunta como um exemplo do que não funcionaria.
lfalin