【问题标题】:Remove HTML Tags from an NSString on the iPhone从 iPhone 上的 NSString 中删除 HTML 标签
【发布时间】:2018-06-21 12:36:07
【问题描述】:

有几种不同的方法可以从Cocoa 中的NSString 中删除HTML tags

One way是将字符串渲染成NSAttributedString,然后抓取渲染后的文本。

Another way 是使用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,当然不会。我知道你说你不能,但我想知道为什么;-)
  • 这个问题有很好的答案Flatten HTML using Objective c
  • 不幸的是,使用 NSScanner 太慢了。
  • 更不幸的是,链接的 NSScanner 示例仅适用于琐碎的 html。我在帖子中提到的每个测试用例都失败了。

标签: ios objective-c iphone cocoa-touch nsstring


【解决方案1】:

一种快速且“脏”(删除 之间的所有内容)的解决方案,适用于 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 类别。

【讨论】:

  • @James 使用解决方案中发布的方法。您必须为 NSString 创建一个类别。在 Google 中查找“Objective-C 类别”。然后将该方法添加到 m 文件中,并将原型添加到 h 文件中。当这一切都设置好后,要使用它,你所要做的就是拥有一个字符串对象(例如:NSString *myString = ...),然后在你的字符串对象上调用该方法(NSString *strippedString = [myString stringByStrippingHTML]; )。
  • +1 非常适合正则表达式,但不幸的是没有涵盖很多情况。
  • 确实又快又脏......这个函数导致我的应用程序出现巨大的内存泄漏......好吧,为了保护它,我正在使用大量数据......
  • 在我的应用程序中,此解决方案导致了性能问题。我改用 NSScanner 而不是 NSRegularExpressionSearch 的解决方案。现在性能问题消失了
  • 它非常非常非常非常耗费内存和时间。仅对少量 html 使用此功能!
【解决方案2】:

NSString 类别使用NSXMLParser 准确地从NSString 中删除任何HTML 标记。这是一个单独的 .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

【讨论】:

  • 虽然正则表达式(如 m.kocikowski 所说)又快又脏,但它更健壮。示例字符串:@"我的测试 name\">html 字符串"。这个答案返回:我的测试 html 字符串。正则表达式返回:My test name">html 字符串。虽然这并不常见,但它更健壮。
  • 除非你有一个像“S&P 500”这样的字符串,它会去掉&符号后面的所有内容,只返回字符串“S”。
【解决方案3】:
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];

对我来说很好

【讨论】:

  • 我遇到了这个解决方案的编码问题
  • 可能是最好的解决方案,但它对 UILabel 没用:-(
【解决方案4】:

你可以像下面这样使用

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

【讨论】:

    【解决方案5】:

    使用这个

    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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

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

        - (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 要求的所有边境案例。这些将需要更昂贵的解析,而普通用例很可能不需要。

        【讨论】:

          【解决方案8】:

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

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

          【讨论】:

          • 这应该是公认的答案。现在的那个太浪费了。
          【解决方案9】:
          NSAttributedString *str=[[NSAttributedString alloc] initWithData:[trimmedString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]} documentAttributes:nil error:nil];
          

          【讨论】:

          • 当我们拥有带有 HTML 标签的元数据并想要应用该标签时,我们应该应用上面的代码来实现期望的输出。
          【解决方案10】:
          #import "RegexKitLite.h"
          
          string text = [html stringByReplacingOccurrencesOfRegex:@"<[^>]+>" withString:@""]
          

          【讨论】:

          【解决方案11】:

          我已经扩展了 m.kocikowski 的答案,并尝试通过使用 NSMutableString 使其更有效率。我还构建了它以在静态 Utils 类中使用(我知道 Category 可能是最好的设计),并删除了 autorelease 以便它在 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; 
          }
          

          【讨论】:

          【解决方案12】:

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

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

          【讨论】:


          • 正在被任何东西取代......这是不可取的。
          【解决方案13】:

          我想最安全的方法就是解析 s,不是吗?循环遍历整个字符串,并将未包含在 s 中的任何内容复制到新字符串中。

          【讨论】:

            【解决方案14】:

            这是 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
            

            【讨论】:

              【解决方案15】:

              以下是公认的答案,但不是类别,而是简单的辅助方法,其中传递了字符串。 (谢谢 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;
              }
              

              【讨论】:

                【解决方案16】:

                这是快速版本:

                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 你在循环之外使用的是百分比编码,应该通过正确的方式修复。
                【解决方案17】:

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

                【讨论】:

                • 看在上帝的份上,不要将 Three20 用于任何事情。有史以来最臃肿和最糟糕的评论框架。
                【解决方案18】:

                从 m.kocikowski 和 Dan J 的答案中进一步扩展,为新手提供更多解释

                1# 首先你必须创建objective-c-categories 以使代码在任何类中都可用。

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

                result 是 NSString 我想从中剥离标签。

                【讨论】:

                  【解决方案19】:

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

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

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

                  Apple 对@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;
                  }
                  

                  【讨论】:

                    【解决方案20】:

                    另一种方式:

                    界面:

                    -(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 字符串。怎么办
                    • 节省了我的时间。很好的解决方案
                    【解决方案21】:

                    适用于最新 iOS 版本的 @m.kocikowski 的更新答案。

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

                    }

                    【讨论】:

                      【解决方案22】:

                      这是一篇博文,讨论了几个可用于剥离 HTML 的库 http://sugarmaplesoftware.com/25/strip-html-tags/ 请注意提供其他解决方案的 cmets。

                      【讨论】:

                      • 这是我在问题中链接到的确切 cmets 集,作为不起作用的示例。
                      猜你喜欢
                      • 2014-07-08
                      • 2012-12-29
                      • 2011-09-04
                      • 1970-01-01
                      • 2012-12-06
                      • 2011-05-03
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多