Cstring from Nsdata

How can I convert NSData to C string?

NSMutableData *data = [[@"foo" dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES] mutableCopy];
char nul = '\0';
[data appendBytes:&nul length:sizeof(nul)];
const char *cString = [data bytes];

Or, simply:

const char *cString = [@"foo" UTF8String];

... with exactly the same result.

Create CString from NSData

A C string is null-terminated. Arbitrary binary data doesn't need to be null terminated. I think the problem is that the bridging mechanism sees a char * and thinks "C string". Perhaps if you changed it to void *, you will get a more appropriate type in Swift.

CString from NSData

You'll need to convert it to NSString first, then you can use the cString() function:

   let xmlData = NSData()
let cocoaString = NSString(data: xmlData, encoding: NSUTF8StringEncoding)

let cString = cocoaString.cString()

Converting NSData that contains UTF-8 and null bytes to string

The documentation for stringWithUTF8String describes its first parameter as:

A NULL-terminated C array of bytes in UTF8 encoding.

Which is why your conversion stops at the first null byte.

What you appear to have is a collection of C strings packed into a single NSData. You can convert each one individually. Use the NSData methods bytes and length to obtain a pointer to the bytes/first C string and the total number of bytes respectively. The standard C function strlen() will give you the length in bytes of an individual string. Combine these and some simple pointer arithmetic and you can write a loop which converts each string and, for example, stores them all into an array or concatenates them.

If you get stuck implementing the solution ask a new question, show your code, and explain the issue. Someone will undoubtedly help you with the next step.

HTH

converting an NSString with accented characters to a CString

The docs for NSString getCString:maxLength:encoding says:

You can use canBeConvertedToEncoding: to check whether a string can be
losslessly converted to encoding. If it can’t, you can use
dataUsingEncoding:allowLossyConversion: to get a C-string
representation using encoding, allowing some loss of information (note
that the data returned by dataUsingEncoding:allowLossyConversion: is
not a strict C-string since it does not have a NULL terminator).

Using the NSString method dataUsingEncoding:allowLossyConversion: does the trick. Here's a code example:

NSString *myAccentStr = @"José";
char str[[myAccentStr length] + 1];

// NSString * to C String (char*)
NSData *strData = [myAccentStr dataUsingEncoding:NSMacOSRomanStringEncoding
allowLossyConversion:YES];
memcpy(str, [strData bytes], [strData length] + 1);
str[[myAccentStr length]] = '\0';
NSLog(@"str (from NSString* to c string): %s", str);

// C String (char*) to NSString *
NSString *newAccentStr = [NSString stringWithCString:str
encoding:NSMacOSRomanStringEncoding];
NSLog(@"newAccentStr (from c string to NSString*): %@", newAccentStr);

The output from that NSLog is:

str (from NSString* to c string): José

newAccentStr (from c string to NSString*): José

So far I've only seen this work properly when using the NSMacOSRomanStringEncoding.


Edit

Changing this to a community wiki. Please feel free to edit.

hooleyhoop had some great points, so I thought I would try to make code that is as verbose as possible. If I'm missing anything, someone please chime in.

Also - Not sure why [NSString canBeConvertedToEncoding:] is returning YES even though the [NSString getCString:maxLength:encoding:] function definitely isn't working right (as seen by the output).

Here's some code to help in analyzing what works / what doesn't:

// Define Block variable to tests out different encodings
void (^tryGetCStringUsingEncoding)(NSString*, NSStringEncoding) = ^(NSString* originalNSString, NSStringEncoding encoding) {
NSLog(@"Trying to convert \"%@\" using encoding: 0x%X", originalNSString, encoding);
BOOL canEncode = [originalNSString canBeConvertedToEncoding:encoding];
if (!canEncode)
{
NSLog(@" Can not encode \"%@\" using encoding %X", originalNSString, encoding);
}
else
{
// Try encoding using NSString getCString:maxLength:encoding:
NSUInteger cStrLength = [originalNSString lengthOfBytesUsingEncoding:encoding];
char cstr[cStrLength];
[originalNSString getCString:cstr maxLength:cStrLength encoding:encoding];
NSLog(@" Converted(1): \"%s\" (expected length: %u)",
cstr, cStrLength);

// Try encoding using NSString dataUsingEncoding:allowLossyConversion:
NSData *strData = [originalNSString dataUsingEncoding:encoding allowLossyConversion:YES];
char cstr2[[strData length] + 1];
memcpy(cstr2, [strData bytes], [strData length] + 1);
cstr2[[strData length]] = '\0';
NSLog(@" Converted(2): \"%s\" (expected length: %u)",
cstr2, [strData length]);
}
};

NSString *myAccentStr = @"José";

// Try out whatever encoding you want
tryGetCStringUsingEncoding(myAccentStr, NSUTF8StringEncoding);
tryGetCStringUsingEncoding(myAccentStr, NSUTF16StringEncoding);
tryGetCStringUsingEncoding(myAccentStr, NSUTF32StringEncoding);
tryGetCStringUsingEncoding(myAccentStr, NSMacOSRomanStringEncoding);

Results:

> Trying to convert "José" using encoding: 0x4
> Converted(1): "" (expected length: 5)
> Converted(2): "José" (expected length: 5)
> Trying to convert "José" using encoding: 0xA
> Converted(1): "" (expected length: 8)
> Converted(2): "ˇ˛J" (expected length: 10)
> Trying to convert "José" using encoding: 0x8C000100
> Converted(1): "" (expected length: 16)
> Converted(2): "ˇ˛" (expected length: 20)
> Trying to convert "José" using encoding: 0x1E
> Converted(1): "-" (expected length: 4)
> Converted(2): "José" (expected length: 4)

How do you convert NSData to NSString or char?

NSString *theString =[[NSString alloc]initWithData:data encoding:NSStringEncodingConversionAllowLossy];
NSLog(@"%@",theString);
[theString release];

What's the fastest way to init a NSString from a CString?

As per my comment, and Brian's answer, I think the problem here is that to create NSStrings you're having to parse the UTF-8 strings. So the question arises: do you really need to parse them, then?

If parsing-on-demand is an option then I'd suggest you write a proxy that can impersonate NSString with an interface along the lines of:

@interface BJLazyUTF8String: NSProxy
- (id)initWithBytes:(const char *)bytes length:(size_t)length;
@end

So it's not a subclass of NSString and it doesn't try to provide any real functionality. Inside the init just keep the bytes, e.g. as _bytes, doing whatever is correct for your C memory ownership. Then:

- (NSString *)bjRealString
{
// we'd better create the NSString if we haven't already

if(!_string)
_string = [NSString stringWithUTF8String:_bytes];

return _string;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// if this is invoked then someone is trying to
// make a call to what they think is a string;
// let's forward that call to a string so that
// it does what they expect
[anInvocation setTarget:[self bjRealString]];
[anInvocation invoke];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [[self bjRealString] methodSignatureForSelector:aSelector];
}

You can then do:

NSString *myString = [[BJLazyUTF8String alloc] initWithBytes:... length:...];

And subsequently treat myString exactly as though it were an NSString.



Related Topics



Leave a reply



Submit