Developer Forums relocated!

Need help with Apple Developer tools and technologies? Want to share information with other developers and Apple engineers? Visit Developer Forums at Apple.

Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

NSNumberFormatter not working for me ?

I am calling a method that I pass a phone number string "1234567890" and should return "(123) 456-7890" but sure enough, that is not what is happening...

Any Ideas ?

Bil Hernandez
Plano, Texas

I created a simple demo below :

$ clang_gen
---> shows : ** BUILD SUCCEEDED **

Build and Analyze shows :
---> Build Succeeded
---> No Issues

NSLog shows :
[Switching to process 2491]
Running…
(gdb) continue
Current language: auto; currently objective-c
2010-04-13 15:04:01.829 Formatter[2491:a0f] [4625] theString = 1234567890
2010-04-13 15:04:01.832 Formatter[2491:a0f] [4626] phoneNumber = 1234567890
kill
quit

The Debugger has exited with status 0.


// ---------+---------+---------+---------+---------+---------+---------+---------
// BHUtility.h
// ---------+---------+---------+---------+---------+---------+---------+---------
#import <Cocoa/Cocoa.h>
@interface BHUtility : NSObject
{
}
+ (NSString *)bhFormatNumberString:(NSString *)aNumberString withFormat:(NSString *)aFormat;
@end



// ---------+---------+---------+---------+---------+---------+---------+---------
// BHUtility.m
// ---------+---------+---------+---------+---------+---------+---------+---------
#import "BHUtility.h"
@implementation BHUtility
// ---------+---------+---------+---------+---------+---------+---------+---------
+ (NSString *)bhFormatNumberString:(NSString *)aNumberString withFormat:(NSString *)aFormat
{
NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
// NSFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[numberFormatter setFormat:aFormat]; // specify just positive values format

NSInteger theInt = [aNumberString intValue];
NSNumber *theNum = [NSNumber numberWithInt:theInt];
NSString *theString = (NSString *)[numberFormatter stringFromNumber:theNum];
NSLog(@"[4625] theString = %@", theString);

return theString;
// ---------+---------+---------+---------+---------+---------+---------+---------
}
@end



// ---------+---------+---------+---------+---------+---------+---------+---------
// BHFormatter.h
// ---------+---------+---------+---------+---------+---------+---------+---------
#import <Cocoa/Cocoa.h>
@interface BHFormatter : NSObject
{
}
@end



// ---------+---------+---------+---------+---------+---------+---------+---------
// BHFormatter.m
// ---------+---------+---------+---------+---------+---------+---------+---------
#import "BHFormatter.h"
#import "BHUtility.h"
@implementation BHFormatter
// ---------+---------+---------+---------+---------+---------+---------+---------
- (void)awakeFromNib
{
// I have already run a routine to strip all non digits by this time
NSString *strippedNumber = @"1234567890";

NSString *phoneNumber;
phoneNumber = [BHUtility bhFormatNumberString:strippedNumber withFormat:@"(###) ###-####"];
NSLog(@"[4626] phoneNumber = %@", phoneNumber);
}
// ---------+---------+---------+---------+---------+---------+---------+---------
@end

iMacG5, Mac OS X (10.6.2)

Posted on Apr 13, 2010 1:35 PM

Reply
7 replies

Apr 13, 2010 9:04 PM in response to Bill_Hernandez

I found a better work around, that allows for extremely flexible formatting: I didn't do much in yhr way of error trapping, but you can add some more if what I put in this is not enough for you...

This is actually a really nice number formatting method that will go in my BHUtility.m

You can format numbers to strings any way you want. You can use it to format more than phone numbers, use your imagination...

Notice that the first three results are 10 digits, after I had stripped out all non-digits, and the fourth example contains 14 digits after all the junk chars are stripped out. I wrote a couple of methods that strip out leading, trailing spaces, converts multiple spaces to single spaces.

The formatters also do capitalization after cleanup, convert middle names to capitalized middle initials with a period on the end, convert state names to short state initials, etc.

