Objective C HTML转义/转义


74

想知道是否有一种简单的方法可以在Objective C中执行简单的HTML转义/转义。我想要的是这样的伪代码:

NSString *string = @"<span>Foo</span>";
[string stringByUnescapingHTML];

哪个返回

<span>Foo</span>

希望也转义所有其他HTML实体,甚至ASCII码(例如Ӓ等)。

Cocoa Touch / UIKit中有什么方法可以做到这一点?


iOS7现在可能最简单的方法是使用NSAttributedString的功能来解码HTML,然后将NSAttributedString转换为NSString-请参阅下面的答案。
orj 2014年

Answers:


30

链接包含以下解决方案。可可CF具有CFXMLCreateStringByUnescapingEntities函数,但在iPhone上不可用。

@interface MREntitiesConverter : NSObject <NSXMLParserDelegate>{
    NSMutableString* resultString;
}

@property (nonatomic, retain) NSMutableString* resultString;

- (NSString*)convertEntitiesInString:(NSString*)s;

@end


@implementation MREntitiesConverter

@synthesize resultString;

- (id)init
{
    if([super init]) {
        resultString = [[NSMutableString alloc] init];
    }
    return self;
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)s {
        [self.resultString appendString:s];
}

- (NSString*)convertEntitiesInString:(NSString*)s {
    if (!s) {
        NSLog(@"ERROR : Parameter string is nil");
    }
    NSString* xmlStr = [NSString stringWithFormat:@"<d>%@</d>", s];
    NSData *data = [xmlStr dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
    NSXMLParser* xmlParse = [[[NSXMLParser alloc] initWithData:data] autorelease];
    [xmlParse setDelegate:self];
    [xmlParse parse];
    return [NSString stringWithFormat:@"%@",resultString];
}

- (void)dealloc {
    [resultString release];
    [super dealloc];
}

@end

1
将其实现为NSString类别而不是完全独立的对象会更容易吗?同样,返回字符串不会自动释放,但调用者不应该拥有它,因为调用者未明确分配它。
dreamlax

6
xmlParse还会泄漏btw,只需向其中添加自动释放并返回str
Udom

1
如果将其设为NSString类别,则仍需要解析器的委托。因此,无论如何您将需要一个单独的对象。
William Jockusch'5

4
即使CFXMLCreateStringByUnescapingEntities在iOS上不可用,您也可以从CFXMLParser.c(从Core Foundation源代码)复制其定义,然后在项目中使用它。我已经对其进行了测试,并且可以正常工作。
Chaitanya Gupta

2
我发现这段代码删除了所有html标记(例如,它从“ <a href="xxx"> Facebook </a>”中仅保留了“ Facebook”),有时在传入复杂的html时什么也没有返回。因此,不幸的是,不能达到我的目标。
Mike Keskinov 2014年

91

查看我的NSString类别中的XMLEntities。有以下方法可以解码XML实体(包括所有HTML字符引用),对XML实体进行编码,剥离标签并从字符串中删除换行符和空格:

- (NSString *)stringByStrippingTags;
- (NSString *)stringByDecodingXMLEntities; // Including all HTML character references
- (NSString *)stringByEncodingXMLEntities;
- (NSString *)stringWithNewLinesAsBRs;
- (NSString *)stringByRemovingNewLinesAndWhitespace;

2
似乎它不支持西里尔字母。您看到支持吗?
slatvick 2010年

谢谢,我已经在使用您的解析了。做得好!
Abramodj 2012年

像魅力。感谢这个伟大的类别!
DevZarak

9
时髦的许可证怎么了?不能用于日记和日记吗?
原子

1
此类别使用的是内部的Google Toolbox类别。最好直接通过Cocoapods安装Google工具箱帮助器:pod "GTMNSStringHTMLAdditions"。请参阅2015
以来

35

Google Toolbox for Mac的另一个HTML NSString类别
尽管名称,但它也适用于iOS。

http://google-toolbox-for-mac.googlecode.com/svn/trunk/Foundation/GTMNSString+HTML.h

/// Get a string where internal characters that are escaped for HTML are unescaped 
//
///  For example, '&amp;' becomes '&'
///  Handles &#32; and &#x32; cases as well
///
//  Returns:
//    Autoreleased NSString
//
- (NSString *)gtm_stringByUnescapingFromHTML;

而且我只需要在项目中包括三个文件:标头,实现和GTMDefines.h


2
值得一提的是,如果您要寻找与之相反的东西,即'&'变为'&amp;',则该内容也将在- (NSString *)gtm_stringByEscapingForHTML;文件的稍后定义中介绍。
克里斯蒂安

请您提供以下链接GTMDefines.h
吗?

值得一提的是,该类别与ARC不兼容,因为它在结构中使用Objective-C对象,而该对象不受支持。即使设置-fno-objc-arc编译器标志也不会停止将该结构标记为Xcode中的错误。
robotpukeko

@robotpukeko这很奇怪,因为我能够仅通过将标志设置为.m文件来编译具有此类的ARC项目。
Timur Kuchkarov 2013年

只需将-fno-objc-arc添加到编译源即可。而且效果很好。
yong ho

29

这是我做过的一个骇人听闻的解决方案,但是如果您只想转义一个字符串而不必担心解析,请执行以下操作:

-(NSString *)htmlEntityDecode:(NSString *)string
    {
        string = [string stringByReplacingOccurrencesOfString:@"&quot;" withString:@"\""];
        string = [string stringByReplacingOccurrencesOfString:@"&apos;" withString:@"'"];
        string = [string stringByReplacingOccurrencesOfString:@"&lt;" withString:@"<"];
        string = [string stringByReplacingOccurrencesOfString:@"&gt;" withString:@">"];
        string = [string stringByReplacingOccurrencesOfString:@"&amp;" withString:@"&"]; // Do this last so that, e.g. @"&amp;lt;" goes to @"&lt;" not @"<"

        return string;
    }

我知道这绝非优雅,但可以完成工作。然后,您可以通过调用以下内容来解码元素:

string = [self htmlEntityDecode:string];

就像我说的那样,它很笨拙,但是可以用。如果要编码字符串,只需反转stringByReplacingOccurencesOfString参数。


5
而性能呢?您要遍历字符串5次。似乎效率不是很高;)
HyLian

