ลบแท็ก HTML ออกจาก NSString บน iPhone


106

มีสองวิธีที่แตกต่างกันเพื่อเอาเป็นHTML tagsจากในNSStringCocoa

วิธีหนึ่งคือการแสดงผลสตริงเป็นNSAttributedStringแล้วจับข้อความที่แสดงผล

อีกวิธีหนึ่งคือการใช้NSXMLDocument's- objectByApplyingXSLTStringวิธีใช้การXSLTแปลงที่ทำได้

น่าเสียดายที่ iPhone ไม่รองรับNSAttributedStringหรือNSXMLDocument. มีเคสขอบมากเกินไปและHTMLเอกสารที่มีรูปแบบไม่ถูกต้องเพื่อให้ฉันรู้สึกสบายใจในการใช้ regex หรือNSScanner. ใครมีวิธีแก้ปัญหานี้หรือไม่?

คำแนะนำอย่างหนึ่งคือเพียงแค่มองหาอักขระแท็กเปิดและปิดวิธีนี้จะใช้ไม่ได้ยกเว้นกรณีที่ไม่สำคัญมาก

ตัวอย่างเช่นกรณีเหล่านี้ (จากบท Perl Cookbook ในเรื่องเดียวกัน) จะทำลายวิธีนี้:

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

<!-- <A comment> -->

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

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

คุณสามารถเพิ่มตรรกะเล็กน้อยเพื่อนำเครื่องหมายคำพูดและเครื่องหมายอะพอสทรอฟีมาพิจารณา ... CDATA จะใช้เวลาทำงานมากขึ้นเล็กน้อย แต่ประเด็นทั้งหมดของ HTML คือการที่ตัวแยกวิเคราะห์สามารถเพิกเฉยต่อแท็กที่ไม่รู้จักได้ หากคุณไม่รู้จักแท็กทั้งหมดคุณควรได้รับข้อความดิบ
Ben Gottlieb

ฉันต้องการแสดงความคิดเห็นว่านิพจน์ทั่วไปที่ดี (แต่เป็นพื้นฐาน) จะไม่ทำลายตัวอย่างของคุณอย่างแน่นอน ไม่แน่นอนหากคุณสามารถรับประกัน XHTML ที่มีรูปแบบได้ดี ฉันรู้ว่าคุณบอกว่าคุณทำไม่ได้ แต่ฉันสงสัยว่าทำไม ;-)
เจค

1
มีคำตอบที่ดีสำหรับคำถามนี้ แบน HTML โดยใช้ Objective c
vipintj

น่าเสียดายที่การใช้ NSScanner ช้ามาก
steipete

น่าเสียดายที่ตัวอย่าง NSScanner ที่เชื่อมโยงนั้นใช้ได้กับ html ที่ไม่สำคัญเท่านั้น มันล้มเหลวสำหรับทุกกรณีการทดสอบที่ฉันพูดถึงในโพสต์ของฉัน
lfalin

คำตอบ:


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
@ เจมส์ใช้วิธีที่โพสต์ไว้ในโซลูชัน คุณต้องสร้างหมวดหมู่สำหรับ NSString ค้นหา "หมวด Objective-C" ใน Google จากนั้นคุณเพิ่มวิธีการนั้นในไฟล์ m และต้นแบบในไฟล์ h เมื่อตั้งค่าทั้งหมดแล้วในการใช้สิ่งที่คุณต้องทำคือมีสตริงอ็อบเจ็กต์ (ตัวอย่าง: NSString * myString = ... ) และคุณเรียกใช้เมธอดนั้นบนอ็อบเจ็กต์สตริงของคุณ (NSString * strippedString = [myString stringByStrippingHTML]; ).
Roberto

3
+1 การใช้งานที่ยอดเยี่ยมสำหรับนิพจน์ทั่วไป แต่ไม่ครอบคลุมกรณีจำนวนมาก
matm

3
เร็วและสกปรกแน่นอน .... ฟังก์ชันนี้ทำให้หน่วยความจำรั่วไหลอย่างมากในแอปพลิเคชันของฉัน ... ในการป้องกันฉันใช้ข้อมูลจำนวนมาก ....
EZFrag

5
ในแอพของฉันวิธีแก้ปัญหานี้ทำให้เกิดปัญหาด้านประสิทธิภาพ ฉันเปลี่ยนไปใช้โซลูชันด้วย NSScanner แทน NSRegularExpressionSearch ตอนนี้ปัญหาด้านประสิทธิภาพหมดไป
carmen_munich

