【问题标题】:Objective C HTML escape/unescapeObjective C HTML 转义/取消转义
【发布时间】:2010-10-14 03:52:58
【问题描述】:

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

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

返回

<span>Foo</span>

希望所有其他 HTML 实体以及 Ӓ 之类的 ASCII 代码也能取消转义。

Cocoa Touch/UIKit 中是否有任何方法可以做到这一点?

【问题讨论】:

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

标签: iphone html objective-c cocoa-touch escaping


【解决方案1】:

查看我的NSString category for XMLEntities。有一些方法可以解码 XML 实体(包括所有 HTML 字符引用)、编码 XML 实体、剥离标签以及从字符串中删除换行符和空格:

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

【讨论】:

  • 好像不支持西里尔字母。你见过支持的吗?
  • 谢谢,顺便说一句,我已经在使用你的解析器了。干得好!
  • 像魅力一样工作。感谢这个伟大的类别!
  • 时髦的许可证怎么了?不能用于日记和日记?
  • 此类别在后台使用 Google 工具箱类别。最好直接通过 Cocoapods 安装 Google Toolbox 助手:pod "GTMNSStringHTMLAdditions"。请参阅 Travis 2015 年 9 月的回答。
【解决方案2】:

来自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

【讨论】:

  • 值得注意的是,如果您正在寻找与此相反的情况,即 '&amp;amp;' 变为 '&amp;amp;',这也包含在 - (NSString *)gtm_stringByEscapingForHTML; 中,稍后在文件中定义。
  • 请提供GTMDefines.h的链接
  • 值得注意的是,此类别与 ARC 不兼容,因为它在结构中使用 Objective-C 对象,这是不受支持的。即使设置 -fno-objc-arc 编译器标志也不会阻止该结构在 Xcode 中被标记为错误。
  • @robotpukeko 这很奇怪,因为我能够编译这个类别的 ARC 项目,只需将标志设置为 .m 文件。
  • 只需将 -fno-objc-arc 添加到编译源。它工作正常。
【解决方案3】:

link 包含以下解决方案。 Cocoa 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

【讨论】:

  • 将其实现为 NSString 类别而不是完全独立的对象不是更容易吗?此外,返回字符串不是自动释放的,但调用者不应该拥有它,因为它不是由调用者显式分配的。
  • xmlParse 也泄漏 btw,只需向其添加一个自动释放并返回Str
  • 如果将其设为 NSString 类别,则仍需要解析器的委托。所以无论如何你都需要一个单独的对象。
  • 即使 CFXMLCreateStringByUnescapingEntities 在 iOS 上不可用,您可以从 CFXMLParser.c(来自 Core Foundation 源代码)复制它的定义并在您的项目中使用它。我已经测试过了,它可以工作。
  • 我发现这段代码删除了所有 html 标签(例如,它只从“Facebook”中留下了“Facebook”),有时在复杂时什么也不返回html 传入。所以,不幸的是它不适合我的目标。
【解决方案4】:

这是我所做的一个令人难以置信的组合解决方案,但如果您想简单地转义字符串而不担心解析,请执行以下操作:

-(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 次。这似乎不是很有效;)
  • 这绝对不是最有效的解决方案,但它确实有效。有什么更有效的方法来做到这一点?
  • 根据使用频率以及通过提高效率可以实际节省多少时间,在这里进行微优化可能没有意义。由于我们在这里处理的是 HTML,因此很可能某处存在网络请求,并且返回所需的时间是上面显示的代码执行时间的数千倍。我可能倾向于不优化这段代码。
  • 建议的方法性能较差,但如果您很少需要处理短字符串,则可以正常工作。感谢您节省了我自己实现这 10 行的时间;)
  • @Andrew 更有效的方法是实现您自己的字符串扫描器,它将所有这些 XML 字符实体引用转换为一次字符串扫描中的相应字符。时间复杂度将下降 5 倍。或者您可以使用 Nikita 下面提出的库 - stackoverflow.com/questions/659602/…
【解决方案5】:

在 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];

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

【讨论】:

  • 我通过编写一个获取字符串、清理它并返回清理后的字符串的方法来做到这一点。看到它here
  • 此解决方案还删除了所有现有的 HTML 标记,例如它从 &lt;b&gt;this&lt;/b&gt; is &lt;a href='test'&gt;test&lt;/a&gt; 留下了 this is test
  • 请注意,NSAttributedString 在构造函数中做了可怕的事情,比如旋转运行循环。我无法在主线程上使用它而不让 UIKit 非常不高兴。
  • 这是拉德。非常感谢,对我来说就像一个魅力。
【解决方案6】:

这是一个中和所有字符的解决方案(通过使它们成为所有 HTML 编码实体的 unicode 值)...根据我的需要使用它(确保来自用户但放置在 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,前导零,所以你的格式应该是 @"&amp;#x%04x;",除此之外,我会添加一个简单的字母数字检测器并且只复制这些字符而不转义。
  • 有趣的是,这段代码在没有分号的情况下对我来说可以正常工作。可能只是 webkit 很健壮。我补充说。但是,不要按照建议执行 %04x,否则您可能会遇到单字节多字节 unicode 字符的问题。使用 %x 打印单字节和多字节的正确数字(如日语)。
【解决方案7】:

对 HTML 或 XML 字符串进行编码和解码的最小侵入性和最轻量级的方法是使用 GTMNSStringHTMLAdditions CocoaPod

它只是 Google Toolbox for Mac 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>

【讨论】:

    【解决方案8】:

    这是一个易于使用的 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
    【解决方案9】:

    上面的 MREntitiesConverter 是一个 HTML 剥离器,而不是编码器。

    如果您需要编码器,请访问:Encode NSString for XML/HTML

    【讨论】:

      【解决方案10】:

      MREntitiesConverter 不适用于转义格式错误的 xml。它会在一个简单的 URL 上失败:

      http://www.google.com/search?client=safari&rls=en&q=fail&ie=UTF-8&oe=UTF-8

      【讨论】:

        【解决方案11】:

        如果您需要生成文字,您可以考虑使用这样的工具:

        http://www.freeformatter.com/java-dotnet-escape.html#ad-output

        为您完成工作。

        另见this answer

        【讨论】:

          【解决方案12】:

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

          这是该类别的头文件:

          #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 编码。
          【解决方案13】:

          为什么不直接使用?

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

          菜鸟问题,但在我的情况下它有效......

          【讨论】:

          • 为什么会这样?据我所知,它只是转换为二进制数据,然后再转换为字符串。我不明白这里会将“>”变成“>”反之亦然。
          【解决方案14】:

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

          实际上,它在许多其他情况下都可以正常工作 答案不是因为 UIWebView 正在做所有的工作。你可以 甚至注入一些javascript(这可能是危险的和/或有用的)。表现应该很糟糕,但实际上并没有那么糟糕。

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

          这是一个使用 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 我想这在性能和资源上都很大 /sarcasm
          猜你喜欢
          • 1970-01-01
          • 2021-07-30
          • 2015-05-16
          • 2012-06-28
          • 1970-01-01
          • 1970-01-01
          • 2019-03-02
          • 1970-01-01
          相关资源
          最近更新 更多