İPhone'daki NSString'den HTML Etiketlerini Kaldırma


106

Kaldırmak için değişik birkaç yol vardır HTML tagsbir mesafedeNSString in Cocoa.

Bir yol , dizeyi birNSAttributedString ve ardından oluşturulan metni almaktır.

Başka bir yol kullanmaktır NSXMLDocument's- objectByApplyingXSLTStringuygulama yöntemiXSLT bunu yapan dönüşümü .

Ne yazık ki, iPhone NSAttributedStringveya desteklemiyor NSXMLDocument. HTMLNormal ifadeler veya normal ifadeleri kullanırken rahat edebileceğim çok fazla uç durum ve hatalı biçimlendirilmiş belge var NSScanner. Kimsenin buna bir çözümü var mı?

Bir öneri, basitçe etiket karakterlerini açıp kapatmak olmuştur, bu yöntem çok önemsiz durumlar dışında çalışmayacaktır.

Örneğin bu durumlar (aynı konudaki Perl Yemek Kitabı bölümünden) bu yöntemi bozacaktır:

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

<!-- <A comment> -->

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

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

Tırnak işaretlerini ve kesme işaretlerini hesaba katmak için biraz mantık ekleyebilirsiniz ... CDATA biraz daha fazla çalışma gerektirir, ancak HTML'nin tüm amacı bilinmeyen etiketlerin ayrıştırıcı tarafından göz ardı edilebilmesidir; TÜM etiketleri bilinmeyen olarak değerlendirirseniz, yalnızca ham metin almanız gerekir.
Ben Gottlieb

İyi (ancak basit) bir düzenli ifadenin örneklerinizde kesinlikle bozulmayacağını söylemek isterim. İyi biçimlendirilmiş XHTML'yi garanti edebiliyorsanız, kesinlikle hayır. Yapamayacağını söylediğini biliyorum ama nedenini merak ediyorum ;-)
Jake

1
Bu sorunun iyi bir cevabı var . Yassılaştırın HTML Amaç c kullanarak
vipintj

Ne yazık ki NSScanner'ı kullanmak çok yavaş.
steipete

Daha da maalesef bağlantılı NSScanner örneği sadece önemsiz html için çalışıyor. Gönderimde bahsettiğim her test vakası için başarısız oluyor.
lfalin

Yanıtlar:


309

Hızlı ve "kirli" (<ile> arasındaki her şeyi kaldırır) çözüm, iOS> = 3.2 ile çalışır:

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

Bunu bir kategori os NSString olarak ilan ettim.


4
@James Çözümde yayınlanan yöntemi kullanmak için. NSString için bir kategori oluşturmalısınız. Google'da "Objective-C Kategorisi" ni arayın. Ardından bu yöntemi m dosyasına ve prototipi h dosyasına eklersiniz. Tüm bunlar ayarlandığında, bunu kullanmak için tek yapmanız gereken bir dizge nesnesine sahip olmaktır (Örnek: NSString * myString = ...) ve bu yöntemi dize nesnenizde çağırırsınız (NSString * strippedString = [myString stringByStrippingHTML]; ).
Roberto

3
+1 Normal ifadeler için harika bir kullanım, ancak maalesef birçok durumu kapsamıyor.
matm

3
Gerçekten hızlı ve kirli .... Bu işlev uygulamamda büyük bir bellek sızıntısına neden oluyor ... Pekala, savunmasında büyük miktarda veri kullanıyorum ....
EZFrag

5
Uygulamamda bu çözüm performans sorunlarına neden oldu. NSRegularExpressionSearch yerine NSScanner ile bir çözüme geçtim. Şimdi performans sorunları ortadan kalktı
carmen_munich

2
Çok çok hafıza ve zaman alıcıdır. Bunu sadece küçük miktarlarda html ile kullanın!
ullstrm

29

Bu NSStringkategori, NSXMLParserherhangi bir HTMLetiketi bir NSString. Bu, tek bir .mve .hkolayca projenize dahil edilebilir dosyası.

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

Daha sonra htmlaşağıdakileri yaparak soyun:

