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 NSString
s 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
Issues While Lightweight Core Data Migration
How to Catch an Exception in Swift
Remove Cell When Button Pressed Inside Cell Customtableviewcell
How to Cut a Hole in a Sprite Image or Texture to Show What Is Behind It Using Spritekit in Swift
iOS 14 Widget Detect System Theme Change
How to Write a Generic Function for Floating Point Values in Swift
How to Change/Modify The Displayed Title of an Nspopupbutton
Pdf417 Decode and Generate The Same Barcode Using Swift
Cleanly Handling /Usr/Local/ with Swift Package Manager and Libevent
Combining Scenekit and Spritekit in a Single Screen
Swift Pattern Match on Array<Any>
Macos, Swift 3: How to Get Data Back After Segue
Swift Running Code in Periodically Background
Swift Corebluetooth Reading a Float Array from Ble
Access Environment Variable Inside Global Function - Swiftui + Coredata