什么是NSString的UTF8String的CFString Equiv?

时间:2009-10-22 19:52:27

标签: iphone objective-c

我今天坚持使用stoopid因为我无法将一段简单的ObjC代码转换为它的Cpp等价物。我有这个:

  const UInt8 *myBuffer = [(NSString*)aRequest UTF8String];

我正试图用这个替换它:

  const UInt8 *myBuffer = (const UInt8 *)CFStringGetCStringPtr(aRequest, kCFStringEncodingUTF8);

这是一个紧凑的单元测试,它通过CFNetwork API在套接字上写一个示例HTTP请求。我有工作的ObjC代码,我正在尝试移植到C ++。我正在逐步用他们的免费桥接等价替换NS API调用。到目前为止,一切都是一对一的,直到最后一行。这就像最后一件需要完成的作品。

6 个答案:

答案 0 :(得分:14)

这是Cocoa在幕后制作所有杂乱内容的事情之一,你永远不会真正意识到事情是多么复杂,直到你不得不卷起袖子自己做。

为什么它不“简单”的简单答案是因为NSString(和CFString)处理处理多个字符集,Unicode等等的所有复杂细节,同时呈现用于操作字符串的简单,统一的API。它的面向对象是最好的 - “如何”(NS|CF)String处理具有不同字符串编码的字符串(UTF8,MacRoman,UTF16,ISO 2022日语等)的细节是私有实现细节。这一切都“正常”。

有助于了解[@"..." UTF8String]的工作原理。这是一个私有的实现细节,所以这不是福音,而是基于观察到的行为。当你发送一个字符串UTF8String消息时,该字符串会做一些近似的事情(实际上没有经过测试,所以考虑它是伪代码,实际上有更简单的方法来做同样的事情,所以这个过于冗长): / p>

- (const char *)UTF8String
{
  NSUInteger utf8Length = [self lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  NSMutableData *utf8Data = [NSMutableData dataWithLength:utf8Length + 1UL];
  char *utf8Bytes = [utf8Data mutableBytes];
  [self     getBytes:utf8Bytes
           maxLength:utf8Length
          usedLength:NULL
            encoding:NSUTF8StringEncoding
             options:0UL
               range:NSMakeRange(0UL, [self length])
      remainingRange:NULL];
  return(utf8Bytes);
}

您不必担心处理-UTF8String返回的缓冲区的内存管理问题,因为NSMutableData已自动释放。

字符串对象可以自由地以任何形式保存字符串的内容,因此无法保证其内部表示形式最符合您的需求(在本例中为UTF8)。如果你只使用普通的C,你将不得不处理管理一些内存来保存可能需要的任何字符串转换。曾经简单的-UTF8String方法调用现在变得非常复杂。

大多数NSString实际上都是在/使用CoreFoundation / CFString实现的,所以显然有CFStringRef - >的路径。 -UTF8String。它不像NSString的{​​{1}}那样整洁简单。大多数并发症都是内存管理。以下是我过去处理过的问题:

-UTF8String

注意:我没有测试过这段代码,但是它是从工作代码中修改过来的。因此,除了明显的错误,我相信它应该有效。

以上尝试获取指向void someFunction(void) { CFStringRef cfString; // Assumes 'cfString' points to a (NS|CF)String. const char *useUTF8StringPtr = NULL; UInt8 *freeUTF8StringPtr = NULL; CFIndex stringLength = CFStringGetLength(cfString), usedBytes = 0L; if((useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8)) == NULL) { if((freeUTF8StringPtr = malloc(stringLength + 1L)) != NULL) { CFStringGetBytes(cfString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', false, freeUTF8StringPtr, stringLength, &usedBytes); freeUTF8StringPtr[usedBytes] = 0; useUTF8StringPtr = (const char *)freeUTF8StringPtr; } } long utf8Length = (long)((freeUTF8StringPtr != NULL) ? usedBytes : stringLength); if(useUTF8StringPtr != NULL) { // useUTF8StringPtr points to a NULL terminated UTF8 encoded string. // utf8Length contains the length of the UTF8 string. // ... do something with useUTF8StringPtr ... } if(freeUTF8StringPtr != NULL) { free(freeUTF8StringPtr); freeUTF8StringPtr = NULL; } } 用于存储字符串内容的缓冲区的指针。如果CFString恰好具有以UTF8编码的字符串内容(或适当兼容的编码,例如ASCII),那么CFString可能会返回非CFStringGetCStringPtr()。这显然是最好,最快的案例。如果由于某种原因无法获得该指针,比如说NULL的内容是以UTF16编码的,那么它会分配一个CFString的缓冲区,该缓冲区足够大,可以在转码时包含整个字符串。到UTF8。然后,在函数结束时,它会检查是否已分配内存,并在必要时malloc()

现在有一些提示和技巧...... free()'倾向于'(这是一个私有的实现细节,所以它可以并且确实在版本之间发生变化)保持'简单'字符串编码为MacRoman,这是一个8位宽的编码。与UTF8一样,MacRoman是ASCII的超集,因此所有字符都是< 128等同于它们的ASCII对应物(或者换句话说,任何字符< 128是ASCII)。在MacRoman中,字符> = 128是'特殊'字符。它们都具有Unicode等价物,并且往往是额外的货币符号和“扩展的西方”字符。有关详细信息,请参阅Wikipedia - MacRoman。但仅仅因为CFString表示它是MacRoman(CFString编码值为CFStringkCFStringEncodingMacRoman编码值为NSString)并不意味着它有字符&gt ; = 128。如果NSMacOSRomanStringEncoding返回的kCFStringEncodingMacRoman编码字符串完全由字符组成< 128,然后它完全等同于其ASCII(CFStringGetCStringPtr())编码表示,它也完全等同于字符串UTF8(kCFStringEncodingASCII)编码表示。

根据您的要求,您可以在致电kCFStringEncodingUTF8时使用kCFStringEncodingMacRoman代替kCFStringEncodingUTF8来“过来”。如果你的字符串需要严格的UTF8编码但是使用CFStringGetCStringPtr(),那么“可能”(可能)会更快,然后检查以确保kCFStringEncodingMacRoman返回的字符串仅包含< 128.如果字符串中有字符> = 128,则通过CFStringGetCStringPtr(string, kCFStringEncodingMacRoman)缓冲区来缓慢路由来保存转换后的结果。例如:

malloc()

就像我说的那样,你并不真正欣赏Cocoa为你做多少工作,直到你必须自己完成。 :)

答案 1 :(得分:5)

在上面的示例代码中,出现以下内容:

CFIndex stringLength = CFStringGetLength(cfString)
然后

stringLength被用于malloc()一个多字节的临时缓冲区加1。

CFStringGetLength()的头文件明确表示它返回的是16位Unicode字符的数量,而不是字节数。因此,如果其中一些Unicode字符超出ASCII范围,malloc()缓冲区将不足以保持字符串的UTF-8转换。

也许我错过了一些东西,但为了绝对安全,当它们全部转换为UTF-8时,保存N个任意Unicode字符所需的字节数最多为4 * n。

答案 2 :(得分:3)

来自documentation

  

此函数是否返回有效指针或NULL取决于许多因素,所有因素都取决于字符串的创建方式及其属性。此外,功能结果可能会在不同版本之间和不同平台上发生变化。因此,在任何情况下都不要指望从此函数接收非NULL结果。

如果CFStringGetCString返回CFStringGetCStringPtr,则应使用NULL

答案 3 :(得分:2)

这是一些有效的代码。我从@ johne的回答开始,为简单起见,将CFStringGetBytes替换为CFStringGetLength,并由@Doug进行修正。

const char *useUTF8StringPtr = NULL;
char *freeUTF8StringPtr = NULL;

if ((useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8)) == NULL)
{
    CFIndex stringLength = CFStringGetLength(cfString);
    CFIndex maxBytes = 4 * stringLength + 1;
    freeUTF8StringPtr = malloc(maxBytes);
    CFStringGetCString(cfString, freeUTF8StringPtr, maxBytes, kCFStringEncodingUTF8);
    useUTF8StringPtr = freeUTF8StringPtr;
}

