Reputation: 4304
There are a couple of different ways to remove HTML tags
from an NSString
in Cocoa
.
One way is to render the string into an NSAttributedString
and then grab the rendered text.
Another way is to use NSXMLDocument's
-objectByApplyingXSLTString
method to apply an XSLT
transform that does it.
Unfortunately, the iPhone doesn't support NSAttributedString
or NSXMLDocument
. There are too many edge cases and malformed HTML
documents for me to feel comfortable using regex or NSScanner
. Does anyone have a solution to this?
One suggestion has been to simply look for opening and closing tag characters, this method won't work except for very trivial cases.
For example these cases (from the Perl Cookbook chapter on the same subject) would break this method:
<IMG SRC = "foo.gif" ALT = "A > B">
<!-- <A comment> -->
<script>if (a<b && a>c)</script>
<![INCLUDE CDATA [ >>>>>>>>>>>> ]]>
Upvotes: 108
Views: 103832
Reputation: 350
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];
work fine for me
Upvotes: 11
Reputation: 1815
An updated answer for @m.kocikowski that works on recent iOS versions.
-(NSString *) stringByStrippingHTMLFromString:(NSString *)str {
NSRange range;
while ((range = [str rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
str = [str stringByReplacingCharactersInRange:range withString:@""];
return str;
}
Upvotes: 0
Reputation: 109
use this
NSString *myregex = @"<[^>]*>"; //regex to remove any html tag
NSString *htmlString = @"<html>bla bla</html>";
NSString *stringWithoutHTML = [hstmString stringByReplacingOccurrencesOfRegex:myregex withString:@""];
don't forget to include this in your code : #import "RegexKitLite.h" here is the link to download this API : http://regexkit.sourceforge.net/#Downloads
Upvotes: 8
Reputation: 1976
This NSString
category uses the NSXMLParser
to accurately remove any HTML
tags from an NSString
. This is a single .m
and .h
file that can be included into your project easily.
https://gist.github.com/leighmcculloch/1202238
You then strip html
by doing the following:
Import the header:
#import "NSString_stripHtml.h"
And then call stripHtml:
NSString* mystring = @"<b>Hello</b> World!!";
NSString* stripped = [mystring stripHtml];
// stripped will be = Hello World!!
This also works with malformed HTML
that technically isn't XML
.
Upvotes: 30
Reputation: 13698
Another one way:
Interface:
-(NSString *) stringByStrippingHTML:(NSString*)inputString;
Implementation
(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;
}
Realization
cell.exampleClass.text = [self stringByStrippingHTML:[exampleJSONParsingArray valueForKey: @"key"]];
or simple
NSString *myClearStr = [self stringByStrippingHTML:rudeStr];
Upvotes: 0
Reputation: 3581
Here's the swift version :
func stripHTMLFromString(string: String) -> String {
var copy = string
while let range = copy.rangeOfString("<[^>]+>", options: .RegularExpressionSearch) {
copy = copy.stringByReplacingCharactersInRange(range, withString: "")
}
copy = copy.stringByReplacingOccurrencesOfString(" ", withString: " ")
copy = copy.stringByReplacingOccurrencesOfString("&", withString: "&")
return copy
}
Upvotes: 2
Reputation: 4030
I have following the accepted answer by m.kocikowski and modified is slightly to make use of an autoreleasepool to cleanup all of the temporary strings that are created by stringByReplacingCharactersInRange
In the comment for this method it states, /* Replace characters in range with the specified string, returning new string. */
So, depending on the length of your XML you may be creating a huge pile of new autorelease strings which are not cleaned up until the end of the next @autoreleasepool. If you are unsure when that may happen or if a user action could repeatedly trigger many calls to this method before then you can just wrap this up in an @autoreleasepool. These can even be nested and used within loops where possible.
Apple's reference on @autoreleasepool states this... "If you write a loop that creates many temporary objects. You may use an autorelease pool block inside the loop to dispose of those objects before the next iteration. Using an autorelease pool block in the loop helps to reduce the maximum memory footprint of the application." I have not used it in the loop, but at least this method cleans up after itself now.
- (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;
}
Upvotes: 0
Reputation: 61
NSAttributedString *str=[[NSAttributedString alloc] initWithData:[trimmedString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]} documentAttributes:nil error:nil];
Upvotes: 5
Reputation: 1530
following is the accepted answer, but instead of category, it is simple helper method with string passed into it. (thank you 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;
}
Upvotes: 1
Reputation: 1101
Without a loop (at least on our side) :
- (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:@""];
}
Upvotes: 5
Reputation: 120324
Here's a more efficient solution than the accepted answer:
- (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;
}
The above NSString
category uses a regular expression to find all the matching tags, makes a copy of the original string and finally removes all the tags in place by iterating over them in reverse order. It's more efficient because:
This performed well enough for me but a solution using NSScanner
might be more efficient.
Like the accepted answer, this solution doesn't address all the border cases requested by @lfalin. Those would be require much more expensive parsing which the average use case most likely doesn't need.
Upvotes: 6
Reputation: 4204
You can use like below
-(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;
}
Upvotes: 9
Reputation: 752
This is the modernization of m.kocikowski answer which removes whitespaces:
@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
Upvotes: 2
Reputation: 1368
Extending this more from m.kocikowski's and Dan J's answers with more explanation for newbies
1# First you have to create objective-c-categories to make the code useable in any class.
.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# Then just import the .h file of the category class you've just created e.g.
#import "NSString+NAME_OF_CATEGORY.h"
3# Calling the Method.
NSString* sub = [result stringByStrippingHTML];
NSLog(@"%@", sub);
result is NSString I want to strip the tags from.
Upvotes: 0
Reputation: 5482
A quick and "dirty" (removes everything between < and >) solution, works with 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;
}
I have this declared as a category os NSString.
Upvotes: 313
Reputation: 16317
If you want to get the content without the html tags from the web page (HTML document) , then use this code inside the UIWebViewDidfinishLoading
delegate method.
NSString *myText = [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.textContent"];
Upvotes: 3
Reputation: 25673
I've extended the answer by m.kocikowski and tried to make it a bit more efficient by using an NSMutableString. I've also structured it for use in a static Utils class (I know a Category is probably the best design though), and removed the autorelease so it compiles in an ARC project.
Included here in case anybody finds it useful.
.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;
}
Upvotes: 3
Reputation: 372
#import "RegexKitLite.h"
string text = [html stringByReplacingOccurrencesOfRegex:@"<[^>]+>" withString:@""]
Upvotes: 4
Reputation: 4339
If you are willing to use Three20 framework, it has a category on NSString that adds stringByRemovingHTMLTags method. See NSStringAdditions.h in Three20Core subproject.
Upvotes: 0
Reputation: 600
Here's a blog post that discusses a couple of libraries available for stripping HTML http://sugarmaplesoftware.com/25/strip-html-tags/ Note the comments where others solutions are offered.
Upvotes: -2
Reputation: 4451
Take a look at NSXMLParser. It's a SAX-style parser. You should be able to use it to detect tags or other unwanted elements in the XML document and ignore them, capturing only pure text.
Upvotes: 7
Reputation: 85522
I would imagine the safest way would just be to parse for <>s, no? Loop through the entire string, and copy anything not enclosed in <>s to a new string.
Upvotes: 2