2
เป็นหน่วยความจำและใช้เวลานานมาก ใช้สิ่งนี้กับ html ในปริมาณเล็กน้อยเท่านั้น!
ullstrm

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

นี้ยังทำงานร่วมกับที่ไม่ถูกต้องว่าในทางเทคนิคไม่ได้HTMLXML


3
ในขณะที่การแสดงออกปกติ (ตามที่ m.kocikowski กล่าว) นั้นรวดเร็วและสกปรก แต่ก็มีประสิทธิภาพมากกว่า สตริงตัวอย่าง: @ "My test <span font = \" font> name \ "> html string" คำตอบนี้ส่งกลับ: สตริง html ทดสอบของฉัน นิพจน์ทั่วไปส่งคืน: ชื่อการทดสอบของฉัน "> สตริง html แม้ว่านี่จะไม่ใช่เรื่องธรรมดา แต่ก็มีประสิทธิภาพมากขึ้น
DonnaLea

1
ยกเว้นในกรณีที่คุณมีสตริงเช่น "S&P 500" มันจะตัดทุกอย่างที่อยู่หลังเครื่องหมายและและส่งกลับสตริง "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];

ทำงานได้ดีสำหรับฉัน


1
ฉันมีปัญหาในการเข้ารหัสด้วยโซลูชันนี้
KIDdAe

อาจเป็นทางออกที่ดีที่สุด แต่ก็ไม่มีประโยชน์สำหรับ UILabel :-(
Zeb

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

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 แบบคงที่ (ฉันรู้ว่าหมวดหมู่น่าจะเป็นการออกแบบที่ดีที่สุด) และลบการปล่อยอัตโนมัติออกเพื่อรวบรวมในโครงการ 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> ฉันสามารถอัปเดตวิธีนี้เพื่อตอบสนองสิ่งนี้ได้
wod

@wod จากนั้นเปลี่ยนนิพจน์ทั่วไปเพื่อ<(?>/?)(?!a).+?>ลบแท็กทั้งหมดยกเว้นแท็กเปิด <a> และปิด </a>
Ashoor

3

หากคุณต้องการรับเนื้อหาโดยไม่มีแท็ก html จากหน้าเว็บ (เอกสาร HTML) ให้ใช้รหัสนี้ภายในวิธีการUIWebViewDidfinishLoading มอบหมาย

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

<br> กำลังถูกแทนที่โดยไม่มีอะไร ... ซึ่งเป็นสิ่งที่ไม่พึงปรารถนา
Nishant

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

0

หากคุณยินดีที่จะใช้Three20 frameworkก็มีหมวดหมู่บน NSString ที่เพิ่มเมธอด stringByRemovingHTMLTags ดู NSStringAdditions.h ในโครงการย่อย Three20Core


26
เพื่อประโยชน์ของพระเจ้าอย่าใช้ Three20 เพื่ออะไร กรอบความคิดเห็นที่ป่องและไม่ดีที่สุดเท่าที่เคยมีมา
kompozer

0

ขยายความเพิ่มเติมจากคำตอบของ m.kocikowski และ Dan J พร้อมคำอธิบายเพิ่มเติมสำหรับมือใหม่

1 # ก่อนอื่นคุณต้องสร้างวัตถุประสงค์ -c-categoryเพื่อให้โค้ดใช้งานได้ในทุกคลาส

.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 และได้รับการแก้ไขเล็กน้อยเพื่อใช้ประโยชน์จาก autoreleasepool เพื่อล้างสตริงชั่วคราวทั้งหมดที่สร้างโดย 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;
}

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 ต้องทำอย่างไร
Krutarth Patel

ประหยัดเวลาของฉันทางออกที่ดี
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;

}


-3

นี่คือบล็อกโพสต์ที่กล่าวถึงไลบรารีสองแห่งที่พร้อมใช้งานสำหรับการลอก HTML http://sugarmaplesoftware.com/25/strip-html-tags/ โปรดสังเกตความคิดเห็นที่เสนอโซลูชันอื่น ๆ


นี่คือชุดความคิดเห็นที่ฉันเชื่อมโยงกับคำถามของฉันเพื่อเป็นตัวอย่างของสิ่งที่ไม่ได้ผล
lfalin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.