绝对不是最有效的解决方案,但它确实有效。有什么更有效的方法可以做到这一点?
Andrew Kozlik 2010年

6
取决于使用它的频率以及通过使其更有效可以实际节省多少时间,在此处进行微优化可能没有意义。因为我们在这里处理HTML,所以很可能在某个地方有网络请求,返回的时间将比上面显示的代码执行时间长数千倍。我可能倾向于不优化此代码。
乔什·布朗

建议的方法性能较差,但是如果您很少需要处理短字符串,则可以正常工作。感谢您节省时间自行完成这10条线;)
Kostiantyn Sokolinskyi

@Andrew更为有效的方法是实现您自己的字符串扫描器,该扫描器将在一次字符串扫描中将所有这些XML字符实体引用转换为相应的字符。时间复杂度将降低5倍。或者你也可以使用像尼基塔下面提出的一个图书馆- stackoverflow.com/questions/659602/...
Kostiantyn Sokolinskyi

11

在iOS 7中,您可以使用NSAttributedString的导入HTML的功能将HTML实体转换为NSString。

例如:

@interface NSAttributedString (HTML)
+ (instancetype)attributedStringWithHTMLString:(NSString *)htmlString;
@end

@implementation NSAttributedString (HTML)
+ (instancetype)attributedStringWithHTMLString:(NSString *)htmlString
{
    NSDictionary *options = @{ NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType,
                               NSCharacterEncodingDocumentAttribute :@(NSUTF8StringEncoding) };

    NSData *data = [htmlString dataUsingEncoding:NSUTF8StringEncoding];

    return [[NSAttributedString alloc] initWithData:data options:options documentAttributes:nil error:nil];
}

@end

然后在您要清理实体的代码中:

NSString *cleanString = [[NSAttributedString attributedStringWithHTMLString:question.title] string];

这可能是最简单的方法,但是我不知道它的性能如何。您应该非常确定“清洗”的内容中不包含任何<img>标签或类似内容,因为此方法将在HTML到NSAttributedString转换期间下载这些图像。:)


为此,我编写了一种方法,该方法接收字符串,将其清理,然后将清理后的字符串返回。在这里看到它。
亚当·辛普森

此解决方案还删除了所有现有的HTML标记,例如,它this is test从离开<b>this</b> is <a href='test'>test</a>
Mike Keskinov 2014年

2
请注意,NSAttributedString在构造函数中会执行可怕的操作,例如旋转runloop。我无法在主线程上使用它,而不会让UIKit非常不愉快。
布莱恩·金

这是拉德。非常感谢,对我来说就像是一种魅力。
蒂姆·约翰森

5

这是一个中和所有字符的解决方案(通过使它们成为所有Unicode值的HTML编码实体)...用于我的需要(确保来自用户但放置在webview内的字符串不能有任何字符) XSS攻击):

接口:

@interface NSString (escape)
- (NSString*)stringByEncodingHTMLEntities;
@end

实现方式:

@implementation NSString (escape)

- (NSString*)stringByEncodingHTMLEntities {
    // Rather then mapping each individual entity and checking if it needs to be replaced, we simply replace every character with the hex entity

    NSMutableString *resultString = [NSMutableString string];
    for(int pos = 0; pos<[self length]; pos++)
        [resultString appendFormat:@"&#x%x;",[self characterAtIndex:pos]];
    return [NSString stringWithString:resultString];
}

@end

用法示例:

UIWebView *webView = [[UIWebView alloc] init];
NSString *userInput = @"<script>alert('This is an XSS ATTACK!');</script>";
NSString *safeInput = [userInput stringByEncodingHTMLEntities];
[webView loadHTMLString:safeInput baseURL:nil];

您的里程会有所不同。


