Rimuovi i tag HTML da una NSString su iPhone


106

Esistono un paio di modi diversi per rimuovere HTML tagsda un file NSStringin Cocoa.

Un modo è rendere la stringa in un NSAttributedStringe quindi prendere il testo renderizzato.

Un altro modo è usare NSXMLDocument's- objectByApplyingXSLTStringmetodo per applicare una XSLTtrasformazione che lo fa.

Sfortunatamente, l'iPhone non supporta NSAttributedStringo NSXMLDocument. Ci sono troppi casi limite e HTMLdocumenti malformati perché io possa sentirmi a mio agio usando regex o NSScanner. Qualcuno ha una soluzione a questo?

Un suggerimento è stato quello di cercare semplicemente i caratteri dei tag di apertura e chiusura, questo metodo non funzionerà tranne che per casi molto banali.

Ad esempio, questi casi (dal capitolo Perl Cookbook sullo stesso argomento) rompono questo metodo:

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

<!-- <A comment> -->

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

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

Potresti aggiungere un po 'di logica per prendere in considerazione virgolette e apostrofi ... CDATA richiederebbe un po' più di lavoro, ma il punto centrale dell'HTML è che i tag sconosciuti possono essere ignorati dal parser; se tratti TUTTI i tag come sconosciuti, dovresti ottenere solo testo non elaborato.
Ben Gottlieb

Vorrei commentare che un'espressione regolare buona (ma di base) sicuramente non interromperà i tuoi esempi. Certamente no se puoi garantire un XHTML ben formato. So che hai detto che non puoi, ma mi chiedo perché ;-)
Jake

1
C'è una buona risposta per questa domanda. Appiattisci l'HTML usando Objective c
vipintj

Sfortunatamente, l'utilizzo di NSScanner è dannatamente lento.
steipete

Ancora più sfortunatamente, l'esempio NSScanner collegato funziona solo per banali HTML. Fallisce per ogni caso di test che ho menzionato nel mio post.
lfalin

Risposte:


309

Una soluzione rapida e "sporca" (rimuove tutto tra <e>), funziona 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;
}

L'ho dichiarato come categoria os NSString.


4
@ James Per utilizzare il metodo pubblicato nella soluzione. Devi creare una categoria per NSString. Cerca "Categoria Objective-C" su Google. Quindi aggiungi quel metodo nel file m e il prototipo nel file h. Quando tutto è impostato, per usarlo tutto ciò che devi fare è avere un oggetto stringa (Esempio: NSString * myString = ...) e chiamare quel metodo sul tuo oggetto stringa (NSString * strippedString = [myString stringByStrippingHTML]; ).
Roberto

3
+1 Ottimo utilizzo per le espressioni regolari, ma sfortunatamente non copre molti casi.
matm

3
Veloce e sporco davvero .... Questa funzione causa un'enorme perdita di memoria nella mia applicazione ... Beh, a sua difesa, sto usando grandi quantità di dati ....
EZFrag

5
Nella mia app questa soluzione ha causato problemi di prestazioni. Sono passato a una soluzione con NSScanner invece NSRegularExpressionSearch. Ora i problemi di prestazione sono spariti
carmen_munich

2
È molto molto molto memoria e richiede tempo. Usalo solo con piccole quantità di html!
ullstrm

29

Questa NSStringcategoria utilizza NSXMLParserper rimuovere accuratamente qualsiasi HTMLtag da un file NSString. Questo è un file singolo .me .hche può essere facilmente incluso nel tuo progetto.

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

Quindi ti spogli htmlfacendo quanto segue:

Importa l'intestazione:

#import "NSString_stripHtml.h"

E poi chiama stripHtml:

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

Funziona anche con malformati HTMLche tecnicamente non lo sono XML.


3
Sebbene l'espressione regolare (come detto da m.kocikowski) sia rapida e sporca, questa è più robusta. Stringa di esempio: @ "My test <span font = \" font> name \ "> html string". Questa risposta restituisce: La mia stringa html di prova. Restituisce espressioni regolari: My test name "> html string. Anche se non è così comune, è solo più robusto.
DonnaLea

1
Tranne se hai una stringa come "S&P 500", rimuoverà tutto dopo la e commerciale e restituirà semplicemente la stringa "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];

funziona bene per me



