Custom Formatting of an NSNumber - iphone

in an ios application, I am trying to allow the user to enter a number and a format and I want to get a string with that number formatted as the user specified:
For that I have tried this:
- (NSString *)formatNumber:(NSNumber *)number withFormat:(NSString *)format
{
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setPositiveFormat:format];
NSString *formatedString = [formatter stringFromNumber:number];
return formatedString;
}
but if I try to call something like this:
NSNumber *number = [NSNumber numberWithDouble:1234567890];
NSString *string = [self formatNumber:number withFormat:#"###-###-####"];
I was expecting to get a result: 123-456-7890
But it is not working.
is the #"###-###-####" format wrong syntax ? or am I using the NSNumberFormatter wrong?
Thanks for any help
PS: I do not want to hard code an algorithm to loop through the characters and insert the dashes (-) because that format is just an example as the user is able to freely change it.
Thanks
EDIT
SORRY while writting my code here I missed a line. now added it

This may be useful:
https://discussions.apple.com/thread/2399192
Key quote:
Parentheses and dashes are not allowed in a number format.

Related

NSNumberFormatter-DecimalStyle-When editing the formatter number, its result to 0

I used below code to format the entered characters to decimal style, I use the below code in textFieldShouldEndEditing:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
long long num = [[formatter numberFromString:textField.text] longLongValue];
NSString *formattedOutput = [formatter stringFromNumber:[NSNumber numberWithLongLong:num ]];
textField.text = formattedOutput;
[formatter release];
I have an issue in above code is, first time its work normally but when we going back to the textfield and edited, and coming out of it , then the value changes to 0,
For example, I entered an initial value of 123456789 and correctly got the output 123,456,789. If I then re-enter and edit the text field to 123,456,78 the field is then populated with 0,
when it should have been 12,345,678.
NSNumberFormatter depends on the text going into it being properly formatted.
So when you delete the last digit from your number... that is, where the number now looks like:
123,456,78
The number is now not correctly formatted and NSNumberFormatter assumes it's not a valid number and returns a zero.
If you want to get foolproof long long values from your text field, remove the commas before converting (while validating the text field to make sure it's only 0-9 digits in there) and you should be good to go.

Formatting number with NSNumberFormatter that already has decimals and thousand seperators

I have these number formats:
100000,459
100000459
100.000
100.000,59
100.000.000,39
The number changes as the user input values to it. And for every value added, I need to re-format the number with NSNumberFormatter. The problem is that this number already has . and , and the formatter does not seem to handle these correctly. It comes out like this:
100 000,00
100 000 459,00
100,00
100,00
100,00
E.g. I want 100000,459 to become 100 000,459.
The code I use now:
NSNumber *amount = [NSNumber numberWithInt:[string intValue]];
NSNumberFormatter *currencyFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"nb_NO"];
[currencyFormatter setLocale:locale];
NSString *commaString = [currencyFormatter stringForObjectValue:amount];
How can I format already formatted numbers?
In the last line you are trying to format a string rather than a number. Try this:
NSString *commaString = [currencyFormatter stringFromNumber:amount];
Here is a good reference.
I've looked at that specific reference. It differs from my problem because in my case I may already have decimals when formatting.
NSNumberFormatter formats a number to a string representation and it needs the correct representation of the number to be able to format it as expected. By the look of your code sample you are providing the formatter with an integer value and that is why you are getting the output you are getting. With string = #"100000,459" [string intValue] gives you 100000. Which is why the output from the formatter is 100 000,00.
What you need to do is to first get the string into a double representation. That can be achieved by using NSScanner. You just have to make sure that you scan the numbers in the correct locale so the thousand separators are detected properly. Your first string can be converted to a double by:
NSScanner *scanner = [NSScanner scannerWithString:#"100000,459"];
[scanner setLocale:locale];
double val;
[scanner scanDouble:&val];
NSNumber *amount = [NSNumber numberWithDouble:val];
How to properly handle the thousand separators I don't know.

How to convert string with number to NSDecimalNumber that has a comma not a decimal point?

I have an interface giving me numbers like this 0000000012345,78
So i figured out how to make a number out of them. But I need to calculate with that number and what I actually need is a decimal number.
NSNumberFormatter *fmtn = [[NSNumberFormatter alloc] init];
[fmtn setFormatterBehavior:NSNumberFormatterBehavior10_4];
[fmtn setNumberStyle:NSNumberFormatterDecimalStyle];
[fmtn setDecimalSeparator:#","];
NSNumber *test = [fmtn numberFromString:#"0000000012345,78"];
How can I make my NSNumber to a NSDecimalNumber?
EDIT: This is the code I ended up with:
NSDictionary *localeDict = [NSDictionary dictionaryWithObject:#"," forKey:#"NSDecimalSeparator"];
NSDecimalNumber *test = [[NSDecimalNumber alloc] initWithString:#"00000000012345,78" locale:localeDict];
How to put together the locale dictionary could not be described as "well documented" and it took me some googling to find an example.
This one also worked:
NSLocale *deLoc = [[NSLocale alloc] initWithLocaleIdentifier:#"de"];
NSDecimalNumber *testd = [NSDecimalNumber decimalNumberWithString:#"00000000012345,78" locale:deLoc];
To convert an NSNumber to NSDecimalNumber, wouldn't it make more sense to avoid the character representation altogether with this code?
NSNumber* source = ...;
NSDecimalNumber* result = [NSDecimalNumber decimalNumberWithDecimal:[source decimalValue]];
If you check out the NSDecimal Class Reference, you'll see you can create new NSDecimalNumbers from NSStrings (with and without a locale), actual numbers, etc.
If you wanted to convert an NSNumber to an NSDecimalNumber, you could do something like this:
NSDictionary *locale = ...;
NSNumber *number = ...;
NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithString:[number descriptionWithLocale:locale] locale:locale];
Of course, you'll have to correctly create the locale, and such, but that's an exercise left up to you (it might be handy to check out the NSNumber Class Reference, the NSLocale Class Reference, and the Locales Programming Guide).
[NSDecimalNumber decimalNumberWithString:#"0000000012345,78"];
Use caution about the locale, though; if you run that code on an iPhone whose region format is not set to French, it might not return what you expect. So you might want to use:
+ (NSDecimalNumber *)decimalNumberWithString:(NSString *)numericString locale:(NSDictionary *)locale
instead.

Using NSNumberFormatter to get a decimal value from an international currency string

It seems that the NSNumberFormatter can't parse Euro (and probably other) currency strings into a numerical type. Can someone please prove me wrong.
I'm attempting to use the following to get a numeric amount from a currency string:
NSNumberFormatter *currencyFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *currencyNumber = [currencyFormatter numberFromString:currencyString];
This works fine for UK and US currency amounts. It even deals with $ and £ and thousands separators with no problems.
However, when I use it with euro currency amounts (with the Region Format set to France or Germany in the settings app) it returns an empty string. All of the following strings fail:
12,34 €
12,34
12.345,67 €
12.345,67
It's worth noting that these strings match exactly what comes out of the NSNumberFormatter's stringFromNumber method when using the corresponding locale.
Setting the Region Format to France in the settings app, then setting currencyNumber to 12.34 in the following code, results in currencyString being set to '12,34 €' :
NSNumberFormatter *currencyFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *currencyString = [currencyFormatter stringFromNumber:currencyNumber];
It would obviously be fairly easy to hack around this problem specifically for the Euro but I'm hoping to sell this app in as many countries as possible and I'm thinking that a similar situation is bound to occur with other locales.
Does anyone have an answer?
TIA, Duncan
This is a difficult one. Doing the following works without issues:
double moneyAmount = 1256.34;
NSLocale *french = [[NSLocale alloc] initWithLocaleIdentifier:#"fr_FR"];
NSNumberFormatter *currencyStyle = [[NSNumberFormatter alloc] init];
[currencyStyle setLocale:french];
[currencyStyle setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *amount = [NSNumber numberWithDouble:moneyAmount];
NSString *amountString = [currencyStyle stringFromNumber:amount];
NSNumber *pAmount = [currencyStyle numberFromString:amountString]; // == 1256.34
However, the output of the -currencyGroupingSeparator method returns the string \u00a0, a non-breaking space, rather than the expected ,.
The formatter classes (as well as the regex classes, among others) on OS X / iOS make use of the ICU library, also used by other projects. I found at least two other bug reports with regards to this particular issue. It appears that this is expected behaviour, though. (I spent some time sifting through the ICU source to find where this is defined, but it is a labyrinth.)
My recommendation, for the time being, would be to pass -setLenient:YES to the currency formatter. Perhaps even file a bug report with Apple's radar.
Have you tried setting the region to France in settings and then attempting to get the numeric amount for the string? Or you could manually set the local of the number formatter with setLocale:. NSNumberFormatter uses whatever locale it has to figure out what the numbers is a string are. If you just need the user to be able to put in a localized currency string for their area, change the system to that locale and then test. If you need the user to be able to put in multiple formats then add some sort of selection system so you know what they are putting in.
I verified that (at least on iOS 4.3) using the [formatter setLenient:YES] flag worked for me using Euros. Without that flag set, the formatter does not return correct values for Euros.
Its working code in Swift..
let formatter = NumberFormatter()
formatter.usesGroupingSeparator = true
formatter.locale = Locale(identifier: "de_DE")
formatter.numberStyle = NumberFormatter.Style.currency
formatter.string(from: NSNumber(floatLiteral: InputAsDoubleValue))!
Identifiers:
for German - "de_DE"
for US - "en_US"
for France - "fr_FR"
The formatted value would also be returned with the respective currency symbol.
I have an ugly answer using NSScanner
NSArray * testStrings = [NSArray arrayWithObjects:#"12,34 €", #"12,34" ,#"12.345,67 €" ,#"12.345,67", nil];
NSCharacterSet * charset = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
for(NSString * s in testStrings)
{
s = [s stringByReplacingOccurrencesOfString:#"." withString:#""];
s = [s stringByReplacingOccurrencesOfString:#"," withString:#"."];
NSScanner * scanner = [NSScanner scannerWithString:s];
[scanner setCharactersToBeSkipped:charset];
float f;
[scanner scanFloat:&f];
NSLog(#"float output is %.2f",f);
}
output:
2011-05-05 12:56:25.460 ScannerTest[10882:903] float output is 12.34
2011-05-05 12:56:25.473 ScannerTest[10882:903] float output is 12.34
2011-05-05 12:56:25.474 ScannerTest[10882:903] float output is 12345.67
2011-05-05 12:56:25.475 ScannerTest[10882:903] float output is 12345.67
With the default settings (lenient = NO), then it will only convert strings to numbers if they are formatted exactly as the strings it produces when formatting numbers to strings.
So in case you want to use non-lenient conversions, you should be aware that in many locales (all?) this means that spaces in the input string must be non-breaking (\u00a0). To wit:
NSString *amount = #"10 000,00 €";
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterCurrencyStyle;
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier: #"fr_FR"];
NSNumber *number = [currencyFormatter numberFromString: amount];
This will not produce the expected result, as the number variable will be nil. To get the desired result, use non-breaking spaces in both places of the string (as a thousands separator and a currency symbol separator):
amount = #"10\u00a0000,00\u00a0€";
Then the conversion will happen as expected.
Of course, if you wish to use lenient conversions, then that is the easiest way to go, and it will probably be the best option to handle human input.
On the other hand, when handling machine-generated data, a lenient interpretation of the strings may not be desirable as you will lose the ability to check the correctness of the input data.
The way to do this in Swift is:
var locale = NSLocale.currentLocale()
var formatter = NSNumberFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US")
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
var moneyDouble = formatter.numberFromString(textField.text)?.doubleValue
Assuming the textField.text is the number being entered.

How to obtain an unformatted string representation of an NSDecimal or NSDecimalNumber?

I have an NSDecimal and need this as technical string, i.e. with no formatting in any way. Floating point should be a "." if there is any, and minus sign should just be a "-" if there is any. besides that, there should be no formatting going on like grouping or chinese numbers.
I was looking for 2 hours through the SDK but it seems there is nothing simple to accomplish this. Are there solutions for this?
For NSDecimal, you can use NSDecimalString with a specified locale:
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
NSString *decimalString = NSDecimalString(&decimalValue, usLocale);
[usLocale release];
The U.S. locale uses a period for the decimal separator, and no thousands separator, so I believe that will get you the kind of formatting you're looking for.
As others have pointed out, for an NSDecimalNumber you can use the -descriptionWithLocale: method with the above-specified U.S. locale. This doesn't lose you any precision.
NSDecimalNumber
NSLog(#"%#", [theNumber stringValue]);
NSDecimal
NSLog(#"%#", NSDecimalString(&theDecimal, nil));
NSDecimalNumber is a subclass of NSNumber which has the -stringValue method.
stringValue
Returns the receiver’s
value as a human-readable string.
- (NSString *)stringValue
Return Value
The receiver’s value as a
human-readable string, created by
invoking descriptionWithLocale: where
locale is nil.
descriptionWithLocale:
Returns a string that represents the contents of
the receiver for a given locale.
- (NSString *)descriptionWithLocale:(id)aLocale
Parameters aLocale
An object
containing locale information with
which to format the description. Use
nil if you don’t want the description
formatted.
Just call [theNumber stringValue].
Use decimalNumberWithString from the NSDecimalNumber class:
NSDecimalNumber *dn = [NSDecimalNumber decimalNumberWithMantissa:12345
exponent:-100
isNegative:YES];
NSDictionary *local = nil;
NSString *ds = [dn descriptionWithLocale: local];
NSLog(#"dn: %#", dn);
NSLog(#"ds: %#", ds);
dn: -0.00000000000000000000000 … 0000000000000000000000000000000000012345
ds: -0.00000000000000000000000 … 0000000000000000000000000000000000012345