从iPhone上的NSString删除HTML标签


106

有几个不同的方法来去除HTML tagsNSStringCocoa

一种方法是将字符串呈现为NSAttributedString,然后获取呈现的文本。

另一种方法是使用NSXMLDocument's- objectByApplyingXSLTString方法来应用执行XSLT此操作的转换。

不幸的是,iPhone不支持NSAttributedStringNSXMLDocument。有太多的边缘情况和畸形HTML,我觉得使用正则表达式或舒适的文档NSScanner。有人对此有解决方案吗?

一种建议是简单地寻找打开和关闭标签字符,这种方法除了非常琐碎的情况以外,是行不通的。

例如,这些情况(来自同一主题的Perl Cookbook章节)将破坏此方法:

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

<!-- <A comment> -->

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

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

您可以添加一些逻辑以考虑引号和撇号... CDATA会花费更多的工作,但是HTML的全部要点是解析器可以忽略未知的标记。如果您将所有标签视为未知标签,那么您应该只获取原始文本。
本·戈特利布

我想评论一下,一个好的(但很基本的)正则表达式肯定不会破坏您的示例。如果不能保证格式良好的XHTML,当然不能。我知道您说不能,但是我不知道为什么;-)
杰克

1
这个问题有一个很好的答案使用Objective c

不幸的是,使用NSScanner实在太慢了。
steipete 2011年

更不幸的是,链接的NSScanner示例仅适用于琐碎的html。我在帖子中提到的每个测试用例都失败了。
lfalin 2013年

Answers:


309

快速而“肮脏的”(删除<和>之间的所有内容)解决方案,可与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;
}

我已将此声明为os NSString类别。


4
@James使用解决方案中发布的方法。您必须为NSString创建一个类别。在Google中查找“ Objective-C类别”。然后,在m文件中添加该方法,在h文件中添加原型。完成所有设置后,要使用它,您要做的就是拥有一个字符串对象(例如:NSString * myString = ...),然后在您的字符串对象上调用该方法(NSString * strippedString = [myString stringByStrippingHTML]; )。
罗伯托

3
+1非常适合用于正则表达式,但不幸的是它不能涵盖很多情况。
matm 2012年

3
快速和肮脏确实....这个功能使我的应用程序巨大的内存泄漏......那么,在它的防御,我使用大量的数据....
EZFrag

5
在我的应用程序中,此解决方案导致性能问题。我改用NSScanner而不是NSRegularExpressionSearch解决方案。现在性能问题已经解决了
carmen_munich

2
这是非常非常非常消耗内存和时间的。仅使用少量的html!
ullstrm 2014年

29

NSString类别使用NSXMLParser来从中准确删除所有HTML标签NSString。这是一个.m.h可以很容易地纳入您的项目文件。

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

然后,html通过执行以下操作进行剥离:

导入标题:

#import "NSString_stripHtml.h"

然后调用stripHtml:

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

这也适用于格式HTML上不允许的格式错误的格式XML


3
尽管正则表达式(如m.kocikowski所说)既快速又肮脏,但是却更加健壮。字符串示例:@“我的测试<span font = \” font> name \“> html字符串”。该答案返回:我的测试html字符串。正则表达式返回:我的测试名称>> html字符串。虽然这种
用法

1
除非您有“ S&P 500”之类的字符串,否则它将在“&”号之后删除所有内容,并仅返回字符串“ S”。
约书亚·格罗斯

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

对我来说很好


1
我对此解决方案有编码问题
KIDdAe 2014年

