Getting nil from NSNumberformater numberFromString - objective-c

I'm trying to format an amount from a .txt file coming in es_US locale(x,xxx.xx), to my current locale with is es_ES(x.xxx,xx). I would expect that [NSNumberFormater numberFromString] would just reformat the string, however and I'm only getting a nil value from this method.
I also tried another approach after checking the answers from here, but NSDecimalnumber does not work if the string has thousand separators, so if anybody could tell me what am I doing wrong please...
- (void) setSaldo_sap:(NSString *)saldo_sap
{
NSNumberFormatter *numFormatter = [[NSNumberFormatter alloc] init];
[numFormatter setLocale:[NSLocale currentLocale]];
[numFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[numFormatter setNegativeFormat:#"-¤#,##0.00"];
//saldo_sap = #" -324,234.55"
NSString * tmpString = [saldo_sap stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSNumber *num = [numFormatter numberFromString:tmpString];
NSDecimalNumber *tempNumber = [NSDecimalNumber decimalNumberWithString:tmpString];
_saldo_sap = [numFormatter stringFromNumber:tempNumber];
}

I think you misinterpret the aim of NSNumberFormatter: it doesn't "reformat", it "formats" and "parses" a numbers formatted along the set rules. So if you have numbers coming in "es_US" locale but want to format them using "es_ES" you will need two NSNumberFormatters: one for each locale.
Parse the incoming number with "es_US" and format using "es_ES", simplifying a bit (I don't know those two locales and the exact format of your numbers so you may need to tweek it a bit):
NSString * tmpString = ...
NSNumberFormatter *usFormatter = [[NSNumberFormatter alloc] init];
[usFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier: #"es_US"] autorelease]];
[usFormatter setHasThousandSeparators: YES];
NSNumberFormatter *esFormatter = [[NSNumberFormatter alloc] init];
[esFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier: #"es_ES"] autorelease]];
[esFormatter setHasThousandSeparators: YES];
// this assignment looks also somewhat weird: is it an instance variable?
// 'cause if it is and you assign an autoreleased string you'll have a bad pointer there
_saldo_sap = [esFormatter stringFromNumber: [usFormatter numberFromString: tmpString]];
// And unless you use ARC you leak your formatter on each call, so at the end
[usFormatter release];
[esFormatter release];
EDIT
If the input strings contain prefix/postfix characters, that may prevent NSNumberFormatter to work (it use usually pretty strict), use setLenient::
"Sets whether the receiver will use heuristics to guess at the number which is intended by a string."
If you have more than one number to be converted, do not create the formatters for each number, this is just a waste of memory and cpu. Make them instance variables and reuse. It will be much clearer than just having one formatter and reconfiguring it between parsing one format and formatting in another.

NSString *_saldo_sap = #" -324234.55";
//NSString *_saldo_sap = #" 324,234.55";
NSString * tmpString = [_saldo_sap stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//tmpString = #"-324,234.55"
NSNumberFormatter *numFormatter = [[NSNumberFormatter alloc] init];
NSNumber *num = [numFormatter numberFromString:tmpString];
[numFormatter setLocale:[NSLocale currentLocale]];
[numFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numFormatter setNegativeFormat:#"-¤#,##0.00"];
_saldo_sap = [numFormatter stringFromNumber:num];
Firstly, for getting NSNumber from NSString the string must be in correct readable format i.e. it must not include any characters like " , " as stated.
Secondly, you must first convert the string to NSNumber and then format it accordingly.

Related

How to get decimal values when the device is in french using NSNumber?

I have an issue with the textfield. This case works fine with the english language. However, if the device language is change to french (canada) , the textfield is not showing the values after the decimal.
For english:
I enter 99.99 and click Done. The text field display 99.99
For french:
I enter 99,99 and click Done . The text field display 99,00
Here is my code:
_amountField.text = [_amountField currencyFormatFromValue:[NSNumber numberWithDouble:_amountField.text.doubleValue]];
Here the [NSNumber numberWithDouble:_amountField.text.doubleValue] part is returning 99.99 in case of english and 99,00 in case of french
-(NSString *)currencyFormatFromValue:(NSNumber *)value{
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.positiveFormat = #"###.00";
formatter.roundingMode = NSNumberFormatterRoundFloor;
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *groupingSeparator = [[NSLocale currentLocale] objectForKey:NSLocaleGroupingSeparator];
[formatter setGroupingSeparator:groupingSeparator];
[formatter setUsesGroupingSeparator:YES];
[formatter setCurrencySymbol:#""];
[formatter setMinimumIntegerDigits:1];
NSString *formattedValue = [formatter stringFromNumber:value];
return formattedValue;
}
I am not sure, why the french text which is converted in double is terminating the decimal values. Any ideas?
Do I need to convert the text back to english before I sent the value to formatter?
I think your positiveFormat specification with the period is conflicting with NSNumberFormatterCurrencyStyle. I would try commenting out a bunch of lines and recompiling:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
// formatter.positiveFormat = #"###.00";
// formatter.roundingMode = NSNumberFormatterRoundFloor;
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
// NSString *groupingSeparator = [[NSLocale currentLocale] objectForKey:NSLocaleGroupingSeparator];
// [formatter setGroupingSeparator:groupingSeparator];
// [formatter setUsesGroupingSeparator:YES];
// [formatter setCurrencySymbol:#""];
// [formatter setMinimumIntegerDigits:1];
NSString *formattedValue = [formatter stringFromNumber:value];
return formattedValue;
In my experience, the formatting out of the box will include grouping separator, currency symbols, rounding mode, etc. Specifying it again can potentially confuse the formatter.
If you really need to explicitly specify the positive format, you can try building the positive format string using the NSLocaleDecimalSeparator constant so that it will be a period or comma as required:
NSString *decimalSeparator = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];
formatter.positiveFormat = [NSString stringWithFormat:#"###%#00", decimalSeparator];
But I don't recommend it!
I found a way to make it work. I need to remove the , character in case of french that occur in the text field and send to formatter .
_amountField.text = [_amountField currencyFormatFromValue:[NSNumber numberWithDouble:[_amountField.text strippedAmount]doubleValue]];
and
//macro
#define IsFrenchLanguage() ([[[NSLocale preferredLanguages] objectAtIndex:0] rangeOfString:#"fr"].location != NSNotFound)
-(NSString *) strippedAmountString
{
NSString * thousandsSeparator = IsFrenchLanguage() ? #" " : #",";
return [self.amountField.text stringByReplacingOccurrencesOfString:thousandsSeparator withString:#""];
}

Convert array of NSStrings to array of formatted NSNumbers

I have an array of strings called valuesArray containing values like this: 2913451.0938
I am trying to format those numbers so that I can display them like this: 2,913,451.09
Using the following code I am able to read the values from the array and convert them to NSNumbers (num), and I am also able to create a formatter to define how I want my numbers to be displayed (formatter).
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:2];
NSNumber *num = valuesArray[indexPath.row];
NSLog(#"num: %#",num);
NSLog(#"Formatter: %#",formatter);
NSString *forNum = [formatter stringFromNumber:num];
NSLog(#"FormattedNum: %#",forNum);
When I run the code and get to the line NSLog(#"FormattedNum: %#",forNum); I see that it prints null. What am I missing?
The problem in your code is that you retrieve an element from you array of strings valueArray but assign it to an NSNumber typed variable—while the object really is an NSString. When you pass it to the formatter it returns nil (even though it also might crash, it's just undefined behavior).
You have to convert the string to an NSNumber:
NSNumber *num = #([valuesArray[indexPath.row] doubleValue]);
I just checked this code with this value :
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:2];
NSNumber *num = #(2913451.0938);
NSLog(#"num: %#",num);
NSLog(#"Formatter: %#",formatter);
NSString *forNum = [formatter stringFromNumber:num];
NSLog(#"FormattedNum: %#",forNum);
It worked fine, no error what so ever as you mentioned.
See the Output :
2013-02-28 21:35:44.417 BrowserModal[4861:403] num: 2913451.0938
2013-02-28 21:35:44.418 BrowserModal[4861:403] Formatter: <NSNumberFormatter: 0x100160840>
2013-02-28 21:35:44.419 BrowserModal[4861:403] FormattedNum: 29,13,451.09
Please clean your target and re-build.
And
Make sure valuesArray[indexPath.row] returns a boxed NSNumber object.
Do as : #([valuesArray[indexPath.row] doubleValue]);

NSNumberFormatter, appending decimal to calculator from UI

Ok, so I am writing a calculator app now. So far, I'm not having much luck in regard to decimals (my most recent approach hasn't worked well).
-(void) DecimalAdded
{
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setAlwaysShowsDecimalSeparator:YES];
[formatter setGeneratesDecimalNumbers:YES];
[formatter setDecimalSeparator:#"."];
//first convert the float value of CN into NSnumber
NSNumber *nextstepNumFromCNF= [NSNumber numberWithFloat:currentNumber];
//now we have to convert that number into a string
NSString *CNconverted = [formatter stringFromNumber:nextstepNumFromCNF];
NSNumber *CNdecmAddedAndReadyForPars = [formatter numberFromString:CNconverted];
currentNumber = currentNumber*10 + [CNdecmAddedAndReadyForPars floatValue];
CalculatorScreen = [NSMutableString stringWithFormat: #"%#", CNconverted];
I can append the string to the Calculator screen I can say the number is 1, I see "1." as I'm typing. However this is usually converted to 1 during th float conversion (which is correct).
What is this best way to accomplish this?

Converting Decimal to String to Decimal

I copied this code from another post. I tried the example, however, I am getting a EXEC_BAD_ACCESS. From what I have read, this error happens when trying to use an object that has been deallocated, but I just don't see where I am doing that:
The call
...
float weighted_average = num_of_passes / total_of_all_passes;
NSString *newNumber = [[NSString alloc] init];
newNumber = [self formattedStringWithDecimal:weightedAverage]; //weighted average (float) = 15.875145
...
The Function
- (NSString *)formattedStringWithDecimal:(NSDecimalNumber *)decimalNumber
{
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setMaximumFractionDigits:2]; //two deimal spaces
[formatter setRoundingMode: NSNumberFormatterRoundHalfUp]; //round up
NSString *result =[NSString stringWithString:[formatter stringFromNumber:decimalNumber]];
[formatter release];
return result;
}
Call the method like this:
newNumber = [self formattedStringWithDecimal:[NSDecimalNumber numberWithFloat:15.434]];
You've tried to pass a primitive, but the method expects an object: an NSDecimalNumber. You've got to use the static convenience method numberWithFloat to create an object of that type.
And by the way, I have the feeling that
newNumber = [NSString stringWithFormat#"%.2f", 15.434];
could achieve the same result with less lines of code. Note this will not round up your number though.
You are returning an autoreleased object. Does the function that uses it retain it? If not, it could be released and then later (later run loop) its trying to be (re)used. Agreed on the enabling zombies to spot that kind of thing.

Formatting a number to show commas and/or dollar sign

I want to format my UILabel with commas or better with a dollar sign and commas (with no decimal).
Here is the code I am using:
IBOutlet UILabel *labelrev
float rev = (x + y)
labelrev.text = [[NSString alloc] initWithFormat:#%2.f",rev];
I get xxxxxxxxx as the output I want to get xxx,xxx,xxx or $xxx,xxx,xxx
How do I do that?
You should definitely use NSNumberFormatter for this. The basic steps are:
Allocate, initialize and configure your number formatter.
Use the formatter to return a formatted string from a number. (It takes an NSNumber, so you'll need to convert your double or whatever primitive you have to NSNumber.)
Clean up. (You know, memory management.)
This code sets up the number formatter. I've done everything that you want except the currency bit. You can look that up in the documentation.
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *groupingSeparator = [[NSLocale currentLocale] objectForKey:NSLocaleGroupingSeparator];
[formatter setGroupingSeparator:groupingSeparator];
[formatter setGroupingSize:3];
[formatter setAlwaysShowsDecimalSeparator:NO];
[formatter setUsesGroupingSeparator:YES];
Next, you want to set up your number and return a formatted string. In your case, we wrap a double in an NSNumber. I do it inline, but you can break it up into two steps:
NSString *formattedString = [formatter stringFromNumber:[NSNumber numberWithFloat:rev];
Don't forget to clean up!
[formatter release];
A quick note about localization:
The NSLocale class provides some useful info about the user's locale. In the first step, notice how I used NSLocale to get a localized grouping separator:
NSString *groupingSeparator = [[NSLocale currentLocale] objectForKey:NSLocaleGroupingSeparator];
(Some countries use a full-stop/period, while others use a comma.) I think there's a way to get a localized currency symbol as well, but I'm not one hundred percent sure, so check the documentation. (It depends upon what your trying to do.)
You will need to use a NSNumberFormatter which supports currency.
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSLog(#"%#", [currencyFormatter stringFromNumber:[NSNumber numberWithInt:10395209]]);
[currencyFormatter release];
Prints: $10,395,209.00
[formatterCurrency setMaximumFractionDigits:0]
is only way to trancate decimal digits and decimal separator in a NSNumberFormatterCurrencyStyle formatter.
NSNumberFormatter *formatterCurrency;
formatterCurrency = [[NSNumberFormatter alloc] init];
formatterCurrency.numberStyle = NSNumberFormatterCurrencyStyle;
[formatterCurrency setMaximumFractionDigits:0];
[formatterCurrency stringFromNumber: #(12345.2324565)];
result
12,345 $