In this case the formatter strips out everything except digits

If anybody is interested in these formatter methods, I can post them.

Thanks again,

Bill Hernandez
Plano, Texas

RESULTS:


Loading program into debugger…
Program loaded.
run
[Switching to process 6148]
Running…
2010-04-13 22:29:15.777 Formatter[6261:a0f] [4626] returnString = (123) 456-7890
2010-04-13 22:29:15.779 Formatter[6261:a0f] [4626] returnString = 123.456.7890
2010-04-13 22:29:15.779 Formatter[6261:a0f] [4626] returnString = <[123.456.7890]>
2010-04-13 22:29:15.780 Formatter[6261:a0f] [4626] returnString = (123) 456-7890 [extension 1234]
kill
quit
The Debugger has exited with status 0.



// ---------+---------+---------+---------+---------+---------+---------+---------
- (void)awakeFromNib
{
// I have already run a routine to strip all non digits by this time
NSString *strippedNumber = @"1234567890";
NSString *phoneNumber;
BOOL useFormatter = YES;
if(useFormatter)
{
phoneNumber = [BHUtility bhFormatNumberString:strippedNumber withFormat:@"(###) ###-####"];
phoneNumber = [BHUtility bhFormatNumberString:strippedNumber withFormat:@"###.###.####"];
phoneNumber = [BHUtility bhFormatNumberString:strippedNumber withFormat:@"<[###.###.####]>"];
strippedNumber = @"12345678901234"; // notice this is 14 digits long now
if([strippedNumber length] == 14)
{
phoneNumber = [BHUtility bhFormatNumberString:strippedNumber withFormat:@"(###) ###-#### [extension ####]"];
}
else
{
phoneNumber = strippedNumber;
}
}
else
{
// THIS IS NOT VERY FLEXIBLE
if([strippedNumber length] == 10)
{
NSString *areacode = [strippedNumber substringWithRange:NSMakeRange(0, 3)];
NSString *exchange = [strippedNumber substringWithRange:NSMakeRange(3, 3)];
NSString *number = [strippedNumber substringWithRange:NSMakeRange(6, 4)];
phoneNumber = [NSString stringWithFormat:@"(%@) %@-%@", areacode, exchange, number];
}
else
{
phoneNumber = strippedNumber;
}
}
NSLog(@"[4626] phoneNumber = %@", phoneNumber);
}
// ---------+---------+---------+---------+---------+---------+---------+---------



// ---------+---------+---------+---------+---------+---------+---------+---------
+ (NSString *)bhFormatNumberString:(NSString *)aNumberString withFormat:(NSString *)aFormat
{
NSString *myFormatString = [[NSString stringWithString:aFormat] copy];
NSUInteger myFormatStringLength = [myFormatString length];
NSMutableString *returnString = [[[NSMutableString alloc] initWithCapacity:myFormatStringLength] autorelease];
NSUInteger i = 0; // i represents the myFormatString character position/counter through the loop
NSUInteger aNumberStringPosition = 0;
unichar myUniChar;
for ( i = 0; i < myFormatStringLength; i ++ )
{
myUniChar = [myFormatString characterAtIndex:i];
// NSLog (@"[4525] i = %i: myUniChar = %C ", i, myUniChar);
if (myUniChar == '#')
{
// aNumberStringPosition += 1;
myUniChar = [aNumberString characterAtIndex:aNumberStringPosition++];
}
[returnString appendFormat:@"%C",myUniChar];
}
NSLog(@"[4626] returnString = %@", returnString);
return returnString;
}
// ---------+---------+---------+---------+---------+---------+---------+---------


Message was edited by: Bill_Hernandez

Apr 15, 2010 2:04 PM in response to Bill_Hernandez

Here are some observations and follow up on what I have found :

Murat,

Funny, how you and I have come to some similar ideas...

During the night I was thinking that with the limitations placed on NSNumberFormatter, compared to other num2string formatters I have used over time, it should have probably been called NSCurrencyDisplayFormatter, or something more limited than what NSNumberFormatter implies.