可能是最好的解决方案,但是对于UILabel来说是没有用的:-(
Zeb 2015年

9

您可以像下面这样使用

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

用这个

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

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

不要忘记在您的代码中包含此代码:#import“ RegexKitLite.h”这是下载此API的链接:http ://regexkit.sourceforge.net/#Downloads


7

看一下NSXMLParser。这是一种SAX样式的解析器。您应该能够使用它来检测XML文档中的标签或其他不需要的元素,并忽略它们,仅捕获纯文本。


6

这是比公认的答案更有效的解决方案:

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

上面的NSString类别使用正则表达式查找所有匹配的标签,复制原始字符串,最后通过以相反的顺序遍历它们来最终删除所有标签。效率更高是因为:

  • 正则表达式仅初始化一次。
  • 使用原始字符串的单个副本。

这对我来说足够好,但是使用解决方案NSScanner可能会更有效。

像已接受的答案一样,此解决方案无法解决@lfalin请求的所有边界案件。这些将需要昂贵得多的解析,而一般用例最有可能不需要解析。


5

没有循环(至少在我们这边):

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

这应该是公认的答案。当前的那是荒谬的浪费。
Adlai Holler 2015年

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

当我们拥有带有HTML标签的元数据并且想要应用该标签时,那时候我们应该应用上面的代码来实现期望的输出。
Pavan Sisode


3

我已经通过m.kocikowski扩展了答案,并尝试通过使用NSMutableString使其更加有效。我还对它进行了结构化,以供在静态Utils类中使用(尽管我知道Category可能是最好的设计),并删除了自动发行版,以便可以在ARC项目中进行编译。

包括在此处,以防任何人发现它有用。

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

此方法很有用,但是,如果我需要取消剥离某些标签(例如链接<a>),我可以更新此方法来实现此目的
2013年

@wod然后只需将正则表达式更改为该值即可<(?>/?)(?!a).+?>删除除开始<a>和结束</a>标记之外的所有标记。
Ashoor

3

如果要从网页(HTML文档)获取不带html标记的内容,请在UIWebViewDidfinishLoading 委托方法中使用此代码。

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

<br>一无所获...这是不可取的。
Nishant 2013年

2

我想最安全的方法就是解析<> s,不是吗?遍历整个字符串,然后将未包含在<>中的所有内容复制到新字符串中。


2

这是m.kocikowski答案的现代化,它消除了空白:

@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

以下是可接受的答案,但不是类别,而是一种简单的辅助方法,其中已将字符串传递给该方法。(谢谢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

这是快速版本:

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
}

伙计,您stringByReplacingOccurrencesOfString在循环之外使用的是百分比编码,应通过正确的方式进行修复。
Vyachaslav Gerchicov's

0

如果您愿意使用Three20框架,则它在NSString上具有一个类别,该类别添加了stringByRemovingHTMLTags方法。请参阅Three20Core子项目中的NSStringAdditions.h。


26
看在上帝的份上,不要将Three20用于任何用途。有史以来最肿,评论最差的框架。
kompozer 2012年

0

从m.kocikowski和Dan J的答案中扩展更多内容,并为新手提供更多解释

1#首先,您必须创建Objective-C类,以使代码可在任何类中使用。

。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#然后,只需导入刚创建的类别类的.h文件,例如

#import "NSString+NAME_OF_CATEGORY.h"

3#调用方法。

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

结果是NSString我想从中剥离标签。


0

我遵循了m.kocikowski接受的答案,并稍作修改以利用自动释放池来清理stringByReplacingCharactersInRange创建的所有临时字符串

在此方法的注释中指出:/ *用指定的字符串替换范围内的字符,并返回新的字符串。* /

因此,根据您XML的长度,您可能会创建大量新的自动释放字符串,这些字符串直到下一个@autoreleasepool结束时才会清除。如果不确定何时会发生这种情况,或者不确定用户操作是否可以重复触发此方法的多次调用,则可以将其包装在@autoreleasepool中。这些甚至可以嵌套,并在可能的情况下在循环中使用。

苹果在@autoreleasepool上的引用指出了这一点……“如果编写了一个创建许多临时对象的循环,则可以在循环内使用一个自动释放池块在下一次迭代之前处理这些对象。在循环中使用一个自动释放池块有助于减少应用程序的最大内存占用。” 我没有在循环中使用它,但至少此方法现在可以自行清除。

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

另一种方式:

接口:

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

实作

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

实现

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

或简单

NSString *myClearStr = [self stringByStrippingHTML:rudeStr];


此方法正在删除html标签。但是我想解析html string.what该怎么做
Krutarth Patel 16/12/28

节省了我的time.nice解决方案
Krutarth Patel

0

@ m.kocikowski的更新的答案,可在最新的iOS版本上使用。

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

}


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.