您缺少“;” 同样,在转义序列的末尾,在所有文档中,我发现一个Unicode数字的长度为4,且前导零。因此,您的格式应为@"&#x%04x;",除此之外,我还要添加一个简单的字母数字检测器并复制此类字符无需转义。
Moshe Gottlieb

有趣的是,此代码对我来说不需要分号就可以正常工作。也许只是Webkit是健壮的。我加了。但是,请不要按照建议的方式执行%04x,否则您可能会遇到单字节多字节unicode字符的麻烦。使用%x可以为单字节和多字节(如日文)打印正确的数字。
BadPirate

4

编码和解码HTML或XML字符串的侵入性最小,最轻便的方法是使用GTMNSStringHTMLAdditions CocoaPod

它只是适用于Mac的Google Toolbox NSString类别GTMNSString+HTML,去除了对的依赖GTMDefines.h。因此,您只需添加一个.h和一个.m,您就可以开始了。

例:

#import "GTMNSString+HTML.h"

// Encoding a string with XML / HTML elements
NSString *stringToEncode = @"<TheBeat>Goes On</TheBeat>";
NSString *encodedString = [stringToEncode gtm_stringByEscapingForHTML];

// encodedString looks like this now:
// &lt;TheBeat&gt;Goes On&lt;/TheBeat&gt;

// Decoding a string with XML / HTML encoded elements
NSString *stringToDecode = @"&lt;TheBeat&gt;Goes On&lt;/TheBeat&gt;";
NSString *decodedString = [stringToDecode gtm_stringByUnescapingFromHTML];

// decodedString looks like this now:
// <TheBeat>Goes On</TheBeat>

2

这是一个易于使用的NSString类别实现:

它远未完成,但是您可以从此处添加一些缺少的实体:http : //code.google.com/p/statz/source/browse/trunk/NSString%2BHTML.m

用法:

#import "NSString+HTML.h"

NSString *raw = [NSString stringWithFormat:@"<div></div>"];
NSString *escaped = [raw htmlEscapedString];

我可以确认此类别的效果很好。它写得很完美。我敦促所有人使用它-我怀疑那里是否有更好的解决方案!再次令人惊奇的是,它还没有内置到iOS .. bizarro中。由于@blago
Fattie




0

最简单的解决方案是创建如下类别:

这是类别的头文件:

#import <Foundation/Foundation.h>
@interface NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding;
@end

这是实现:

#import "NSString+URLEncoding.h"
@implementation NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
    return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
               (CFStringRef)self,
               NULL,
               (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
               CFStringConvertNSStringEncodingToEncoding(encoding));
}
@end

现在我们可以简单地做到这一点:

NSString *raw = @"hell & brimstone + earthly/delight";
NSString *url = [NSString stringWithFormat:@"http://example.com/example?param=%@",
            [raw urlEncodeUsingEncoding:NSUTF8Encoding]];
NSLog(url);

此答案的学分转到以下网站:

http://madebymany.com/blog/url-encoding-an-nsstring-on-ios

这是URL编码,问题是关于HTML转义而不是URL编码。
蒂姆·约翰森

-4

为什么不只是使用?

NSData *data = [s dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSString *result = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
return result;

菜鸟问题,但就我而言,它可行...


1
为什么会这样?据我所知,它只是转换为二进制数据,然后再转换为字符串。我不明白这里会把“>”变成“&gt;” 反之亦然。
亚历克斯·韦恩

-5

这是我几年前发布的旧答案。我的意图不是提供“好的”和“可敬的”解决方案,而是提供在某些情况下可能有用的“ hacky”解决方案。请不要使用此解决方案,除非其他方法无效。

实际上,它在很多情况下都可以很好地工作,而其他答案却没有,因为UIWebView可以完成所有工作。您甚至可以注入一些javascript(可能是危险的和/或有用的)。性能应该很糟糕,但实际上并不算差。

必须提及另一种解决方案。只需创建一个UIWebView,加载编码后的字符串并取回文本即可。它转义了标签“ <>”,并且还解码了所有html实体(例如“&gt;”),并且可能在其他人不在的地方工作(例如,使用西里尔字母)。我认为这不是最好的解决方案,但是如果上述解决方案不起作用,它可能会很有用。

这是一个使用ARC的小例子:

@interface YourClass() <UIWebViewDelegate>

    @property UIWebView *webView;

@end

@implementation YourClass 

- (void)someMethodWhereYouGetTheHtmlString:(NSString *)htmlString {
    self.webView = [[UIWebView alloc] init];
    NSString *htmlString = [NSString stringWithFormat:@"<html><body>%@</body></html>", self.description];
    [self.webView loadHTMLString:htmlString baseURL:nil];
    self.webView.delegate = self;
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    self.webView = nil;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    self.webView = nil;
    NSString *escapedString = [self.webView stringByEvaluatingJavaScriptFromString:@"document.body.textContent;"];
}

- (void)webViewDidStartLoad:(UIWebView *)webView {
    // Do Nothing
}

@end

讽刺我想这在性能和资源上都很大/
sarcasm
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.