How to format string encoded with NSASCIIStringEncoding - objective-c

In my Objective-C project, I'm fetching a key from cloudKit and wanted to format it before like this and wondering what's wrong with the formatting,
[cloudKitDB performQuery:query inZoneWithID:nil completionHandler:^(NSArray<CKRecord *> * _Nullable results, NSError * _Nullable error) {
NSString *key = [[NSString alloc] initWithData:[results.firstObject objectForKey:#"keyvalue"] encoding:NSASCIIStringEncoding];
NSLog(#"First Key Output: %#",key);
NSLog(#"Second Key Output: %#",key);
NSString *formattedKey = [NSString stringWithFormat:#"%#%#",[key stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]], #"1234"];
NSLog(#"Formatted Key: %#",formattedKey);
And here my logged
2020-08-15 21:08:14.704650+0700 myapp[2208:79554] First Key Output: ADEvXp2F2BFJ2E4Lm2dSnYvHENhkFrK8
2020-08-15 21:08:14.704828+0700 myapp[2208:79554] Second Key Output: ADEvXp2F2BFJ2E4Lm2dSnYvHENhkFrK8
2020-08-15 21:08:14.705456+0700 myapp[2208:79554] Formatted Key: ADEvXp2F2BFJ2E4Lm2dSnYvHENhkFrK8
Although, I have tried to trim whitespace but still no luck!
Thanks

This is a strange problem, but I can reproduce it with the code below.
Based on this I still suspect that there are some funnies behind the string and have given ways to fix it in the code.
// Note C strlen will stop at the first 0
char * s = "3Lm9dGmeTf3Lm9dGmeTf3Lm9dGmeTfdd\0\0\0";
NSData * data = [NSData dataWithBytes:s length:strlen( s ) + 2];
NSString * key = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSString * keyFormatted = [NSString stringWithFormat:#"%#%#", key, #"1234"];
NSLog ( #"Key : %#", key );
NSLog ( #"Formatted key: %#", keyFormatted );
// Use strlen to get rid of funnies
NSData * dataFixed = [data subdataWithRange:NSMakeRange( 0, strlen( s ) )];
NSString * keyFixed = [[NSString alloc] initWithData:dataFixed encoding:NSASCIIStringEncoding];
NSString * keyForm2 = [NSString stringWithFormat:#"%#%#", keyFixed, #"1234"];
NSLog ( #"Fixed key : %#", keyFixed );
NSLog ( #"Formatted key: %#", keyForm2 );
// Use C isprint to trim ... crude but works
while ( key.length && ! isprint ( [key characterAtIndex:key.length - 1] ) )
{
key = [key substringToIndex:key.length - 1];
}
keyFormatted = [NSString stringWithFormat:#"%#%#", key, #"1234"];
NSLog ( #"Key : %#", key );
NSLog ( #"Formatted key: %#", keyFormatted );

Related

Unable to convert UTF-8 text from NSDictionary in Objective-C

Im using foursquare API to get some locations around me, but when the name of that place wasn't in english, the name will be like follows:
name = "\U0645\U0633\U062c\U062f \U0627\U0644\U0633\U064a\U062f\U0629 \U0639\U0627\U0626\U0634\U0629 | Aisha Mosque";
i tried to convert the response to a UTF-8 but nothing changed.
Here is my code:
-(void)setUpLocations{
NSURL *url = [NSURL URLWithString: #"https://api.foursquare...."];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSLog(#"Response: %#",[[[json objectForKey:#"response"]objectForKey:#"groups"]valueForKey:#"items"]);
}
And the log result is:
contact = {
};
id = 51712507498ec4e8c5ae9f48;
likes = {
count = 0;
groups = (
);
};
location = {
address = Abdoun;
cc = JO;
city = Amman;
country = Jordan;
distance = 3819;
lat = "31.95406043797281";
lng = "35.88095228186612";
};
name = "\U0645\U0633\U062c\U062f \U0627\U0644\U0633\U064a\U062f\U0629 \U0639\U0627\U0626\U0634\U0629 | Aisha Mosque";
restricted = 1;
stats = {
checkinsCount = 43;
tipCount = 2;
usersCount = 23;
};
verified = 0;
},
Any Suggestions ??
EDIT:
here is how i extract the data from the dictionary:
NSDictionary *dic = [[[[json objectForKey:#"response"]objectForKey:#"groups"]valueForKey:#"items"] copy];
namesArray = [[NSArray alloc]initWithArray:[self removeWhiteSpaces:[dic valueForKey:#"name"]]];
-(NSArray *)removeWhiteSpaces:(NSDictionary *)dic{
NSString *str = [NSString stringWithFormat:#"%#",dic];
NSString *str2 = [str stringByReplacingOccurrencesOfString:#"\n" withString:#""];
NSString *secondString = [str2 stringByReplacingOccurrencesOfString:#" " withString:#""];
NSString *thirdString = [secondString stringByReplacingOccurrencesOfString:#"(" withString:#""];
NSString *forthString = [thirdString stringByReplacingOccurrencesOfString:#")" withString:#""];
NSString *fifthString = [forthString stringByReplacingOccurrencesOfString:#"\"" withString:#""];
NSArray *items = [fifthString componentsSeparatedByString:#","];
return items;
}
And in the UITableView:
cell.textLabel.text = [NSString stringWithFormat:#"Name: %# ",[namesArray objectAtIndex:indexPath.row] ];
Update
After trying #Martin R answer i got the same results:
NSDictionary *dic = [[[[json objectForKey:#"response"]objectForKey:#"groups"]valueForKey:#"items"] copy];
NSString *value =[dic valueForKey:#"name"];
NSLog(#"%#", value);
UILabel *lbl = [[UILabel alloc]initWithFrame:self.view.frame];
lbl.numberOfLines = 0;
lbl.text = [NSString stringWithFormat:#"%#",value];;
[self.view addSubview:lbl];
and here is an image of the result
There is no problem.
NSLog() calls the description method of NSDictionary and NSArray, and that prints all non-ASCII characters as \Unnnn escape sequence.
If you extract the string values from the dictionary and print that you will see
that everything is correct.
Simple example:
NSDictionary *dict = #{ #"currency": #"€" };
NSLog(#"%#", dict);
// Output: { currency = "\U20ac"; }
NSString *value = dict[#"currency"];
NSLog(#"%#", value);
// Output: €
UPDATE: The problem seems to be in your removeWhiteSpaces: method, because
NSString *str = [NSString stringWithFormat:#"%#",dic];
already uses the description method to convert the dictionary to a string,
and the following stringByReplacingOccurrencesOfString calls are a (sorry!) very bad
method to fix that.
You should access the dictionary keys with objectForKey instead, or enumerate
the dictionary with for (NSString *key in dic) { ... } and build the desired
array.
UPDATE 2: From the JSON data (posted in chat discussion) it seem that you just need
NSArray *itemsArray = json[#"response"][#"groups"][0][#"items];
NSArray *namesArray = [itemsArray valueForKey:#"name"];
Note that "groups" is an array with one element.
Try to use this one..
[NSString stringWithUTF8String:]

JSON text and variable count

I am reading like this...
NSString *fileContent = [[NSString alloc] initWithContentsOfFile:path];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSDictionary *data = (NSDictionary *) [parser objectWithString:fileContent error:nil];
// getting the data from inside of "menu"
NSString *message = (NSString *) [data objectForKey:#"message"];
NSString *name = (NSString *) [data objectForKey:#"name"];
NSArray *messagearray = [data objectForKey:#"message"];
NSArray *namearray = [data objectForKey:#"name"];
NSDictionary* Dictionary = [NSDictionary dictionaryWithObjects:message forKeys:name];
for (NSString* Key in [Dictionary allKeys]){
NSLog(#"%# %#",Key,[Dictionary objectForKey:Key]);
}
...this JSON file...
{"message":["Untitled1a","Untitled2a","Untitled3a"],"name": ["Untitled1b","Untitled2b","Untitled3b"]}
...this is the result...
Untitled3b Untitled3a
2012-05-12 11:31:17.983 Quick Homework[721:f803] Untitled1b Untitled1a
2012-05-12 11:31:17.983 Quick Homework[721:f803] Untitled2b Untitled2a
...but for each pair (Untitled 1b 2b) I would like to alloc two UITextFields, witch display the correspondent text...
I tried using this method:
for (NSString *string in messagearray){
}do{
NSLog(#"happt = %i", b);
b++;
}
while(b == b);
//While loop
while (b == b ) {
NSLog(#"x = %i", b);
b++;
}
}
I would like to count the objects in the array in order to repeat an alloc code for UITextField that number of times, and display the text accordingly, but I am not able. Please help!!
Why can't you use -count?
b = [messagearray count]
To directly answer your question:
b = 0;
for (id item in messagearray)
b++;

Blob to image conversion

I am fetching result by fire a transaction but by the transaction one result is coming as blob attributes, that is image, I want to change that blob attribute to image
I wrote code for that "icon" is the key for fetch the image from transaction,
so please help me check this,
image is printing nil,
why?
NSString *inputString = [[[self formModel] attributeAsString:#"icon"] description];
NSLog(#"icon is %#",[[self formModel] attributeAsString:#"icon"]);
NSLog(#"inputstring is %#",inputString);
//NSImage *image = [NSUnarchiver unarchiveObjectWithData:[[self formModel] attributeAsString:#"icon"]];
//NSLog(#"image is %#",image);
NSArray *words = [inputString componentsSeparatedByString:#" "];
NSLog(#"words is %#",words);
NSArray *sizes = [words valueForKey:#"length"];
int sizeOfBytes = 0;
for (NSNumber *size in sizes) {
sizeOfBytes += [size intValue]/2;
}
int bytes[sizeOfBytes];
int counts = 0;
for (NSString *word in words) {
// convert each word from string to int
NSMutableString *ostr = [NSMutableString stringWithCapacity:[word length]];
while ([word length] > 0) {
[ostr appendFormat:#"%#", [word substringFromIndex:[word length] - 2]];
word = [word substringToIndex:[word length] - 2];
}
NSScanner *scaner = [NSScanner scannerWithString:ostr];
unsigned int val;
[scaner scanHexInt:&val];
bytes[counts] = val;
counts++;
}
// get NSData form c array
NSData *data = [NSData dataWithBytes:bytes length:sizeOfBytes];
NSLog(#"My NSDATA %#",data);
NSImage *Image = [[NSImage alloc] initWithData:data];
Never use the output of description to do processing. There is no guarantee of its format. What format is your original "blob" in and how was it generated? Your code suggests it might be an NSData or it might be an NSKeyArchiver. Both of these easily convert to NSData. You never need to do this by hand by converting to a string.

NSData to NSString in ObjectiveC

I am converting the APN Device token which is in NSData format to NSString, but i am some special characters,
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(#"Device Token 111 : %#", deviceToken);
NSString *deviceStr = [[NSString alloc] initWithData:deviceToken encoding:NSUTF8StringEncoding];
NSLog(#"Device Token : %#", deviceStr);
[deviceStr release];
}
Device Token 111 : <d8b62879 48de8f9f 90507519 da1d39cf 1b700f7f 022dcaf4 7532a8b7 a6f9afe4>
Device Token : ض(yHÞPuÚ9Ïep-Êôu2¨·¦ù¯ä
I have even tried with NSASCIIStringEncoding.
What am i doing wrong ?
Try using the following method with [deviceToken bytes] as the first parameter.
const static char hexchar[] = "0123456789ABCDEF";
- (NSString*) bytes2hex:(const char* ) buffer length:(int)buf_len {
size_t i;
char *p;
int len = (buf_len * 2) + 1;
p = malloc(len);
for (i = 0; i < buf_len; i++) {
p[i * 2] = hexchar[(unsigned char)buffer[i] >> 4 & 0xf];
p[i * 2 + 1] = hexchar[((unsigned char)buffer[i] ) & 0xf];
}
p[i * 2] = '\0';
NSString * result = [NSString stringWithCString:p encoding:NSUTF8StringEncoding];
free(p);
return result;
}
You may use description method to get the string representation of a token
NSLog(#"Token: [%#]", [devToken description]);
To remove non-numerical characters you may do:
NSCharacterSet *set = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
NSString *tkn = [[[devToken description] componentsSeparatedByCharactersInSet:set] componentsJoinedByString: #""];

Convert basic script to Objective C (money formatting)

I've got this basic like script that I need to convert to objective c, it turns big units of money into shortened versions (ie: 1.2m, etc), I've got most of the conversion done, but the biggest problem I'm having is right at the end.
The original basic code is:
; Basic Code
Function ShortCash$(BigNumber)
out$=""
; First, grab the length of the number
L=Len(BigNumber)
Letter$=""
;Next, Do a sweep of the values, and cut them down.
If l<13
out$=(BigNumber/1000000000)
; For each figure, out remainder should be divided so that it leaves a 2 digit decimal number..
remainder=(BigNumber Mod 1000000000)/10000000
; And we also want a letter to symbolise our large amounts..
Letter$="b" ; BILLION!!!!
EndIf
If l<10 Then out$=(BigNumber/1000000):remainder=(BigNumber Mod 1000000)/10000:Letter$="m"
If l<7 Then out$=(BigNumber/1000):remainder=(BigNumber Mod 1000)/10:Letter$="k"
If l<4 Then out$=BigNumber:remainder=0:Letter$=""
;Next, if remainder=0 then we're happy.. ie, £1m is fine, we need no decimal.
;But, if the remainder is >0 we'll want a nice rounded 2 decimal number, instead.
If remainder>0
out$=out$+"."+Right$("00"+remainder,2) ; Last two numbers..
; Additionally, if the rightmost figure is a 0, remove it.
; (ie, if the value is 1.50, we don't need the 0)
If Right$(out$,1)="0" Then out$=Left$(out$,Len(out$)-1)
EndIf
; And throw on our letter, at the end.
out$=out$+letter$
Return out$
End Function
// The following was edited on Thur 5 Aug by Author of post.
I believe I've got it sorted now, I've got the following to work for thousands for the moment, I'm not sure if it will work under all circumstances and would welcome any help/guidance on this. I am aware of the memory issues, I'll sort that out later, its the string manipulation part I am resolving first.
// This goes inside the (IBAction) update method;
NSNumber *bigNumber = nil;
if ( [inputField.text length] >0)
{
bigNumber = [NSNumber numberWithInt:[inputField.text intValue]];
}
int bigNumberAsInt = [bigNumber intValue];
NSString *bigNumberAsString = [bigNumber stringValue];
int bigNumberStrLen = [bigNumberAsString length];
NSLog(#"bigNumber = %#", bigNumber);
//NSLog(#"bigNumberAsString = %#", bigNumberAsString);
NSLog(#"bigNumberStrLen = %d", bigNumberStrLen);
NSLog(#"=========");
// =========
NSNumberFormatter *nformat = [[[NSNumberFormatter alloc] init] autorelease];
[nformat setFormatterBehavior:NSNumberFormatterBehavior10_4];
[nformat setCurrencySymbol:#"$"];
[nformat setNumberStyle:NSNumberFormatterCurrencyStyle];
[nformat setMaximumFractionDigits:0];
NSLog(#"Cash = %#", [nformat stringFromNumber:bigNumber]);
// =========
NSString *output = [[NSString alloc] init];
NSString *letter;
// ==========
// Anything less than 1m represent with a k
if (bigNumberStrLen < 7)
{
letter = #"k";
int sum = (bigNumberAsInt / 1000);
int int_remainder = ((bigNumberAsInt % 1000) / 10);
NSLog(#"Remainder = %d", int_remainder);
NSString *sumAsString = [NSString stringWithFormat:#"%d", sum];
NSString *remainderAsString = [NSString stringWithFormat:#"%d", int_remainder];
NSLog(#"Sum as String = %#", sumAsString);
NSLog(#"Remainder as String = %#", remainderAsString);
if (int_remainder >0)
{
NSLog(#"Remainder > 0");
output = [output stringByAppendingString:sumAsString];
output = [output stringByAppendingString:#"."];
output = [output stringByAppendingString:remainderAsString];
NSLog(#"Output = %#", output);
NSUInteger outputStrLen = [output length];
NSLog(#"Output strlen = %d", outputStrLen);
if ([output hasSuffix:#"0"])
{
NSLog(#"Has suffix of 0");
// Remove suffix
output = [output substringWithRange: NSMakeRange(0, outputStrLen-1)];
}
}
output = [output stringByAppendingString:letter];
NSLog(#"Final output = %#", output);
}
This will display 10.2k (if it ends with a 0 suffix) or it will display 10.2x where X is the last number.
Can someone just double check this, or perhaps there's an easier way to do all this. In either case, thanks for your help.
Just to improve the solution, a good idea is maybe to subclass the NSNumberFormatter class and override the - (NSString *)stringForObjectValue:(id)anObject method.
Using the code from zardon, I added a statement for the values < 1000 which doesn't format the number.
Here is the code of the method :
/*
Override the stringForObjectValue method from NSNumberFormatter
100 -> 100
1000 -> 1k
1 000 000 -> 1m
1 000 000 000 -> 1b
1 000 000 000 -> 1t
*/
- (NSString *)stringForObjectValue:(id)anObject {
// If we don't get a NSNumber, we can't create the string
if (![anObject isKindOfClass:[NSNumber class]]) {
return nil;
}
NSNumberFormatter *nformat = [[NSNumberFormatter alloc] init];
// Decimal value from the NSObject
double doubleValue = [anObject doubleValue];
NSString *stringValue = nil;
// Abbrevations used
NSArray *abbrevations = [NSArray arrayWithObjects:#"k", #"m", #"b", #"t", nil] ;
// If the value is less than 1000, we display directly the value
if(doubleValue < 1000.0) {
stringValue = [NSString stringWithFormat: #"%#", [nformat stringFromNumber: [NSNumber numberWithDouble: doubleValue]] ];
}
else { // Otherwise we format it as expected
for (NSString *s in abbrevations) {
doubleValue /= 1000.0 ;
if ( doubleValue < 1000.0 ) {
if ( (long long)doubleValue % (long long) 100 == 0 ) {
[nformat setMaximumFractionDigits:0];
} else {
[nformat setMaximumFractionDigits:2];
}
stringValue = [NSString stringWithFormat: #"%#", [nformat stringFromNumber: [NSNumber numberWithDouble: doubleValue]] ];
NSUInteger stringLen = [stringValue length];
if ( [stringValue hasSuffix:#".00"] )
{
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-3)];
} else if ( [stringValue hasSuffix:#".0"] ) {
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-2)];
} else if ( [stringValue hasSuffix:#"0"] ) {
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-1)];
}
// Add the letter suffix at the end of it
stringValue = [stringValue stringByAppendingString: s];
break;
}
}
}
[nformat release];
return stringValue;
}
In the interface we simply add the inheritage statement :
#interface MoneyNumberFormatter : NSNumberFormatter
Hope this helps..
.... Okay, with thanks to the author of the Cocoa Tidbits blog, I believe I have a solution which is much more elegant, faster and doesn't require so much coding; it still needs testing, and it also probably requires a little more editing, but it seems to be much better than my original.
I modified the script a little to make it not show any trailing zeros where relevant;
NSNumberFormatter *nformat = [[NSNumberFormatter alloc] init];
[nformat setFormatterBehavior:NSNumberFormatterBehavior10_4];
[nformat setCurrencySymbol:#"$"];
[nformat setNumberStyle:NSNumberFormatterCurrencyStyle];
double doubleValue = 10200;
NSString *stringValue = nil;
NSArray *abbrevations = [NSArray arrayWithObjects:#"k", #"m", #"b", #"t", nil] ;
for (NSString *s in abbrevations)
{
doubleValue /= 1000.0 ;
if ( doubleValue < 1000.0 )
{
if ( (long long)doubleValue % (long long) 100 == 0 ) {
[nformat setMaximumFractionDigits:0];
} else {
[nformat setMaximumFractionDigits:2];
}
stringValue = [NSString stringWithFormat: #"%#", [nformat stringFromNumber: [NSNumber numberWithDouble: doubleValue]] ];
NSUInteger stringLen = [stringValue length];
if ( [stringValue hasSuffix:#".00"] )
{
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-3)];
} else if ( [stringValue hasSuffix:#".0"] ) {
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-2)];
} else if ( [stringValue hasSuffix:#"0"] ) {
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-1)];
}
// Add the letter suffix at the end of it
stringValue = [stringValue stringByAppendingString: s];
//stringValue = [NSString stringWithFormat: #"%#%#", [nformat stringFromNumber: [NSNumber numberWithDouble: doubleValue]] , s] ;
break ;
}
}
NSLog(#"Cash = %#", stringValue);