【问题标题】:iOS HTML Unicode to NSString?iOS HTML Unicode 到 NSString?
【发布时间】:2011-11-27 23:55:42
【问题描述】:

我正在将 Android 应用程序移植到 iOS,但遇到了一个小障碍。我正在从网页中提取 HTML 编码数据,但其中一些数据以 Unicode 格式显示以显示外来字符...因此俄语 (Лети за мной) 中的字符将被解析为 "Лет..."

在 android 中,我可以通过调用 HTML.fromHTML() 来解决这个问题。 iOS 有类似的吗?

【问题讨论】:

  • 这里有什么问题?如今,UTF-8 非常普遍。您没有提供有关您用于 HTML 解析的任何详细信息,或者您的问题是什么。
  • 啊,你更新了。我认为您的意思是数据是用 HTML 实体编码的,但实际上不包含 HTML 标签?

标签: html ios unicode


【解决方案1】:

编写自己的 HTML 实体解码器非常容易。只需扫描字符串以查找 &,阅读以下 ;,然后解释结果。如果是“amp”、“lt”、“gt”或“quot”,请将其替换为相关字符。如果它以# 开头,它是一个数字实体。如果 # 后跟“x”,则将其余部分视为十六进制,否则视为十进制。读取数字,然后将字符插入字符串(如果您正在写入NSMutableString,则可以使用[str appendFormat:@"%C", thechar]NSScanner 可以使字符串扫描变得非常容易,特别是因为它已经知道如何读取十六进制数字。

我刚刚创建了一个应该为您执行此操作的函数。请注意,我还没有实际测试过这个,所以你应该按照它的速度运行它:

- (NSString *)stringByDecodingHTMLEntitiesInString:(NSString *)input {
    NSMutableString *results = [NSMutableString string];
    NSScanner *scanner = [NSScanner scannerWithString:input];
    [scanner setCharactersToBeSkipped:nil];
    while (![scanner isAtEnd]) {
        NSString *temp;
        if ([scanner scanUpToString:@"&" intoString:&temp]) {
            [results appendString:temp];
        }
        if ([scanner scanString:@"&" intoString:NULL]) {
            BOOL valid = YES;
            unsigned c = 0;
            NSUInteger savedLocation = [scanner scanLocation];
            if ([scanner scanString:@"#" intoString:NULL]) {
                // it's a numeric entity
                if ([scanner scanString:@"x" intoString:NULL]) {
                    // hexadecimal
                    unsigned int value;
                    if ([scanner scanHexInt:&value]) {
                        c = value;
                    } else {
                        valid = NO;
                    }
                } else {
                    // decimal
                    int value;
                    if ([scanner scanInt:&value] && value >= 0) {
                        c = value;
                    } else {
                        valid = NO;
                    }
                }
                if (![scanner scanString:@";" intoString:NULL]) {
                    // not ;-terminated, bail out and emit the whole entity
                    valid = NO;
                }
            } else {
                if (![scanner scanUpToString:@";" intoString:&temp]) {
                    // &; is not a valid entity
                    valid = NO;
                } else if (![scanner scanString:@";" intoString:NULL]) {
                    // there was no trailing ;
                    valid = NO;
                } else if ([temp isEqualToString:@"amp"]) {
                    c = '&';
                } else if ([temp isEqualToString:@"quot"]) {
                    c = '"';
                } else if ([temp isEqualToString:@"lt"]) {
                    c = '<';
                } else if ([temp isEqualToString:@"gt"]) {
                    c = '>';
                } else {
                    // unknown entity
                    valid = NO;
                }
            }
            if (!valid) {
                // we errored, just emit the whole thing raw
                [results appendString:[input substringWithRange:NSMakeRange(savedLocation, [scanner scanLocation]-savedLocation)]];
            } else {
                [results appendFormat:@"%C", c];
            }
        }
    }
    return results;
}

【讨论】:

  • 您的代码失败,我不想尝试修复它:P 相反,我研究了您的方法,显然 Google 发布了“Google Toolbox for Mac”(code.google.com/p/google-toolbox-for-mac),其中包含一堆有用的功能,可以做我想要实现的。感谢您为我指明正确的方向。
  • @Scott:很抱歉。我只是尝试编译这个东西,并修复了错误。我现在发布的版本应该可以工作,虽然我没有彻底测试过。
  • 注意:除了&amp;"&lt;&gt;!,还有更多的 HTML 实体可以使用
  • @bobince:当然,“真正的”HTML 解析器会小心处理所有这些问题。但是大多数实体编码的数据都使用数字实体,命名实体往往只显示在人工创建的 HTML 中。
  • @Kevin:不幸的是,很多 PHP 作者似乎喜欢使用 htmlentities() 而不是通常更合适的 htmlspecialchars(),这会导致大量不必要的实体引用。 :-(
【解决方案2】:

HTML(和 XML)中的 &amp;#(number); 构造被称为字符引用。它不是特定于 Unicode 的,除了 HTML 中的所有字符都是根据 Unicode 定义的,无论是逐字包含还是编码为字符或实体引用。 (实体引用是看起来像 &amp;eacute;&amp;amp; 的命名引用,如果您正在抓取 HTML 页面,您当然也必须处理这些。)

标准库中没有用于解码字符或实体引用的函数。有关解码 HTML 文本内容的方法,请参阅 this question。如果您只有字符引用和标准 XML 实体,例如 &amp;amp;,您可以利用 NSXMLParser 来解析 &lt;element&gt;+yourstring+&lt;/element&gt;,但这不会处理特定于 HTML 的实体,例如 &amp;eacute; .

一般来说,屏幕抓取最好使用适当的 HTML 解析器来完成,而不是字符串破解。这会将所有文本内容转换为文本节点,同时转换字符和实体引用。但是,标准库中没有可用的 HTML 解析器。如果目标页面是格式良好的独立 XHTML,您可以再次使用 NSXMLParser。否则,您可能想尝试 libxml2,它提供了 HTML 解析器和 XML。有关背景信息,请参阅 this question

【讨论】:

  • 感谢您的澄清。不知道该怎么称呼它。
【解决方案3】:

如果您从网站获取数据,您将拥有一个NS(Mutable)Data-Object 作为您的接收缓冲区。您只需通过以下方式将 NSData 转换为 NSString
NSString *myString = [[NSString alloc] initWithData:myRecvData usingEncoding:NSUnicodeStringEncoding]
如果您的服务器以 Unicode 发送。如果您的服务器发送的是 utf-8 或其他格式,那么您还必须调整接收代码中的字符串编码。

here a list of all supported string-encoding-types

编辑: 看看这个so-thread

【讨论】:

  • 不正确。 OP 有 HTML 实体编码的数据,而不是实际的“unicode”数据。
  • 是的,但中间有一个编辑...我会更新我的答案
  • 使用 NSXMLParser 解码实体有点矫枉过正。
  • 刚刚注意到您在编辑中链接的那个线程中指向 Google 工具箱的链接(虽然已经找到了)。感谢您的帮助。
猜你喜欢
  • 2014-08-30
  • 1970-01-01
  • 2011-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多