从字符串解析数字时如何限制小数位数?

时间:2013-08-19 21:33:04

标签: objective-c uikit nsnumberformatter

我想限制允许用户输入只接受(本地化)数字输入的UITextField的小数位数。

允许4位数字的示例:

  • 好:4210.12312345.2345
  • 不好:0.1234566.54321

现在,我正在NSNumberFormatter代理numberFromString:中使用UITextField的{​​{1}}来确定它是否是合法的数值。

不幸的是,textField:shouldChangeCharactersInRange:replacementString:似乎忽略了NSNumberFormatter中的maximumFractionDigits。在使用numberFromString:的测试中,我遇到了同样的问题,getObjectValue:forString:range:error:之后也是字符串的全长(除非我开始输入字母;然后range仅指示字符串的一部分位):

range

如何最好地限制用户输入中的小数位数?

3 个答案:

答案 0 :(得分:1)

有几种方法可以做到这一点,但最简单的可能是将字符串分成两部分(你必须本地化'。')并检查第二部分的长度,如下所示:

- (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

修改
您添加到问题中的代码的问题是您正在打印NSDecimalNumber的description,它未本地化或仅限于位数。 NSDecimalNumber本身存储您提供的所有内容,因此如果要更改原始字符串,则需要更改原始字符串(如上例所示)。但是,一旦有了NSDecimalNumber,就可以使用相同的数字格式器将其转换回您喜欢的格式的字符串:

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

答案 1 :(得分:1)

获得无限制数后,可以对该数字使用stringWithFormat来创建具有一定小数位数的字符串。

例如

double number = myTextField.text.doubleValue;
NSString *restrictedString =  [NSString stringWithFormat:@"%.4f", number];

答案 2 :(得分:0)

我解决这个问题的方法是检查小数分隔符的位置并确保插入位于该位置之前或插入不会超过小数位数的最大位数。 另外,我检查新分隔符的输入是否发生在一个会导致超过允许的小数位数并且可以插入不超过一个分隔符的位置

-(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;
}

该方法可能不是防弹的,但对于常见的文本插入应该足够了。