Probabilmente la soluzione migliore, ma è inutile per un UILabel :-(
Zeb

9

Puoi usare come di seguito

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

Usa questo

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

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

non dimenticare di includerlo nel codice: #import "RegexKitLite.h" ecco il link per scaricare questa API: http://regexkit.sourceforge.net/#Downloads


7

Dai un'occhiata a NSXMLParser. È un parser in stile SAX. Dovresti essere in grado di usarlo per rilevare tag o altri elementi indesiderati nel documento XML e ignorarli, catturando solo testo puro.


6

Ecco una soluzione più efficiente rispetto alla risposta accettata:

- (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 NSStringcategoria precedente utilizza un'espressione regolare per trovare tutti i tag corrispondenti, crea una copia della stringa originale e infine rimuove tutti i tag in posizione ripetendoli in ordine inverso. È più efficiente perché:

  • L'espressione regolare viene inizializzata solo una volta.
  • Viene utilizzata una singola copia della stringa originale.

Questo ha funzionato abbastanza bene per me, ma una soluzione che utilizza NSScannerpotrebbe essere più efficiente.

Come la risposta accettata, questa soluzione non risolve tutti i casi di confine richiesti da @lfalin. Quelli richiederebbero un'analisi molto più costosa di cui il caso d'uso medio molto probabilmente non ha bisogno.


5

Senza loop (almeno dalla nostra parte):

- (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:@""];
}

Questa dovrebbe essere la risposta accettata. Quello attuale è ridicolmente dispendioso.
Adlai Holler

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

Quando abbiamo i metadati con tag HTML e vogliamo applicare quei tag, quella volta dovremmo applicare il codice sopra per ottenere l'output desiderato.
Pavan Sisode


3

Ho esteso la risposta di m.kocikowski e ho cercato di renderlo un po 'più efficiente utilizzando NSMutableString. L'ho anche strutturato per l'uso in una classe Utils statica (so che una categoria è probabilmente il miglior design) e ho rimosso l'autorizzazione in modo che si compili in un progetto ARC.

Incluso qui nel caso qualcuno lo trovi utile.

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

Questo metodo è utile ma, se ho bisogno di non rimuovere alcuni tag come link <a> chi posso aggiornare questo metodo per soddisfarlo
wod

@wod quindi cambia semplicemente la regex in <(?>/?)(?!a).+?>questo rimuoverà tutti i tag esclusi i tag di apertura <a> e chiusura </a>.
Ashoor

3

Se si desidera ottenere il contenuto senza i tag html dalla pagina Web (documento HTML), utilizzare questo codice all'interno del metodo UIWebViewDidfinishLoading delegato .

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

<br> viene sostituito da niente ... il che è indesiderabile.
Nishant

2

Immagino che il modo più sicuro sarebbe quello di analizzare <> s, no? Ripeti l'intera stringa e copia tutto ciò che non è racchiuso tra <> s in una nuova stringa.


2

Questa è la modernizzazione della risposta di m.kocikowski che rimuove gli spazi bianchi:

@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 seguente è la risposta accettata, ma invece della categoria, è un semplice metodo di supporto con una stringa passata al suo interno. (grazie 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

Ecco la versione rapida:

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
}

Amico, l' stringByReplacingOccurrencesOfStringuso al di fuori del ciclo è una codifica percentuale e dovrebbe essere corretto in modo corretto.
Vyachaslav Gerchicov

0

Se sei disposto a utilizzare il framework Three20 , ha una categoria su NSString che aggiunge il metodo stringByRemovingHTMLTags. Vedere NSStringAdditions.h nel sottoprogetto Three20Core.


26
Per l'amor di Dio, non usare Three20 per niente. Il quadro più gonfio e male commentato mai.
kompozer

0

Estendendolo di più dalle risposte di m.kocikowski e Dan J con più spiegazioni per i neofiti

1 # Per prima cosa devi creare categorie-obiettivo per rendere il codice utilizzabile in qualsiasi 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 # Quindi importa semplicemente il file .h della classe di categoria che hai appena creato, ad es

#import "NSString+NAME_OF_CATEGORY.h"

3 # Chiamare il metodo.

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

il risultato è NSString da cui desidero rimuovere i tag.


0

Ho seguito la risposta accettata da m.kocikowski e modificata è leggermente per utilizzare un autoreleasepool per pulire tutte le stringhe temporanee create da stringByReplacingCharactersInRange

Nel commento per questo metodo si afferma, / * Sostituisci i caratteri nell'intervallo con la stringa specificata, restituendo una nuova stringa. * /

Quindi, a seconda della lunghezza del tuo XML potresti creare un'enorme pila di nuove stringhe di rilascio automatico che non vengono pulite fino alla fine del prossimo @autoreleasepool. Se non sei sicuro di quando ciò può accadere o se un'azione dell'utente potrebbe innescare ripetutamente molte chiamate a questo metodo prima, puoi semplicemente racchiuderlo in un @autoreleasepool. Questi possono anche essere annidati e utilizzati all'interno di cicli, ove possibile.

Il riferimento di Apple su @autoreleasepool afferma questo ... "Se scrivi un ciclo che crea molti oggetti temporanei. Puoi utilizzare un blocco di pool di rilascio automatico all'interno del ciclo per eliminare quegli oggetti prima dell'iterazione successiva. Usare un blocco di pool di rilascio automatico nel ciclo aiuta a ridurre il footprint di memoria massimo dell'applicazione. " Non l'ho usato nel ciclo, ma almeno questo metodo si ripulisce da solo ora.

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

Un altro modo:

Interfaccia:

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

Implementazione

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

Realizzazione

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

o semplice

NSString *myClearStr = [self stringByStrippingHTML:rudeStr];


questo metodo sta rimuovendo i tag html. ma voglio analizzare la stringa html. cosa fare
Krutarth Patel

mi ha fatto risparmiare tempo. bella soluzione
Krutarth Patel

0

Una risposta aggiornata per @ m.kocikowski che funziona sulle versioni iOS recenti.

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

}


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.