How to print unsigned char* in NSLog() - objective-c

Title pretty much says everything.
would like to print (NOT in decimal), but in unsigned char value (HEX).
example
unsigned char data[6] = {70,AF,80,1A, 01,7E};
NSLog(#"?",data); //need this output : 70 AF 80 1A 01 7E
Any idea? Thanks in advance.

There is no format specifier for an char array. One option would be to create an NSData object from the array and then log the NSData object.
NSData *dataData = [NSData dataWithBytes:data length:sizeof(data)];
NSLog(#"data = %#", dataData);

Nothing in the standard libraries will do it, so you could write a small hex dump function, or you could use something else that prints non-ambigious full data. Something like:
char buf[1 + 3*dataLength];
strvisx(buf, data, dataLength, VIS_WHITE|VIS_HTTPSTYLE);
NSLog(#"data=%s", buf);
For smallish chunks of data you could try to make a NSData and use the debugDescription method. That is currently a hex dump, but nothing promises it will always be one.

To print char* in NSLog try the following:
char data[6] = {'H','E','L','L','0','\n'};
NSString *string = [[NSString alloc] initWithUTF8String:data];
NSLog(#"%#", string);
You will need to null terminate your string.
From Apple Documentation:
- (instancetype)initWithUTF8String:(const char *)nullTerminatedCString;
Returns an NSString object initialized by copying the characters from a given C array of UTF8-encoded bytes.

Related

Converting NSData bytes to NSString

I am trying to create a 16 byte and later 32 byte initialization vector in objective-c (Mac OS). I took some code on how to create random bytes and modified it to 16 bytes, but I have some difficulty with this. The NSData dumps the hex, but an NSString dump gives nil, and a cstring NSLog gives the wrong number of characters (not reproduced the same in the dump here).
Here is my terminal output:
2012-01-07 14:29:07.705 Test3Test[4633:80f] iv hex <48ea262d efd8f5f5 f8021126 fd74c9fd>
2012-01-07 14:29:07.710 Test3Test[4633:80f] IV string: (null)
2012-01-07 14:29:07.711 Test3Test[4633:80f] IV char string t^Q¶�^��^A
Here is the main program:
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//NSString *iv_string = [NSString stringWithCString:iv encoding:NSUTF8StringEncoding];
testclass *obj = [testclass alloc];
NSData *iv_data = [obj createRandomNSData];
//[iv_string dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"iv hex %#",iv_data);
//NSString *iv_string = [[NSString alloc] initWithBytes:[iv_data bytes] length:16 encoding:NSUTF8StringE$
NSString *iv_string = [[NSString alloc] initWithData:iv_data encoding:NSUTF8StringEncoding];
NSLog(#"IV string: %#",iv_string);
NSLog(#"IV char string %.*s",[iv_data bytes]);
return 0;
]
(I left in the above some commented code that I tried and did not work also).
Below is my random number generater, taken from a stack overflow example:
#implementation testclass
-(NSData*)createRandomNSData
{
int twentyMb = 16;
NSMutableData* theData = [NSMutableData dataWithCapacity:twentyMb];
for( unsigned int i = 0 ; i < twentyMb/4 ; ++i )
{
u_int32_t randomBits = arc4random();
[theData appendBytes:(void*)&randomBits length:4];
}
NSData *data = [NSData dataWithData:theData];
[theData dealloc];
return data;
}
#end
I am really quite clueless as to what could be the problem here. If I have data as bytes, it should convert to a string or not necessarily? I have looked over the relevant examples here on stackoverflow, but none of them have worked in this situation.
Thanks,
Elijah
An arbitrary byte sequence may not be legal UTF8 encoding. As #Joachim Isaksson notes, there is seldom reason to convert to strings this way. If you need to store random data as a string, you should use an encoding scheme like Base64, serialize the NSData to a plist, or similar approach. You cannot simply use a cstring either, since NULL is legal inside of a random byte sequence, but is not legal inside of a cstring.
You do not need to build your own random byte creator on Mac or iOS. There's one built-in called SecRandomCopyBytes(). For example (from Properly encrypting with AES with CommonCrypto):
+ (NSData *)randomDataOfLength:(size_t)length {
NSMutableData *data = [NSMutableData dataWithLength:length];
int result = SecRandomCopyBytes(kSecRandomDefault,
length,
data.mutableBytes);
NSAssert(result == 0, #"Unable to generate random bytes: %d",
errno);
return data;
}
When converting NSData to NSString using an UTF8 encoding, you won't necessarily end up with the same number of bytes since not all binary values are valid encodings of characters. I'd say using a string for binary data is a recipe for problems.
What is the use of the string? NSData is exactly the datatype you want for storing binary data to begin with.

NSString to char[]?

struct DATA
{
unsigned char USERNAME[32];
};
i want copy a NSString to struct DATA.USERNAME , how to do it ?
You can use the -[NSString UTF8String] method to get a C string of your NSString. Then, you can use strncpy(DATA.USERNAME, [mystring UTF8String], 32); to copy that string into the structure.
You first need to know what encoding is expected. NSString can generate bytes in a wide range of encodings. Then you pass a pointer to the USERNAME array to getCString:maxLength:encoding:. So, for example, if you want to copy the contents of the NSString myCocoaString as UTF-8 into USERNAME field of a DATA struct called myData, you'd do:
BOOL success = [myCocoaString getCString:myData.USERNAME maxLength:32 encoding:NSUTF8StringEncoding];
NSLog(#"Was %# to store string contents in USERNAME!", success ? #"able" : #"not able");

Open file and read from file Objective-c

I'm trying to open a file, and read from it.. but I'm having some issues.
FILE *libFile = fopen("/Users/pineapple/Desktop/finalproj/test242.txt","r");
char wah[200];
fgets(wah, 200, libFile);
printf("%s test\n", wah);
this prints: \377\376N test rather than any of the contents of my file.
any idea why?
complete code:
#import <Cocoa/Cocoa.h>
#import <stdio.h>
int main(int argc, char *argv[])
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
FILE *libFile = fopen("/Users/pineapple/Desktop/finalproj/test242.txt","r");
if(libFile){
char wah[200];
fgets(wah, 200, libFile);
printf("%s test\n", wah);
}
[pool drain];
return 0;
}
And the test242.txt doesn't contain more than 200 chars.
If this is for Objective-C, why not do something like:
use NSFileHandle:
NSString * path = #"/Users/pineapple/Desktop/finalproj/test242.txt";
NSFileHandle * fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
NSData * buffer = nil;
while ((buffer = [fileHandle readDataOfLength:1024])) {
//do something with the buffer
}
or use NSString:
NSString * fileContents = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
or if you need to read it line-by-line:
How to read data from NSFileHandle line by line?
IMO, there's no need to drop down to the C-level fileIO functions unless you have a very very very good reason for doing so (ie, open a file using O_SHLOCK or something)
Your file is stored in UTF-16 (Unicode). The first character in your file is "L", which is code point 0x4C. The first 4 bytes of your file are FF FE 4C 00, which are a byte-order mark (BOM) and the letter L encoded in UTF-16 as two bytes.
fgets is not Unicode-aware, so it's looking for the newline character '\n', which is the byte 0x0A. Most likely this will happen on the first byte of a Unicode newline (the two bytes 0A 00), but it could also happen on plenty of other non-newline characters such as U+010A (LATIN CAPITAL LETTER A WITH DOT ABOVE) or anything in the Gurmukhi or Gujarati scripts (U+0A00 to U+0AFF).
In any case, though, the data that's ending up in the buffer wah has lots of embedded nulls and looks something like FF FE 4C 00 47 00 4F 00 4F 00 0A 00. NUL (0x00) is the C string terminator, so when you attempt to print this out using printf, it stops at the first null, and all you see is \377\376L. \377\376 is the octal representation of the bytes FF FE.
The fix for this is to convert your text file to a single-byte encoding such as ISO 8859-1 or UTF-8. Note that must single-byte encodings (UTF-8 excepted) cannot encode the full range of Unicode characters, so if you need Unicode, I strongly recommend using UTF-8. Alternatively, you can convert your program to be Unicode-aware, but then you can no longer use a lot of standard library functions (such as fgets and printf), and you need to use wchar_t everywhere in place of char.
If you don't mind reading all of a file you can do something like this:
NSData* myData = [NSData dataWithContentsOfFile:myFileWithPath];
and then do whatever you'd like with the data from there. You will get nil if the file doesn't exist.
If you are assuming text (string) data in this file you can additionally do something like this and then parse it as a NSString:
NSString* myString = [[NSString alloc] initWithBytes:[myData bytes] length:[myData length] encoding:NSUTF8StringEncoding];
Since you mentioned you are relatively new to objective-c you can search NSStrings fairly well. Have a look here for more info on this.
I wanted this as well and thought "do this instead" did not answer the question, here is a working example below. Beware that fgets reads the \n delimiter and appends to your text.
NSString * fName = [[NSBundle mainBundle] pathForResource:#"Sample" ofType:#"txt"];
FILE *fileHandle = fopen([fName UTF8String],"r");
char space[1024];
while (fgets(space, 1024, fileHandle) != NULL)
{
NSLog(#"space = %s", space);
}
fclose(fileHandle);
printf("%s test\n");
You're not passing the string to printf. Try
printf("%s test\n", wah);
Also, if your file contains a line more than 200 characters long, fgets will read 200 characters into wah - then add a NUL to the end, which will be off the end of wah (since you declared it to be 200 characters) and will trample over something random, and the behaviour of your program will be undefined and may set fire to your cat.
Slycrel's got it. Expanding on that answer, here is another (in my opinion, simpler) way of turning that data into a string:
NSString *myFileString = [[NSString alloc] initWithData:someData encoding:NSUTF8StringEncoding];
This declares a new NSString directly using the NSData specified.

Using scanf with NSStrings

I want the user to input a string and then assign the input to an NSString. Right now my code looks like this:
NSString *word;
scanf("%s", &word);
The scanf function reads into a C string (actually an array of char), like this:
char word[40];
int nChars = scanf("%39s", word); // read up to 39 chars (leave room for NUL)
You can convert a char array into NSString like this:
NSString* word2 = [NSString stringWithBytes:word
length:nChars
encoding:NSUTF8StringEncoding];
However scanf only works with console (command line) programs. If you're trying to get input on a Mac or iOS device then scanf is not what you want to use to get user input.
scanf does not work with any object types. If you have a C string and want to create an NSString from it, use -[NSString initWithBytes:length:encoding:].
scanf does not work with NSString as scanf doesn’t work on objects. It works only on primitive datatypes such as:
int
float
BOOL
char
What to do?
Technically a string is made up of a sequence of individual characters. So to accept string input, you can read in the sequence of characters and convert it to a string.
use:
[NSString stringWithCString:cstring encoding:1];
Here is a working example:
NSLog(#"What is the first name?");
char cstring[40];
scanf("%s", cstring);
firstName = [NSString stringWithCString:cstring encoding:1];
Here’s an explanation of the above code, comment by comment:
You declare a variable called cstring to hold 40 characters.
You then tell scanf to expect a list of characters by using the %s format specifier.
Finally, you create an NSString object from the list of characters that were read in.
Run your project; if you enter a word and hit Enter, the program should print out the same word you typed. Just make sure the word is less than 40 characters; if you enter more, you might cause the program to crash — you are welcome to test that out yourself! :]
Taken from: RW.
This is how I'd do it:
char word [40];
scanf("%s",word);
NSString * userInput = [[NSString alloc] initWithCString: word encoding: NSUTF8StringEncoding];
yes, but sscanf does, and may be a good solution for complex NSString parsing.
Maybe this will work for you because it accepts string with spaces as well.
NSLog(#"Enter The Name Of State");
char name[20];
gets(name);
NSLog(#"%s",name);
Simple Solution is
char word[40];
scanf("%39s", word);
NSString* word2 = [NSString stringWithUTF8String:word];
The NSFileHandle class is an object-oriented wrapper for a file descriptor. For files, you can read, write, and seek within the file.
NSFileHandle *inputFile = [NSFileHandle fileHandleWithStandardInput];
NSData *inputData = [inputFile availableData];
NSString *word = [[NSString alloc]initWithData:inputData encoding:NSUTF8StringEncoding];

Casting NSString to unsigned char *

I'm trying to use a function that has the following signature to sign a HTTP request:
extern void hmac_sha1(const unsigned char *inText, int inTextLength, unsigned char* inKey, const unsigned int inKeyLength, unsigned char *outDigest);
And this is the method I wrote to use it:
- (NSString *)sign: (NSString *)stringToSign {
NSString *secretKey = #"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const unsigned char *inText = (unsigned char *)[stringToSign UTF8String];
int inTextLength = [stringToSign length];
unsigned char *inKey = (unsigned char *)[secretKey UTF8String];
const unsigned int inKeyLength = (unsigned int)[secretKey length];
unsigned char *outDigest;
hmac_sha1(inText, inTextLength, inKey, inKeyLength, outDigest);
NSString *output = [NSString stringWithUTF8String:(const char *)outDigest];
return output;
}
The problem is I'm sure this is not the way I'm supposed to do this casting, as inside this hmac_sha1 function I get a EXC_BAD_ACCESS exception.
Since I am new to Objective-C and have close to no experience in C (surprise!) I don't really know what to search for. Any tips on how I can start solving this?
Thanks in advance!
BTW, I got the reference for this function here in stackoverflow.
It looks like the problem is not with the casting, but with outDigest. The fifth argument to hmac_sha1 should point to an already allocated buffer of size 20 bytes (I think).
If you change the line that says
unsigned char *outDigest;
to say
#define HMACSHA1_DIGEST_SIZE 20
void *outDigest = malloc(HMACSHA1_DIGEST_SIZE);
That should get you past the crash inside hmac_sha1.
Then you've got the problem of converting the data at outDigest into an NSString. It looks like hmac_sha1 will put 20 bytes of random-looking data at outDigest, and not a null terminated UTF-8 string, so stringWithUTF8String: won't work. You might want to use something like this instead if you have to return an NSString:
NSString *output = [[NSString alloc] initWithBytesNoCopy:outDigest
length:HMACSHA1_DIGEST_SIZE
encoding:NSASCIIStringEncoding
freeWhenDone:YES];
I don't think NSString is really the right type for the digest, so it might be worth changing your method to return an NSData if you can.
This wasn't part of your question but it's a bug nonetheless, you shouldn't use -length to get the byte count of an UTF8 string. That method returns the number of Unicode characters in the string, not the number of bytes. What you want is -lengthOfBytesUsingEncoding:.
NSUInteger byteCount = [stringToSign lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
Also be aware that the result does not account for a terminating NULL character.
Are you sure you don't need to allocate some memory for outDigest before calling hmac_sha1? Since you pass in a pointer, rather than a pointer to a pointer, there's no way that the memory can be allocated inside the routine.