NSString initWithData返回null

时间:2010-08-14 21:28:45

标签: iphone objective-c cocoa macos nsstring

我通过NSURLConnection从网站提取数据,并将收到的数据存储在NSMutableData的实例中。在connectionDidFinishLoading委托方法中,数据被转换为一个字符串,并调用NSString的适当方法:

NSString *result = [[NSString alloc] initWithData:data 
                                     encoding:NSUTF8StringEncoding]

结果字符串结果为空。但是,如果我使用NSASCIIStringEncoding,我会获得相应的字符串,尽管unicode字符会按预期乱码。服务器的Content-Type标头指定UTF-8编码,但我尝试了许多具有类似场景的不同网站,并且字符串转换发生得很好。看起来问题只与给定的Web服务有关,但我不知道为什么。

另一方面,是从API良好实践中提取网页和数据,即缓冲数据,转换为字符串,然后操纵字符串?

非常感谢!

5 个答案:

答案 0 :(得分:27)

你说它“肯定是UTF-8”,但没有Content-Type标题,你真的不知道。 (即使你确实有一个标题说,它仍然可能是错误的。)

我的猜测是你的数据通常是ASCII,它总是以UTF-8正确解析,但你有时会试图解析实际上在ISO 8859-1或Windows代码页1252中编码的数据。这些数据通常主要是ASCII ,但有一些字节在0-127范围之外的ASCII定义。 UTF-8希望这些字节在指定的范围序列中形成一系列代码单元,但在其他编码中,任何字节,无论值如何,都是一个完整的字符。试图将非ASCII非UTF-8数据解释为UTF-8几乎总会得到错误的结果(错误的字符)或根本没有结果(无法解码;解码器返回nil),因为数据是从来没有以UTF-8编码。

您应首先尝试使用UTF-8,如果失败,请使用ISO 8859-1。如果您让用户检索任何网页,您应该让他们更改用于解码数据的编码,以防他们发现它实际上是8859-9或codepage-1252或其他一些8位编码。 / p>

如果您从特定服务器下载数据,特别是如果您对该服务器上运行的内容有影响,您应该使其提供准确的Content-Type标头和/或修复导致它的任何错误提供非UTF-8的文本。

答案 1 :(得分:8)

正如彼得所说,内容类型标题只是内容发送预期的“暗示”。在服务器端,您可以设置任何内容类型并发送任何字节序列,这些序列可能无效。

我在处理不正确的UTF-8数据时遇到了同样的问题,其中包括ISO-8859-1(Latin-1)字符(法语口音)。

Wikipedia about UTF-8值得一读,以了解此问题以及如何处理编码错误。

事实是NSString initWithData:encoding:严格实现只在发生解码错误时返回nil。 (不像java那样使用替换字符)

将大多数UTF-8数据转换为Latin-1的彼得解决方案并不令我满意。 (所有UTF-8字符都变得不正确,只有一个拉丁文1个不稳定的字符)

最好的选择是在服务器端修复,当然,但我不负责这方面......

所以我看得更深,并找到了使用GNU libiconv C库的解决方案(在OSX和iOS上可用) 原则是使用iconv删除非UTF-8无效字符(即“prété”将成为“prt”)

这是一个示例代码,相当于命令行iconv -c -f UTF-8 -t UTF-8 invalid.txt > cleaned.txt

#include "iconv.h"

- (NSData *)cleanUTF8:(NSData *)data {
  iconv_t cd = iconv_open("UTF-8", "UTF-8"); // convert to UTF-8 from UTF-8
  int one = 1;
  iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, &one); // discard invalid characters

  size_t inbytesleft, outbytesleft;
  inbytesleft = outbytesleft = data.length;
  char *inbuf  = (char *)data.bytes;
  char *outbuf = malloc(sizeof(char) * data.length);
  char *outptr = outbuf;
  if (iconv(cd, &inbuf, &inbytesleft, &outptr, &outbytesleft)
      == (size_t)-1) {
    NSLog(@"this should not happen, seriously");
    return nil;
  }
  NSData *result = [NSData dataWithBytes:outbuf length:data.length - outbytesleft];
  iconv_close(cd);
  free(outbuf);
  return result;
}

然后可以使用NSData

安全地解码生成的NSUTF8StringEncoding

请注意,最新的iconv还允许使用以下方法进行回退:

iconvctl(cd, ICONV_SET_FALLBACKS, &fallbacks);

通过使用unicode错误的回退,您可以使用替换字符或更好的方法来尝试其他编码。 在我的情况下,我设法回退到LATIN-1,其中UTF-8失败,导致99%的正向转换。查看iconv源代码以了解它。

答案 2 :(得分:5)

如果未指定HTTP,则为HTTP的默认编码为ISO-8859-1。如果HTTP响应符合HTTP / 1.1并且它没有指定字符集编码,那就是它正在使用的编码。

尝试使用NSISOLatin1StringEncoding解码字符串。

答案 3 :(得分:3)

数据可能是另一种unicode编码,例如UTF16,或者是一些完全不同的编码。

有些库可以猜测数据中使用的编码,但这应该是最后的选择。 如果您使用的是Web服务,那么该Web服务应该有一个文档,说明它使用的编码。查找它,或向Web服务提供商询问它使用的编码。如果两者都不可用,您应该尝试获取示例数据并确定其编码,并在程序中使用它。

  

另一方面,是从API良好实践中提取网页和数据,即缓冲数据,转换为字符串,然后操纵字符串?

这取决于数据的大小。如果它很小,那就完全没问题了。如果它很大,最好是零散地处理数据。

答案 4 :(得分:0)

等一下,OP正在从网上读到第一位吗?为什么不使用NSString呢? stringWithContentsOfURL:usedEncoding:error: 返回通过读取给定URL中的数据创建的字符串,并通过引用返回用于解释数据的编码。

+ (id)stringWithContentsOfURL:(NSURL *)url usedEncoding:(NSStringEncoding *)enc error:(NSError **)error

页面n页减少到一行嘿......除非我当然遗憾地错了。