// ... do something with useUTF8StringPtr... 

if (freeUTF8StringPtr != NULL)
    free(freeUTF8StringPtr);

答案 4 :(得分:0)

如果它的目的地是套接字,那么CFStringGetBytes()可能是你最好的选择吗?

另请注意,CFStringGetCStringPtr()的文档说:

  

此函数或者在常量时间内立即返回请求的指针,没有内存分配和复制,或者返回NULL。如果后者是结果,则调用替代函数(例如CFStringGetCString函数)来提取字符。

答案 5 :(得分:0)

这是printf CFStringRef的一种方法,这意味着我们从CFStringRef得到一个'\ 0'终止的字符串:

// from: http://lists.apple.com/archives/carbon-development/2001/Aug/msg01367.html
// by Ali Ozer
// gcc -Wall -O3 -x objective-c -fobjc-exceptions -framework Foundation test.c

#import <stdio.h>
#import <Foundation/Foundation.h>

/*
This function will print the provided arguments (printf style varargs) out to the console.
Note that the CFString formatting function accepts "%@" as a way to display CF types.
For types other than CFString and CFNumber, the result of %@ is mostly for debugging
and can differ between releases and different platforms. Cocoa apps (or any app which
links with the Foundation framework) can use NSLog() to get this functionality.
*/

void show(CFStringRef formatString, ...) {
   CFStringRef resultString;
   CFDataRef data;
   va_list argList;
   va_start(argList, formatString);
   resultString = CFStringCreateWithFormatAndArguments(NULL, NULL, formatString, argList);
   va_end(argList);
   data = CFStringCreateExternalRepresentation(NULL, resultString, 
   CFStringGetSystemEncoding(), '?');
   if (data != NULL) {
      printf ("%.*s\n", (int)CFDataGetLength(data), CFDataGetBytePtr(data));
      CFRelease(data);
   }
   CFRelease(resultString);
}

int main(void)
{

   // To use:
   int age = 25;
   CFStringRef name = CFSTR("myname");

   show(CFSTR("Name is %@, age is %d"), name, age);

   return 0;
}