我今天坚持使用stoopid因为我无法将一段简单的ObjC代码转换为它的Cpp等价物。我有这个:
const UInt8 *myBuffer = [(NSString*)aRequest UTF8String];
我正试图用这个替换它:
const UInt8 *myBuffer = (const UInt8 *)CFStringGetCStringPtr(aRequest, kCFStringEncodingUTF8);
这是一个紧凑的单元测试,它通过CFNetwork API在套接字上写一个示例HTTP请求。我有工作的ObjC代码,我正在尝试移植到C ++。我正在逐步用他们的免费桥接等价替换NS API调用。到目前为止,一切都是一对一的,直到最后一行。这就像最后一件需要完成的作品。
答案 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
编码值为CFString
,kCFStringEncodingMacRoman
编码值为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)
此函数是否返回有效指针或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;
}