Floating point vars know nothing about dollar signs, commas, etc. They are stored as a float internally, but are displayed in an NSTextField , or provided to a string, with either no string formatting as "43.75", or with some display formatting such as "$ 43.75". The point is that NSNumberFormatter appears to me missing some flexibility in dealing with other than its own set of formatting converters, whatever the programmers thought they would need at the time they wrote it. I looked at a the header file for the class and it is gigantic, it is an amazing piece of work.

So even when you take numeric values from a couple of NSTextFields and perform some calculation, the display of those fields is purely string representation of the underlying values, but the minute you begin doing calculations, you are back working with the floats, ints, etc. Some systems handle this automatically for you, some require you to deal with that conversion, keeping track of what's what, etc.

It's OK that the NSNumberFormatter doesn't have that capability, or at least that I havent found it, work-arounds have always been a specialty for me, so that doesn't bother me in the least, I was just trying to explore my way around Cocoa. The NSDateFormatter has worked very well for me, and showed no such weakness.

As I mentioned before when displaying the {string, text} representation of the underlying {item, object} which could be {floats, doubles, ints, etc} the format is applied, and the result is used for display purposes, whether it be to the screen, printer, etc., and obviously the formatting does not change in any way the underlying object.

Assuming there's a method to trigger the conversion, its job is to apply the format for conversion from object to display representation, and if you pass the method an NSNumber, and an NSNumberFormatter, then regardless of the source of that NSNumber, or its intended use, the result should be in the format that you provide. The formatter should not care, nor does it know anything about what the number is going to be used for, or where it came from. It does NOT know, whether you are going to use that number to {add, subtract, etc} or whatever. It's only function in life is to take a properly structured number in the form of an NSNumber Object, whether that NSNumber came from a phoneNumber string, a calculation, etc. It just doesn't matter, its source is irrelevant, and its target is also irrelevant.

NSString *aNumberString = @"1234567890";
NSString *format = @"(###) ###-####";
NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[numberFormatter setFormat:format]; // specify just positive values format
NSInteger theInt = [aNumberString intValue];
NSNumber *theNum = [NSNumber numberWithInt:theInt];
NSString *theResultString = (NSString *)[numberFormatter stringFromNumber:theNum];

The above should work, and the only way this scenario to fail is if there are additional assumptions/limitations, as to what is allowable within the format string, and with the type of variable you are dealing with, as shown below.

Using (numberWithInt:theInt), the results show that the class tries to protect you from your own bufoonery. It senses that you used an NSNumber with integer contents, and ignores the ".##" portion of the format string. So it is doing more checking, and prevents you from displaying an integer as a float, unless of course you force the format as you can see below :

from : format = @"##,###,###.##";
to : format = @"##,###,###.00";
NSUInteger theInt = [aNumberString intValue];
NSNumber *theNum = [NSNumber numberWithInt:theInt];
NSString *aNumberString = @"1234567890";
NSString *format = @"##,###,###.##";
2010-04-15 13:09:34.646 Formatter[771:a0f] [4625] theString = 1,234,567,890

in which case it will let you do that :

NSString *aNumberString = @"1234567890";
NSString *format = @"##,###,###.00";
2010-04-15 13:09:34.646 Formatter[771:a0f] [4625] theString = 1,234,567,890.00


------------------------------------------------------------------------------

If I change the following from (numberWithInt:theInt) to (numberWithDouble:theDouble) then the outcome is correct :

// NSUInteger theInt = [aNumberString intValue];
// NSNumber *theNum = [NSNumber numberWithInt:theInt];
double theDouble = [aNumberString doubleValue];
NSNumber *theNum = [NSNumber numberWithDouble:theDouble];
NSString *theString = (NSString *)[numberFormatter stringFromNumber:theNum];

--------------------------
and yields :

NSString *aNumberString = @"1234567890.99";
NSString *format = @"$ #,###,###,###.##";
2010-04-15 13:43:25.977 Formatter[1337:a0f] [4625] theString = $ 1,234,567,890.99

