Xóa thẻ HTML khỏi NSString trên iPhone


106

Có một số cách khác nhau để xóa HTML tagskhỏi một NSStringtrong Cocoa.

Một cách là kết xuất chuỗi thành một NSAttributedStringvà sau đó lấy văn bản được kết xuất.

Một cách khác là sử dụng NSXMLDocument's- objectByApplyingXSLTStringmethod để áp dụng một XSLTphép biến đổi thực hiện nó.

Thật không may, iPhone không hỗ trợ NSAttributedStringhoặc NSXMLDocument. Có quá nhiều trường hợp cạnh và HTMLtài liệu không đúng định dạng để tôi cảm thấy thoải mái khi sử dụng regex hoặc NSScanner. Có ai có giải pháp cho điều này không?

Một gợi ý là chỉ cần tìm các ký tự thẻ mở và đóng, phương pháp này sẽ không hoạt động trừ những trường hợp rất nhỏ.

Ví dụ: những trường hợp này (từ chương Perl Cookbook về cùng chủ đề) sẽ phá vỡ phương pháp này:

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

<!-- <A comment> -->

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

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

Bạn có thể thêm một chút logic để tính đến dấu ngoặc kép và dấu nháy đơn ... CDATA sẽ tốn nhiều công hơn một chút, nhưng toàn bộ điểm của HTML là các thẻ không xác định có thể bị trình phân tích cú pháp bỏ qua; nếu bạn coi TẤT CẢ các thẻ là không xác định, thì bạn chỉ nhận được văn bản thô.
Ben Gottlieb

Tôi muốn nhận xét rằng một biểu thức chính quy tốt (nhưng cơ bản) chắc chắn sẽ không phá vỡ các ví dụ của bạn. Chắc chắn là không nếu bạn có thể đảm bảo XHTML được định dạng tốt. Tôi biết rằng bạn đã nói rằng bạn không thể, nhưng tôi tự hỏi tại sao ;-)
Jake

1
câu trả lời tốt cho câu hỏi này. Làm phẳng HTML bằng Objective c
vipintj

Thật không may, sử dụng NSScanner rất chậm.
steipete

Thật không may, ví dụ NSScanner được liên kết chỉ hoạt động đối với html tầm thường. Nó không thành công cho mọi trường hợp thử nghiệm mà tôi đã đề cập trong bài đăng của mình.
lfalin

Câu trả lời:


309

Một giải pháp nhanh chóng và "bẩn" (loại bỏ mọi thứ giữa <và>), hoạt động với 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;
}

Tôi đã khai báo điều này như một thể loại os NSString.


4
@James Để sử dụng phương pháp được đăng trong giải pháp. Bạn phải tạo một danh mục cho NSString. Tra cứu "Danh mục Objective-C" trong Google. Sau đó, bạn thêm phương thức đó trong tệp m và nguyên mẫu trong tệp h. Khi tất cả đã được thiết lập xong, để sử dụng nó, tất cả những gì bạn phải làm là có một đối tượng chuỗi (Ví dụ: NSString * myString = ...) và bạn gọi phương thức đó trên đối tượng chuỗi của mình (NSString * stripeString = [myString stringByStrippingHTML]; ).
Roberto

3
+1 Sử dụng tuyệt vời cho các biểu thức chính quy, nhưng không may là không bao gồm nhiều trường hợp.
matm

3
Nhanh chóng và dơ bẩn thật .... Chức năng này gây ra rò rỉ bộ nhớ rất lớn trong ứng dụng của tôi ... Vâng, để bảo vệ nó, tôi đang sử dụng một lượng lớn dữ liệu ....
EZFrag

5
Trong ứng dụng của tôi, giải pháp này gây ra sự cố về hiệu suất. Tôi đã chuyển sang giải pháp với NSScanner thay vì NSRegularExpressionSearch. Bây giờ vấn đề hiệu suất đã mất hết
carmen_munich

2
Nó là rất rất rất rất bộ nhớ và thời gian. Chỉ sử dụng điều này với một lượng nhỏ html!
ullstrm

29

NSStringDanh mục này sử dụng NSXMLParserđể xóa chính xác bất kỳ HTMLthẻ nào khỏi một NSString. Đây là một tệp duy nhất .m.hcó thể được đưa vào dự án của bạn một cách dễ dàng.

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

Sau đó, bạn tách htmlbằng cách làm như sau:

Nhập tiêu đề:

#import "NSString_stripHtml.h"

Và sau đó gọi dảiHtml:

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

Điều này cũng hoạt động với định dạng không đúng HTMLvề mặt kỹ thuật XML.


