Entfernen Sie HTML-Tags von einem NSString auf dem iPhone


106

Es gibt verschiedene Möglichkeiten, um HTML tagsaus einem NSStringIn zu entfernen Cocoa.

Eine Möglichkeit besteht darin, die Zeichenfolge in eine zu rendern NSAttributedStringund dann den gerenderten Text abzurufen.

Eine andere Möglichkeit ist die Verwendung der Methode NSXMLDocument's- objectByApplyingXSLTStringMethode, um eine XSLTTransformation anzuwenden , die dies ausführt.

Leider unterstützt das iPhone nicht NSAttributedStringoder NSXMLDocument. Es gibt zu viele Randfälle und fehlerhafte HTMLDokumente, als dass ich mich mit Regex oder Regex wohl fühlen könnte NSScanner. Hat jemand eine Lösung dafür?

Ein Vorschlag war, einfach nach öffnenden und schließenden Tag-Zeichen zu suchen. Diese Methode funktioniert nur in sehr trivialen Fällen.

Zum Beispiel würden diese Fälle (aus dem Perl-Kochbuch-Kapitel zum gleichen Thema) diese Methode brechen:

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

<!-- <A comment> -->

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

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

Sie könnten ein wenig Logik hinzufügen, um Anführungszeichen und Apostrophe zu berücksichtigen ... CDATA würde etwas mehr Arbeit erfordern, aber der springende Punkt bei HTML ist, dass unbekannte Tags vom Parser ignoriert werden können. Wenn Sie ALLE Tags als unbekannt behandeln, sollten Sie nur Rohtext erhalten.
Ben Gottlieb

Ich möchte darauf hinweisen, dass ein guter (aber grundlegender) regulärer Ausdruck bei Ihren Beispielen definitiv nicht kaputt geht. Sicher nicht, wenn Sie wohlgeformtes XHTML garantieren können. Ich weiß, dass du gesagt hast, dass du es nicht kannst, aber ich frage mich warum ;-)
Jake

1
Auf diese Frage gibt es eine gute Antwort . Reduzieren Sie HTML mit Objective c
vipintj

Leider ist die Verwendung von NSScanner verdammt langsam.
Steipete

Noch bedauerlicher ist, dass das verknüpfte NSScanner-Beispiel nur für triviales HTML funktioniert. Es schlägt für jeden Testfall fehl, den ich in meinem Beitrag erwähnt habe.
lfalin

Antworten:


309

Eine schnelle und "schmutzige" (entfernt alles zwischen <und>) Lösung, funktioniert mit 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;
}

Ich habe dies als Kategorie von NSString deklariert.


4
@James Um die in der Lösung angegebene Methode zu verwenden. Sie müssen eine Kategorie für NSString erstellen. Suchen Sie in Google nach "Objective-C-Kategorie". Anschließend fügen Sie diese Methode in die m-Datei und den Prototyp in die h-Datei ein. Wenn das alles eingerichtet ist, müssen Sie nur ein String-Objekt haben (Beispiel: NSString * myString = ...) und diese Methode für Ihr String-Objekt aufrufen (NSString * strippedString = [myString stringByStrippingHTML]; ).
Roberto

3
+1 Hervorragende Verwendung für reguläre Ausdrücke, deckt aber leider nicht viele Fälle ab.
Matm

3
Schnell und schmutzig in der Tat ... Diese Funktion verursacht einen großen Speicherverlust in meiner Anwendung ... Nun, zu ihrer Verteidigung verwende ich große Datenmengen ...
EZFrag

5
In meiner App verursachte diese Lösung Leistungsprobleme. Ich habe stattdessen mit NSScanner zu einer Lösung mit NSRegularExpressionSearch gewechselt. Jetzt sind die Leistungsprobleme weg
carmen_munich

2
Es ist sehr sehr sehr speicher- und zeitaufwändig. Verwenden Sie dies nur mit kleinen Mengen von HTML!
ullstrm

29

Diese NSStringKategorie verwendet das NSXMLParser, um HTMLTags aus einem genau zu entfernen NSString. Dies ist eine Single .mund .hDatei, die einfach in Ihr Projekt aufgenommen werden kann.

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

Anschließend ziehen Sie sich wie htmlfolgt aus:

Importieren Sie den Header:

#import "NSString_stripHtml.h"

Und rufen Sie dann stripHtml auf:

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

Dies funktioniert auch mit Missbildungen HTML, die technisch nicht sind XML.


3
Während der reguläre Ausdruck (wie von m.kocikowski gesagt) schnell und schmutzig ist, ist dies robuster. Beispielzeichenfolge: @ "Mein Test <span font =" font> name "> HTML-Zeichenfolge". Diese Antwort gibt Folgendes zurück: Mein Test-HTML-String. Regulärer Ausdruck gibt zurück: Mein
Testname

1
Außer wenn Sie eine Zeichenfolge wie "S & P 500" haben, wird nach dem kaufmännischen Und alles entfernt und nur die Zeichenfolge "S" zurückgegeben.
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];

Arbeit gut für mich


1
Ich habe ein Codierungsproblem mit dieser Lösung
KIDdAe

Wahrscheinlich die beste Lösung, aber für ein UILabel nutzlos :-(
Zeb

9

Sie können wie unten verwenden

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

benutze das

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

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

Vergessen Sie nicht, dies in Ihren Code aufzunehmen: #import "RegexKitLite.h" Hier ist der Link zum Herunterladen dieser API: http://regexkit.sourceforge.net/#Downloads


7

Schauen Sie sich NSXMLParser an. Es ist ein Parser im SAX-Stil. Sie sollten in der Lage sein, damit Tags oder andere unerwünschte Elemente im XML-Dokument zu erkennen und zu ignorieren, wobei nur reiner Text erfasst wird.


6

Hier ist eine effizientere Lösung als die akzeptierte Antwort:

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

Die obige NSStringKategorie verwendet einen regulären Ausdruck, um alle übereinstimmenden Tags zu finden, erstellt eine Kopie der ursprünglichen Zeichenfolge und entfernt schließlich alle vorhandenen Tags, indem sie in umgekehrter Reihenfolge durchlaufen werden. Es ist effizienter, weil:

  • Der reguläre Ausdruck wird nur einmal initialisiert.
  • Eine einzelne Kopie der ursprünglichen Zeichenfolge wird verwendet.

Dies hat für mich gut genug funktioniert, aber eine Lösung, die verwendet wird, NSScannerkönnte effizienter sein.

Wie die akzeptierte Antwort behandelt diese Lösung nicht alle von @lfalin angeforderten Grenzfälle. Diese würden eine viel teurere Analyse erfordern, die der durchschnittliche Anwendungsfall höchstwahrscheinlich nicht benötigt.


5

Ohne Schleife (zumindest auf unserer Seite):

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

Dies sollte die akzeptierte Antwort sein. Der aktuelle ist lächerlich verschwenderisch.
Adlai Holler

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

Wenn wir die Metadaten mit HTML-Tags haben und diese Tags anwenden möchten, sollten wir dieses Mal den obigen Code anwenden, um die gewünschte Ausgabe zu erreichen.
Pavan Episode


3

Ich habe die Antwort von m.kocikowski erweitert und versucht, sie mithilfe eines NSMutableString etwas effizienter zu gestalten. Ich habe es auch für die Verwendung in einer statischen Utils-Klasse strukturiert (ich weiß, dass eine Kategorie wahrscheinlich das beste Design ist) und die Autorelease entfernt, damit sie in einem ARC-Projekt kompiliert wird.

Hier enthalten, falls jemand es nützlich findet.

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

Diese Methode ist nützlich, aber wenn ich ein Tag wie Link <a> nicht
entfernen muss

@wod Ändern Sie dann einfach den regulären Ausdruck in <(?>/?)(?!a).+?>diesen, um alle Tags mit Ausnahme der öffnenden <a> und schließenden </a> Tags zu entfernen.
Ashoor

3

Wenn Sie den Inhalt ohne die HTML-Tags von der Webseite (HTML-Dokument) abrufen möchten, verwenden Sie diesen Code in der UIWebViewDidfinishLoading Delegate- Methode.

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

<br> wird durch nichts ersetzt ... was unerwünscht ist.
Nishant

2

Ich würde mir vorstellen, dass der sicherste Weg wäre, nur nach <> s zu analysieren, nein? Durchlaufen Sie die gesamte Zeichenfolge und kopieren Sie alles, was nicht in <> s enthalten ist, in eine neue Zeichenfolge.


2

Dies ist die Modernisierung der Antwort von m.kocikowski , die Leerzeichen entfernt:

@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

Das Folgende ist die akzeptierte Antwort, aber anstelle der Kategorie ist es eine einfache Hilfsmethode, an die eine Zeichenfolge übergeben wird. (danke 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

Hier ist die schnelle Version:

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
}

Mann, stringByReplacingOccurrencesOfStringdu verwendest außerhalb des Zyklus ist Prozentcodierung und sollte auf korrekte Weise behoben werden.
Vyachaslav Gerchicov

0

Wenn Sie bereit sind, das Three20-Framework zu verwenden , verfügt es über eine Kategorie in NSString, die die Methode stringByRemovingHTMLTags hinzufügt. Siehe NSStringAdditions.h im Three20Core-Unterprojekt.


26
Um Gottes willen, verwenden Sie Three20 für nichts. Das aufgeblähte und schlecht kommentierte Framework aller Zeiten.
Kompozer

0

Erweitern Sie dies mehr aus den Antworten von m.kocikowski und Dan J mit mehr Erklärungen für Neulinge

1 # Zuerst müssen Sie Objective-C-Kategorien erstellen , damit der Code in jeder Klasse verwendet werden kann.

.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 # Importieren Sie dann einfach die .h- Datei der soeben erstellten Kategorieklasse, z

#import "NSString+NAME_OF_CATEGORY.h"

3 # Aufrufen der Methode.

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

Ergebnis ist NSString, von dem ich die Tags entfernen möchte.


0

Ich habe die akzeptierte Antwort von m.kocikowski befolgt und etwas geändert, um einen Autoreleasepool zu verwenden, um alle temporären Zeichenfolgen zu bereinigen, die von stringByReplacingCharactersInRange erstellt werden

In dem Kommentar zu dieser Methode heißt es: / * Ersetzen Sie Zeichen im Bereich durch die angegebene Zeichenfolge und geben Sie eine neue Zeichenfolge zurück. * /

Abhängig von der Länge Ihres XML erstellen Sie möglicherweise einen großen Stapel neuer Autorelease-Zeichenfolgen, die erst am Ende des nächsten @ autoreleasepools bereinigt werden. Wenn Sie sich nicht sicher sind, wann dies passieren kann oder ob eine Benutzeraktion zuvor wiederholt viele Aufrufe dieser Methode auslösen könnte, können Sie dies einfach in einem @autoreleasepool zusammenfassen. Diese können sogar verschachtelt und nach Möglichkeit in Schleifen verwendet werden.

In Apples Referenz zu @autoreleasepool heißt es: "Wenn Sie eine Schleife schreiben, die viele temporäre Objekte erstellt. Sie können einen Autorelease-Poolblock innerhalb der Schleife verwenden, um diese Objekte vor der nächsten Iteration zu entsorgen. Verwenden Sie einen Autorelease-Poolblock in der Schleife hilft, den maximalen Speicherbedarf der Anwendung zu reduzieren. " Ich habe es nicht in der Schleife verwendet, aber zumindest bereinigt diese Methode jetzt nach sich selbst.

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

Ein anderer Weg:

Schnittstelle:

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

Implementierung

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

Realisierung

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

oder einfach

NSString *myClearStr = [self stringByStrippingHTML:rudeStr];


Diese Methode entfernt HTML-Tags. Aber ich möchte HTML-Zeichenfolge analysieren. Was zu tun ist
Krutarth Patel

sparte meine Zeit. Schöne Lösung
Krutarth Patel

0

Eine aktualisierte Antwort für @ m.kocikowski, die auf aktuellen iOS-Versionen funktioniert.

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

}}


-3

In diesem Blogbeitrag werden einige Bibliotheken beschrieben, die zum Entfernen von HTML verfügbar sind. Http://sugarmaplesoftware.com/25/strip-html-tags/ Beachten Sie die Kommentare, in denen andere Lösungen angeboten werden.


Dies ist die genaue Reihe von Kommentaren, auf die ich in meiner Frage als Beispiel für das verwiesen habe, was nicht funktionieren würde.
lfalin
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.