Which is correct...
------------------------------------------------------------------------------
This next format, as well as some of the others I attempted fail as well (so as an observation, and not an absolute truth), apparently NSNumberFormatter will not let you use other than accepted currency formatting characters {$, " ", ",", ".", -} {$, spaces, commas, periods, minus signs, etc} within the format.

NSString *aNumberString = @"1234567890.99";
NSString *format = @"####_######.##";
2010-04-15 14:13:46.115 Formatter[1442:a0f] [4625] theString = 1234567891

which of course is incorrect.
------------------------------------------------------------------------------

Anyway, I finally got this to work, but conclude that primarily from what I've been able to figure out, NSNumberFormatter, unless someone can show otherwise really is more of an NSCurrencyDisplayFormatter, which is OK, its just fine. I just needed to understand how to make it work for me...

This whole exercise for me only had to do with trying to learn about one more little area of Cocoa, and regardless of what else can be said, there was definitely learning involved.

Obviously the whole Cocoa environment is mind boggling, it is an incredible feat, I cant even begin to fathom the vision, to reality, and who am I to question such an amazing piece of work. I am trying to find my way to doing something useful with it.

Beyond that, I am sure somebody knows the big picture, and has actually seen it. I spoke to someone the other day about the learning curve with Cocoa, and they said it was a short trip, somewhere around 20 parsecs, so there is hope...

http://www.wisegeek.com/what-is-a-parsec.htm

Thanks to everybody who helped...

Bil Hernandez
Plano, Texas
------------------------------------------------------------------------------

From: Chris Kane <ckane@apple.com>
Subject: Re: NSNumberFormatter not working for me ?
Date: April 15, 2010 1:38:05 PM CDT
To: Bill Hernandez <ms@mac-specialist.com>
Cc: list-cocoa-dev <cocoa-dev@lists.apple.com>

I think people are confusing two issues, one being the abstract "phone numbers aren't numbers and NSNumberFormatter is only for quantities", and the other is the reason you don't get what you want out of NSNumberFormatter, in trying to explain. Let me try to explain the latter as directly as possible.

In this kind of example of the result you want:


format = @"(###) ###-####"
result = @"(123) 456-7890"


You're trying to format a number object with NSNumberFormatter and get the formatter to put junk in the middle of the number (digit sequence). NSNumberFormatter does not support formats putting junk in the middle of the number, except for a limited set involving the thousands separator and the decimal point.

You're already doing the right thing for what you want to do by writing your own algorithm. You could mold that into the form of an NSFormatter subclass then, if that is useful to you, or just keep it off to the side as a helper function/method.

Chris Kane
Cocoa Frameworks, Apple
------------------------------------------------------------------------------

From: Bill Hernandez <ms@mac-specialist.com>
Subject: Re: NSNumberFormatter not working for me ?
Date: April 15, 2010 3:23:59 PM CDT
To: list-cocoa-dev <cocoa-dev@lists.apple.com>

Chris,

Thanks for stepping in...

I had thought about some of this every time I woke up during the night, and began composing a message this morning that I just posted, in reference to my findings.

I posted the message, checked the emails, and found your response, which agreed with my observations.

In the end the string2num formatter NSNumberFormatter is more of a subset of a true number formatter, it really should be called a currency formatter, perhaps NSCurrencyFormatter, or NSCurrencyDisplayFormatter. Not that I care, now that I understand what its target audience, and its functional scope is.

I was trying to use it within a broader scope than what it was designed for, and that is OK, I can certainly do workarounds. I just wanted to understand and use what was available instead of reinventing whatever...

It took me a while to figure this out, because I am used to number2string formatters that handle currency, as well as custom formats of all types.

Since I am learning the Cocoa Framework, I prefer to use what is available, but I also love and thrive on work-arounds...

Thanks for clarifying and confirming my understanding...

Bill Hernandez
Plano, Texas

NSNumberFormatter not working for me ?

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.