3
Trong khi biểu thức chính quy (như được nói bởi m.kocikowski) nhanh và bẩn, điều này mạnh mẽ hơn. Chuỗi ví dụ: @ "Bài kiểm tra của tôi <span font = \" font> name \ "> chuỗi html". Câu trả lời này trả về: Chuỗi html thử nghiệm của tôi. . Thường xuyên trở về biểu: Tên bài kiểm tra của tôi "> chuỗi html Trong khi điều này không phải là phổ biến, nó chỉ là mạnh mẽ hơn.
DonnaLea

1
Ngoại trừ nếu bạn có một chuỗi như "S&P 500", nó sẽ loại bỏ mọi thứ sau dấu và và chỉ trả lại chuỗi "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];

làm việc tốt cho tôi


1
Tôi gặp sự cố mã hóa với giải pháp này
KIDdAe

Có lẽ là giải pháp tốt nhất, nhưng nó là vô ích cho một UILabel :-(
Zeb

9

Bạn có thể sử dụng như bên dưới

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

dùng cái này

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

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

đừng quên bao gồm điều này trong mã của bạn: #import "RegexKitLite.h" đây là liên kết để tải xuống API này: http://regexkit.sourceforge.net/#Downloads


7

Hãy xem NSXMLParser. Đó là một trình phân tích cú pháp kiểu SAX. Bạn sẽ có thể sử dụng nó để phát hiện các thẻ hoặc các phần tử không mong muốn khác trong tài liệu XML và bỏ qua chúng, chỉ chụp văn bản thuần túy.


6

Đây là một giải pháp hiệu quả hơn câu trả lời được chấp nhận:

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

NSStringDanh mục trên sử dụng biểu thức chính quy để tìm tất cả các thẻ phù hợp, tạo bản sao của chuỗi gốc và cuối cùng xóa tất cả các thẻ tại chỗ bằng cách lặp lại chúng theo thứ tự ngược lại. Nó hiệu quả hơn vì:

  • Biểu thức chính quy chỉ được khởi tạo một lần.
  • Một bản sao của chuỗi gốc được sử dụng.

Điều này hoạt động đủ tốt đối với tôi nhưng một giải pháp sử dụng NSScannercó thể hiệu quả hơn.

Giống như câu trả lời được chấp nhận, giải pháp này không giải quyết tất cả các trường hợp biên giới mà @lfalin yêu cầu. Những thứ đó sẽ yêu cầu phân tích cú pháp đắt hơn nhiều mà trường hợp sử dụng trung bình rất có thể không cần.


5

Không có vòng lặp (ít nhất là ở phía chúng tôi):

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

Đây phải là câu trả lời được chấp nhận. Cái hiện tại là lãng phí một cách vô lý.
Adlai Holler

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

Khi chúng ta có dữ liệu meta với các thẻ HTML và muốn áp dụng các thẻ đó, lúc đó chúng ta nên áp dụng đoạn mã trên để đạt được đầu ra mong muốn.
Pavan S Chap

4
#import "RegexKitLite.h"

string text = [html stringByReplacingOccurrencesOfRegex:@"<[^>]+>" withString:@""]

2
HTML không phải là một ngôn ngữ thông thường, vì vậy bạn không nên cố gắng phân tích cú pháp / tách nó bằng một biểu thức chính quy. stackoverflow.com/questions/1732348/…
csaunders

3

Tôi đã mở rộng câu trả lời của m.kocikowski và cố gắng làm cho nó hiệu quả hơn một chút bằng cách sử dụng NSMutableString. Tôi cũng đã cấu trúc nó để sử dụng trong một lớp Utils tĩnh (mặc dù vậy, tôi biết Category có lẽ là thiết kế tốt nhất) và đã loại bỏ autorelease để nó biên dịch trong một dự án ARC.

Bao gồm ở đây trong trường hợp bất kỳ ai thấy nó hữu ích.

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

Phương pháp này rất hữu ích nhưng, nếu tôi cần phải phi dải số thẻ chẳng hạn như liên kết <a> người tôi có thể cập nhật phương pháp này để thực hiện điều này
WOD

@wod sau đó chỉ cần thay đổi regex thành <(?>/?)(?!a).+?>này sẽ xóa tất cả các thẻ ngoại trừ thẻ mở <a> và đóng </a>.
Ashoor

3

Nếu bạn muốn lấy nội dung mà không có thẻ html từ trang web (tài liệu HTML), thì hãy sử dụng mã này bên trong phương thức UIWebViewDidfinishLoading ủy nhiệm .

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

<br> đang được thay thế bởi không có gì ... đó là điều không mong muốn.
Nishant

2

Tôi sẽ tưởng tượng cách an toàn nhất sẽ chỉ là phân tích cú pháp cho <> s, không? Lặp qua toàn bộ chuỗi và sao chép bất kỳ thứ gì không có trong <> s sang một chuỗi mới.


2

Đây là hiện đại hóa câu trả lời m.kocikowski , loại bỏ khoảng trắng:

@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

sau đây là câu trả lời được chấp nhận, nhưng thay vì danh mục, nó là phương thức trợ giúp đơn giản với chuỗi được truyền vào nó. (cảm ơn 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

Đây là phiên bản nhanh:

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
}

Người đàn ông, stringByReplacingOccurrencesOfStringbạn sử dụng bên ngoài chu kỳ là mã hóa phần trăm và cần được sửa chữa thông qua một cách chính xác.
Vyachaslav Gerchicov

0

Nếu bạn sẵn sàng sử dụng khung công tác Three20 , nó có một danh mục trên NSString bổ sung phương thức stringByRemovingHTMLTags. Xem NSStringAdditions.h trong dự án con Three20Core.


26
Vì chúa, đừng sử dụng Three20 cho bất cứ việc gì. Khung bình luận cồng kềnh và tồi tệ nhất từ ​​trước đến nay.
kompozer

0

Mở rộng điều này nhiều hơn từ câu trả lời của m.kocikowski và Dan J với nhiều giải thích hơn cho người mới

1 # Đầu tiên bạn phải tạo mục tiêu-c-category để làm cho mã có thể sử dụng được trong bất kỳ lớp nào.

.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 # Sau đó, chỉ cần nhập tệp .h của lớp danh mục bạn vừa tạo, ví dụ:

#import "NSString+NAME_OF_CATEGORY.h"

3 # Gọi Phương thức.

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

kết quả là NSString Tôi muốn tách các thẻ khỏi đó.


0

Tôi đã làm theo câu trả lời được chấp nhận bởi m.kocikowski và đã sửa đổi một chút để sử dụng công cụ lưu trữ tự động để dọn dẹp tất cả các chuỗi tạm thời được tạo bởi stringByReplacingCharactersInRange

Trong nhận xét cho phương thức này, nó nêu rõ, / * Thay thế các ký tự trong phạm vi bằng chuỗi được chỉ định, trả về chuỗi mới. * /

Vì vậy, tùy thuộc vào độ dài của XML, bạn có thể đang tạo một đống lớn các chuỗi tự động khôi phục mới chưa được dọn dẹp cho đến khi kết thúc @autoreleasepool tiếp theo. Nếu bạn không chắc khi nào điều đó có thể xảy ra hoặc nếu một hành động của người dùng có thể liên tục kích hoạt nhiều lệnh gọi đến phương thức này trước đó thì bạn chỉ cần gói gọn điều này trong @autoreleasepool. Chúng thậm chí có thể được lồng vào nhau và sử dụng trong các vòng lặp nếu có thể.

Tham chiếu của Apple trên @autoreleasepool nêu rõ điều này ... "Nếu bạn viết một vòng lặp tạo ra nhiều đối tượng tạm thời. Bạn có thể sử dụng khối nhóm tự động phát hành bên trong vòng lặp để loại bỏ các đối tượng đó trước lần lặp tiếp theo. Sử dụng khối nhóm tự động phát hành trong vòng lặp giúp giảm dung lượng bộ nhớ tối đa của ứng dụng. " Tôi đã không sử dụng nó trong vòng lặp, nhưng ít nhất phương pháp này tự dọn dẹp ngay bây giờ.

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

Một cách khác:

Giao diện:

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

Thực hiện

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

Hiện thực hóa

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

hoặc đơn giản

NSString *myClearStr = [self stringByStrippingHTML:rudeStr];


phương pháp này là loại bỏ html tags.but tôi muốn phân tích cú pháp html string.what làm
Krutarth Patel

lưu giải pháp time.nice tôi
Krutarth Patel

0

Câu trả lời cập nhật cho @ m.kocikowski hoạt động trên các phiên bản iOS gần đây.

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

}


-3

Đây là một bài đăng trên blog thảo luận về một vài thư viện có sẵn để loại bỏ HTML http://sugarmaplesoftware.com/25/strip-html-tags/ Lưu ý các nhận xét nơi các giải pháp khác được cung cấp.


Đây là tập hợp chính xác các nhận xét mà tôi đã liên kết đến trong câu hỏi của mình như một ví dụ về những gì sẽ không hoạt động.
lfalin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.