Başlığı içe aktarın:

#import "NSString_stripHtml.h"

Ve sonra stripHtml'yi çağırın:

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

Bu aynı zamanda HTMLteknik olarak yanlış biçimlendirilmiş olarak da çalışır XML.


3
Normal ifade (m.kocikowski'nin söylediği gibi) hızlı ve kirliyken, bu daha sağlamdır. Örnek dize: @ "Testim <span font = \" font> name \ "> html dizesi". Bu yanıt şunu döndürür: Test html dizem. Normal ifade şunu döndürür: Test adım "> html dizesi. Bu o kadar yaygın olmasa da, daha sağlamdır.
DonnaLea

1
"S&P 500" gibi bir dizeniz olması dışında, "ve" işaretinden sonraki her şeyi çıkarır ve yalnızca "S" dizesini döndürür.
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];

benim için iyi çalış


1
Bu çözümle kodlama sorunu
yaşıyorum

Muhtemelen en iyi çözüm, ancak bir UILabel için işe yaramaz :-(
Zeb

9

Aşağıdaki gibi kullanabilirsiniz

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

bunu kullan

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

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

bunu kodunuza eklemeyi unutmayın: #import "RegexKitLite.h" işte bu API'yi indirmek için bağlantı: http://regexkit.sourceforge.net/#Downloads


7

NSXMLParser'a bir göz atın. SAX tarzı bir ayrıştırıcıdır. XML belgesindeki etiketleri veya diğer istenmeyen öğeleri algılamak ve bunları yok saymak için yalnızca saf metni yakalayabilmeniz gerekir.


6

İşte kabul edilen cevaptan daha etkili bir çözüm:

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

Yukarıdaki NSStringkategori, eşleşen tüm etiketleri bulmak için normal bir ifade kullanır, orijinal dizenin bir kopyasını oluşturur ve son olarak tüm etiketleri ters sırada yineleyerek yerlerinde kaldırır. Daha verimli çünkü:

  • Normal ifade yalnızca bir kez başlatılır.
  • Orijinal dizenin tek bir kopyası kullanılır.

Bu benim için yeterince iyi performans gösterdi, ancak kullanan bir çözüm NSScanner daha verimli olabilir.

Kabul edilen yanıt gibi, bu çözüm de @lfalin tarafından talep edilen tüm sınır durumlarını ele almıyor. Bunlar, ortalama kullanım durumunun büyük olasılıkla ihtiyaç duymadığı çok daha pahalı ayrıştırma gerektirecektir.


5

Döngü olmadan (en azından bizim tarafımızda):

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

Kabul edilen cevap bu olmalıdır. Mevcut olan gülünç derecede savurgan.
Adlai Holler

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

HTML etiketleri olan meta verilere sahip olduğumuzda ve bu etiketleri uygulamak istediğimizde, o zaman arzu edilen çıktıyı elde etmek için yukarıdaki kodu uygulamalıyız.
Pavan Bölüm


3

Cevabı m.kocikowski ile genişlettim ve NSMutableString kullanarak biraz daha verimli hale getirmeye çalıştım. Ayrıca onu statik Utils sınıfında kullanılmak üzere yapılandırdım (Kategori muhtemelen en iyi tasarımdır) ve bir ARC projesinde derlenmesi için otomatik sürümü kaldırdım.

Herhangi birinin faydalı bulması durumunda buraya dahil edilmiştir.

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

Bu yöntem kullanışlıdır, ancak bağlantı <a> gibi bazı etiketleri soymam gerekirse, bunu yerine getirmek için bu yöntemi kim güncelleyebilirim
wod

@wod ardından normal ifadeyi değiştirerek, <(?>/?)(?!a).+?>açılış <a> ve kapanış </a> etiketleri hariç tüm etiketleri kaldırır.
Ashoor

3

İçeriği html etiketleri olmadan web sayfasından (HTML belgesi) almak istiyorsanız, bu kodu UIWebViewDidfinishLoading temsilci yöntemi içinde kullanın .

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

<br> hiçbir şeyle değiştirilmiyor ... bu istenmeyen bir durum.
Nishant

2

En güvenli yolun <> s için ayrıştırmak olduğunu düşünürdüm, değil mi? Dizenin tamamı boyunca döngü yapın ve <> s içinde olmayan her şeyi yeni bir dizeye kopyalayın.


2

Bu, beyaz boşlukları ortadan kaldıran m.kocikowski yanıtının modernizasyonudur :

@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

Aşağıdaki kabul edilen cevaptır, ancak kategori yerine, içine dize geçirilen basit yardımcı yöntemdir. (teşekkür ederim 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

İşte hızlı versiyon:

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
}

Dostum, stringByReplacingOccurrencesOfStringdöngü dışında kullanmak yüzde kodlamasıdır ve doğru bir şekilde düzeltilmelidir.
Vyachaslav Gerchicov

0

Three20 çerçevesini kullanmak istiyorsanız , NSString'de stringByRemovingHTMLTags yöntemini ekleyen bir kategoriye sahiptir. Three20Core alt projesinde NSStringAdditions.h'ye bakın.


26
Tanrı aşkına, Three20'yi hiçbir şey için kullanmayın. Şimdiye kadar en çok şişirilmiş ve kötü yorumlanmış çerçeve.
kompozer

0

Bunu m.kocikowski'nin ve Dan J'nin yanıtlarından daha fazla genişletmek ve yeni başlayanlar için daha fazla açıklama yapmak

1 # Kodu herhangi bir sınıfta kullanılabilir hale getirmek için öncelikle hedef-c kategorileri oluşturmanız gerekir.

.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 # Ardından, az önce oluşturduğunuz kategori sınıfının .h dosyasını içe aktarın, örn.

#import "NSString+NAME_OF_CATEGORY.h"

3 # Yöntemi Çağırmak.

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

Sonuç NSString. Etiketleri çıkarmak istiyorum.


0

M.kocikowski tarafından kabul edilen cevabı takip ettim ve değiştirildi stringByReplacingCharactersInRange tarafından oluşturulan tüm geçici dizeleri temizlemek için bir otomatik yayın havuzunu kullanmak için biraz değiştirildi

Bu yöntemin açıklamasında, / * Aralıktaki karakterleri belirtilen dizeyle değiştirerek yeni dizge döndürür. * /

Bu nedenle, XML'inizin uzunluğuna bağlı olarak, bir sonraki @autoreleasepool'un sonuna kadar temizlenmeyen çok sayıda yeni otomatik yayın dizesi oluşturuyor olabilirsiniz. Bunun ne zaman olabileceğinden emin değilseniz veya bir kullanıcı eylemi daha önce bu yönteme yönelik birçok çağrıyı tekrar tekrar tetikleyebiliyorsa, bunu bir @autoreleasepool'da tamamlayabilirsiniz. Bunlar iç içe geçebilir ve mümkün olduğunda döngüler içinde kullanılabilir.

Apple'ın @autoreleasepool ile ilgili referansı şunu belirtir: "Birçok geçici nesne oluşturan bir döngü yazarsanız. Bir sonraki yinelemeden önce bu nesneleri elden çıkarmak için döngü içinde bir otomatik serbest bırakma havuz bloğu kullanabilirsiniz. Döngüde otomatik serbest bırakma havuzu bloğu kullanma uygulamanın maksimum bellek ayak izini azaltmaya yardımcı olur. " Bunu döngüde kullanmadım, ama en azından bu yöntem şimdi kendisinden sonra temizliyor.

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

Başka bir yol:

Arayüz:

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

Uygulama

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

Gerçekleşme

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

veya basit

NSString *myClearStr = [self stringByStrippingHTML:rudeStr];


bu yöntem html etiketlerini kaldırıyor. ancak html dizesini ayrıştırmak istiyorum. ne yapmalı
Krutarth Patel

zamanımı kurtardım. güzel çözüm
Krutarth Patel

0

En son iOS sürümlerinde çalışan @ m.kocikowski için güncellenmiş bir yanıt.

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

}


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.