NSString: remove leading 0s so '00001234' becomes '1234'? [duplicate] - objective-c

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Removing leading zeroes from a string
I need to remove leading 0s in an NSString, a quick way I can think of is to convert the string to an NSNumber then convert back to NSString which will give me the clean string, although I'm not sure if it works. Is there any other way to do this?
Thanks

So just for fun, I decided to time the various ways of doing this. This was by no means a scientific test, done mostly for my amusement, but some of you might like to see it anyway.
I created a method and substituted the various routines to process strings representing the numbers 0 - 100,000 all with three leading 0's.
Keep in mind however, that even the slowest method is going to be perfectly acceptable if you only have one (or even one hundred) of these strings to trim. (Using the number formatter takes about .000012039905 seconds, or about 1/100th of a millisecond.) Other things such as how easy your code is to read and understand is usually more important unless you really do need to process a large file full of strings like this. My personal favorite is still the regular expression, because it is relatively fast and immediately obvious what you are trying to accomplish, even without documentation.
Here are the results (from fastest to slowest):
Looping through the string
// I tried this by looping through the utf8String and got the same times
// (actually, ever so slightly longer, probably since it had to create the c string)
//
// I did find that using `[string getCharacters:buffer range:range]` and
// iterating over the character buffer that it took another 0.01 seconds
// off the time. Hardly worth it though. :)
NSUInteger length = [string length];
for (NSUInteger i = 0; i < length; i++)
{
if ([string characterAtIndex:i] != '0')
{
return [string substringFromIndex:i];
}
}
return #"0";
// Time 1: 0.210126
// Time 2: 0.219159
// Time 3: 0.201496
Converting to an int and then back to a NSString
int num = [string intValue];
return [NSString stringWithFormat:#"%d", num];
// Time 1: 0.322206
// Time 2: 0.345259
// Time 3: 0.324954
long long num = [string longLongValue];
return [NSString stringWithFormat:#"%lld", num];
// Time 1: 0.364318
// Time 2: 0.344946
// Time 3: 0.364761
// These are only slightly slower, but you can do bigger numbers using long long
Using a NSScanner
NSScanner *scanner = [NSScanner scannerWithString:string];
NSCharacterSet *zeros = [NSCharacterSet
characterSetWithCharactersInString:#"0"];
[scanner scanCharactersFromSet:zeros intoString:NULL];
return [string substringFromIndex:[scanner scanLocation]];
// Time 1: 0.505277
// Time 2: 0.481884
// Time 3: 0.487209
Using a regular expression
NSRange range = [string rangeOfString:#"^0*" options:NSRegularExpressionSearch];
return [string stringByReplacingCharactersInRange:range withString:#""];
// Time 1: 0.610879
// Time 2: 0.645335
// Time 3: 0.637690
Using a static number formatter
static NSNumberFormatter *formatter = nil;
if (formatter == nil)
{
formatter = [NSNumberFormatter new];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
}
NSNumber *number = [formatter numberFromString:string];
return [formatter stringFromNumber:number];
// Time 1: 1.774198
// Time 2: 1.753013
// Time 3: 1.753893
Using a number formatter
NSNumberFormatter *formatter = [NSNumberFormatter new];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
NSNumber *number = [formatter numberFromString:string];
return [formatter stringFromNumber:number];
// Time 1: 11.978336
// Time 2: 12.039905
// Time 3: 11.904984
// No wonder Apple recommends reusing number formatters!

You could use regular expressions.
NSString *numStr = #"0001234";
NSRange range = [numStr rangeOfString:#"^0*" options:NSRegularExpressionSearch];
NSString *result = [numStr stringByReplacingCharactersInRange:range withString:#""];
If you doing it a lot (as in thousands of times) you may benefit from using a compiled NSRegularExpression.

This works, I've tested it. And I think this is a very good way. Less code less bug. KISS.
NSString *numStr = #"000123";
int num = numStr.intValue;
numStr = [NSString stringWithFormat:#"%d", num];

Related

Scan string, get info. Xcode

I have made an app which the user types in data and it gets a url from google maps like this - [https://www.google.com.au/maps/search/nearest+pizza+shop/#-27.4823545,153.0297855,12z/data=!3m1!4b1
In the middle you see 27.4823545,153.0297855 this is long and lat. So with this I can make my maps work. But I really need to know how to scan this string (the url was made into a string) and get only those numbers, I have already tried this ->
NSString *currentURL = web.request.URL.absoluteString;
string1 = currentURL;
NSScanner *scanner = [NSScanner scannerWithString:string1];
NSString *token = nil;
[scanner scanUpToString:#"#" intoString:NULL];
[scanner scanUpToString:#"z" intoString:&token];
label.text = token;
I think it would be highly likely I did a mistake, since I am new to objective-c, but if there are more effective ways please share . :)
Thanks for all the people who took the time to help.
Bye All!
A solution:
Extrating the path from an NSURL. Then looking at each path components to extract the coordinates components :
NSURL *url = [NSURL URLWithString:#"https://www.google.com.au/maps/search/nearest+pizza+shop/#-27.4823545,153.0297855,12z/data=!3m1!4b1"];
NSArray *components = [url.path componentsSeparatedByString:#"/"];
NSArray *results = nil;
for (NSString *comp in components) {
if ([comp length] > 1 && [comp hasPrefix:#"#"]) {
NSString *resultString = [comp substringFromIndex:1]; // removing the '#'
results = [resultString componentsSeparatedByString:#","]; // lat, lon, zoom
break;
}
}
NSLog(#"results: %#", results);
Will print:
results: (
"-27.4823545",
"153.0297855",
12z
)
This solution gives you a lot of control points for data validation.
Hope this helps.
Edit:
To get rid of the "z". I would treat that as a special number with a number formatter in decimal style and specify that character as the positive and negative suffix:
NSString *targetStr = #"12z";
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
formatter.positiveSuffix = #"z";
formatter.negativeSuffix = #"z";
NSNumber *result = [formatter numberFromString:targetStr];
The var result will contain whatever positive or negative number before the 'z'. You could use a NSScanner to do the trick but I believe it's less flexible.
As a side note, it would be great to get that "z" (zoom's character) from Google's API. If they ever change it to something else your number will still be parsed properly.

Get a substring from an NSString until arriving to any letter in an NSArray - objective C

I am trying to parse a set of words that contain -- first greek letters, then english letters. This would be easy if there was a delimiter between the sets.That is what I've built so far..
- (void)loadWordFileToArray:(NSBundle *)bundle {
NSLog(#"loadWordFileToArray");
if (bundle != nil) {
NSString *path = [bundle pathForResource:#"alfa" ofType:#"txt"];
//pull the content from the file into memory
NSData* data = [NSData dataWithContentsOfFile:path];
//convert the bytes from the file into a string
NSString* string = [[NSString alloc] initWithBytes:[data bytes]
length:[data length]
encoding:NSUTF8StringEncoding];
//split the string around newline characters to create an array
NSString* delimiter = #"\n";
incomingWords = [string componentsSeparatedByString:delimiter];
NSLog(#"incomingWords count: %lu", (unsigned long)incomingWords.count);
}
}
-(void)parseWordArray{
NSLog(#"parseWordArray");
NSString *seperator = #" = ";
int i = 0;
for (i=0; i < incomingWords.count; i++) {
NSString *incomingString = [incomingWords objectAtIndex:i];
NSScanner *scanner = [NSScanner localizedScannerWithString: incomingString];
NSString *firstString;
NSString *secondString;
NSInteger scanPosition;
[scanner scanUpToString:seperator intoString:&firstString];
scanPosition = [scanner scanLocation];
secondString = [[scanner string] substringFromIndex:scanPosition+[seperator length]];
// NSLog(#"greek: %#", firstString);
// NSLog(#"english: %#", secondString);
[outgoingWords insertObject:[NSMutableArray arrayWithObjects:#"greek", firstString, #"english",secondString,#"category", #"", nil] atIndex:0];
[englishWords insertObject:[NSMutableArray arrayWithObjects:secondString,nil] atIndex:0];
}
}
But I cannot count on there being delimiters.
I have looked at this question. I want something similar. This would be: grab the characters in the string until an english letter is found. Then take the first group to one new string, and all the characters after to a second new string.
I only have to run this a few times, so optimization is not my highest priority.. Any help would be appreciated..
EDIT:
I've changed my code as shown below to make use of NSLinguisticTagger. This works, but is this the best way? Note that the interpretation for english characters is -- for some reason "und"...
The incoming string is: άγαλμα, το statue, only the last 6 characters are in english.
int j = 0;
for (j=0; j<incomingString.length; j++) {
NSString *language = [tagger tagAtIndex:j scheme:NSLinguisticTagSchemeLanguage tokenRange:NULL sentenceRange:NULL];
if ([language isEqual: #"und"]) {
NSLog(#"j is: %i", j);
int k = 0;
for (k=0; k<j; k++) {
NSRange range = NSMakeRange (0, k);
NSString *tempString = [incomingString substringWithRange:range ];
NSLog (#"tempString: %#", tempString);
}
return;
}
NSLog (#"Language: %#", language);
}
Alright so what you could do is use NSLinguisticTagger to find out the language of the word (or letter) and if the language has changed then you know where to split the string. You can use NSLinguisticTagger like this:
NSArray *tagschemes = #[NSLinguisticTagSchemeLanguage];
NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:tagschemes options: NSLinguisticTagPunctuation | NSLinguisticTaggerOmitWhitespace];
[tagger setString:#"This is my string in English."];
NSString *language = [tagger tagAtIndex:0 scheme:NSLinguisticTagSchemeLanguage tokenRange:NULL sentenceRange:NULL];
//Loop through each index of the string's characters and check the language as above.
//If it has changed then you can assume the language has changed.
Alternatively you can use NSSpellChecker's requestCheckingOfString to get teh dominant language in a range of characters:
NSSpellChecker *spellChecker = [NSSpellChecker sharedSpellChecker];
[spellChecker setAutomaticallyIdentifiesLanguages:YES];
NSString *spellCheckText = #"Guten Herr Mustermann. Dies ist ein deutscher Text. Bitte löschen Sie diesen nicht.";
[spellChecker requestCheckingOfString:spellCheckText
range:(NSRange){0, [spellCheckText length]}
types:NSTextCheckingTypeOrthography
options:nil
inSpellDocumentWithTag:0
completionHandler:^(NSInteger sequenceNumber, NSArray *results, NSOrthography *orthography, NSInteger wordCount) {
NSLog(#"dominant language = %#", orthography.dominantLanguage);
}];
This answer has information on how to detect the language of an NSString.
Allow me to introduce two good friends of mine.
NSCharacterSet and NSRegularExpression.
Along with them, normalization. (In Unicode terms)
First, you should normalize strings before analyzing them against a character set.
You will need to look at the choices, but normalizing to all composed forms is the way I would go.
This means an accented character is one instead of two or more.
It simplifies the number of things to compare.
Next, you can easily build your own NSCharacterSet objects from strings (loaded from files even) to use to test set membership.
Lastly, regular expressions can achieve the same thing with Unicode Property Names as classes or categories of characters. Regular expressions could be more terse but more expressive.

How to restrict number of fraction digits when parsing number from string?

I want to restrict the number of fraction digits a user is allowed to enter into a UITextField that only accepts (localized) numeric input.
Example with 4 fraction digits allowed:
Good: 42, 10.123, 12345.2345
Bad: 0.123456, 6.54321
Right now, I'm using NSNumberFormatter's numberFromString: in the UITextField delegate's textField:shouldChangeCharactersInRange:replacementString: to determine whether it's a legal numeric value.
Unfortunately, NSNumberFormatter seems to ignore maximumFractionDigits in numberFromString:. In tests using getObjectValue:forString:range:error: I had the same problem, and range also was the full length of the string afterwards (unless I start entering letters; then range indicates only the part of the string with digits):
NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
formatter.maximumFractionDigits = 3;
formatter.roundingMode = NSNumberFormatterRoundHalfUp;
formatter.generatesDecimalNumbers = YES;
NSDecimalNumber* n = (NSDecimalNumber*)[formatter numberFromString:#"10.12345"];
NSLog(#"Number: %#", n.description); // expected: 10.123, but is: 10.12345
How to best restrict the number of fraction digits in user input?
after you get the unrestricted number, you can use stringWithFormat on that number to create a string with a certain number of decimal places.
eg.
double number = myTextField.text.doubleValue;
NSString *restrictedString = [NSString stringWithFormat:#"%.4f", number];
There are a few ways to do this, but the easiest is probably to split the string into two parts (you will have to localize the '.') and check the length of the second part, like this:
- (BOOL)LNNumberIsValid:(NSString *)string
{
NSArray *numArray = [string componentsSeparatedByString:#"."];
if ([numArray count] == 2)
if ([[numArray objectAtIndex:1] length] > 4)
return NO;
return YES;
}
// Tests
NSLog(#"42: %i", [self LNNumberIsValid:#"42"]); // 1
NSLog(#"10.123: %i", [self LNNumberIsValid:#"10.123"]); // 1
NSLog(#"12345.2345: %i", [self LNNumberIsValid:#"12345.2345"]); // 1
NSLog(#"0.123456: %i", [self LNNumberIsValid:#"0.123456"]); // 0
NSLog(#"6.54321: %i", [self LNNumberIsValid:#"6.54321"]); // 0
EDIT:
The problem with the code that you added to your question is that you are printing the description of the NSDecimalNumber, which is not localized or limited to the number of digits. The NSDecimalNumber itself stores everything that you give it, so you need to change the original string (like my example above) if you want to change that. However, once you have your NSDecimalNumber, you can use the same number formatter to convert it back to a string in the format that you like:
NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
formatter.maximumFractionDigits = 3;
formatter.roundingMode = NSNumberFormatterRoundHalfUp;
formatter.generatesDecimalNumbers = YES;
NSDecimalNumber* n = (NSDecimalNumber*)[formatter numberFromString:#"10.12345"];
NSString *s = [formatter stringFromNumber:n];
NSLog(#"Number: %#", s); // expected: 10.123, and is: 10.123
The way I solved this is by checking the position of the decimal separator and making sure that the insertion either is before that position or the insertion would not exceed the maximum number of fraction digits.
Also, I check that the input of a new separator does not occur at a place that would lead to more then the allowed fraction digits and that not more than one separators can be inserted
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *separator = self.numberFormatter.decimalSeparator;
if(string.length == 0) {
// Empty String means deletion, always possible
return YES;
}
// Check for valid characters (0123456789 + decimal Separator)
for (int i = 0; i < [string length]; ++i) {
unichar c = [string characterAtIndex:i];
if (![self.legalCharSet characterIsMember:c])
{
return NO;
}
}
// Checks if input is separator
if([string isEqualToString:separator]) {
// Check that separator insertion would not lead to more than 2 fraction digits and that not more than one separators are inserted
// (the MIN() makes sure that length - kMaxFractionDigits won’t be below 0 as length and location are NSUIntegers)
return range.location >= self.valueField.text.length - MIN(self.valueField.text.length,kMaxFractionDigits) && [self.valueField.text containsString:separator] == NO;
} else {
// Check if a separator is already included in the string
NSRange separatorPos = [self.valueField.text rangeOfString: separator];
if(separatorPos.location != NSNotFound) {
// Make sure that either the input is before the decimal separator or that the fraction digits would not exceed the maximum fraction digits.
NSInteger fractionDigits = self.valueField.text.length - (separatorPos.location + 1);
return fractionDigits + string.length <= kMaxFractionDigits || range.location <= separatorPos.location;
}
}
return YES;
}
The method may not be bullet proof but it should be sufficient for common text insertions.

Objective C: Formatting numbers with "just enough" decimal places

Is there a quick way to format numbers (e.g., when using appendFormat) so that only enough decimal places are shown? E.g., 4.23100 is shown as 4.231, while 5.0000 is shown as just 5.
Thanks for reading.
Use %g instead of %f
double n = 1234.5678;
NSString str = [NSString stringWithFormat:#"number is %g", n];
Use %g with the appropriate precision.
double number = 1234.123;
NSLog(#"Number equals: %.8g", number)
As referenced here, the %g conversion will default to 6 significant figures unless a maximum precision is specified. %.7g or larger will all give the desired float.
I couldn't find a built-in way to do this at first, so I built a quick function (below) if anyone is interested. That said, I believe %g (Thanks MSgambel, Roger Gilbrat) and the NSNumberFormatter class (Thanks Inafziger, David Nedrow) do this much more succinctly, so they're probably the better option!
+ (NSString *)trimmedFraction:(float)fraction{
int i=0;
float numDiff=0;
// Loop through possible decimal values (in this case, maximum of 6)
for (i=0;i<=6;i++){
// Calculate difference between fraction and rounded fraction
numDiff=round(fraction*pow(10,i))/pow(10,i)-fraction;
// Check if difference is less than half of the smallest possible value
if (fabsf(numDiff)<(0.5*pow(10,-6))){
break;
}
}
// Return string of truncated fraction
NSString *stringPattern=[NSString stringWithFormat:#"%%0.%df",i];
return [NSString stringWithFormat:stringPattern,fraction];
}
In the code you can specify the maximum number of decimal spaces (e.g., 6 in this case). Then you just send it a fraction and it returns a string with only the relevant decimals. For example, send it 4.231000 and it returns #"4.231", send it 5.000000 and it returns #"5". (Again, probably better to just use %g and NSNumberWithFormatter).
If you store the original values as strings instead of floating-point numbers, the question makes a lot of sense.
I handle this using a category on NSString:
+ (NSString *) stringForNotANumber {
static NSString *singleton;
if (!singleton) singleton = #"NaN";
return singleton;
}
- (NSUInteger) numberOfDecimalPlaces {
NSString *strValue = self;
// If nil, return -1
if (!strValue) return -1;
// If non-numeric, return -1
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
[f setMaximumFractionDigits:128];
NSNumber *numValue = [f numberFromString:strValue];
if (!numValue) return -1;
// Count digits after decimal point in original input
NSRange range = [strValue rangeOfString:#"."];
if (NSNotFound == range.location) return 0;
return [strValue substringFromIndex:range.location+1].length;
}
- (NSString *) plus:(NSString *) addend1 {
NSString *addend2 = self;
if (!addend1 || !addend2) return nil;
if (addend1 == [NSString stringForNotANumber] ||
addend2 == [NSString stringForNotANumber])
return [NSString stringForNotANumber];
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
NSNumber *num1 = [f numberFromString:addend1];
NSNumber *num2 = [f numberFromString:addend2];
if (!num1 || !num2) return [NSString stringForNotANumber];
double sum = num1.doubleValue + num2.doubleValue;
[f setMinimumFractionDigits:MAX(addend1.numberOfDecimalPlaces,
addend2.numberOfDecimalPlaces)];
return [f stringFromNumber:[NSNumber numberWithDouble:sum]];
}

Is there any method that can take "one" and return "1"?

I've looked at NSNumberFormatter, but that hasn't worked, so is there a way of parsing written numbers and turning them in to actual numbers?
Something like this would work (for positive whole numbers anyway). This is just a starting point, you would have to check to see that the words were correct and maybe ignore capitalization to make it more robust:
[self parseNumberWords:#"five two three"];
-(NSInteger)parseNumberWords:(NSString *)input {
NSArray *wordArray = [NSArray arrayWithObjects:#"zero",#"one",#"two",#"three",#"four",#"five",#"six",#"seven",#"eight",#"nine", nil];
NSArray *words = [input componentsSeparatedByString:#" "];
NSInteger num = 0;
NSInteger j =0;
for (NSInteger i = [words count]; i>0 ;i--) {
num = num + [wordArray indexOfObject:[words objectAtIndex:i-1]] * pow(10, j);
j = j+1;
}
NSLog(#"%ld",num);
return num;
}
NSNumberFormatter will get you some of the way there, via NSNumberFormatterSpellOutStyle. The basic formatting that NSNumber does will finish it off.
NSNumberFormatter * nf = [[NSNumberFormatter alloc] init];
[nf setNumberStyle:NSNumberFormatterSpellOutStyle];
NSString * numberWordString = #"three one two";
NSMutableString * digitString = [[NSMutableString alloc] init];
// Break up the input string at spaces and iterate over the result
for(NSString * s in [numberWordString componentsSeparatedByString:#" "]){
// Let the formatter turn each string into an NSNumber, then get
// the stringValue from that, which will be a digit.
[digitString appendString:[[nf numberFromString:s] stringValue]] ;
}
NSLog(#"%#", digitString); // prints "312"
Obviously, you'll have to put some work in to handle different input formats, lowercase, bad input (this will crash if nf fails to format -- it'll return nil which is an illegal argument to appendString:), etc.