【问题标题】:How to restrict number of fraction digits when parsing number from string?从字符串解析数字时如何限制小数位数?
【发布时间】:2013-08-19 21:33:03
【问题描述】:

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

允许 4 个小数位的示例:

  • 好:4210.12312345.2345
  • 错误:0.1234566.54321

现在,我在UITextField 委托的textField:shouldChangeCharactersInRange:replacementString: 中使用NSNumberFormatternumberFromString: 来确定它是否是合法的数值。

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

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

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

【问题讨论】:

    标签: objective-c uikit nsnumberformatter


    【解决方案1】:

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

    例如。

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

    【讨论】:

    • 两件事:1)他使用的是NSString,而不是浮点数(因为它来自文本字段)。 2)由于存储浮点数的不精确性,您不应该从代表用户输入的浮点数创建字符串。
    • 我不认为他会直接获得浮动。我在 UITextField 的 text 属性上调用 floatValue。
    • 很遗憾,.floatValue 没有本地化。不过,通过格式化字符串进行往返看起来很有趣。
    • 是的,很抱歉我错过了。但是,第 2 点仍然有效并且会给出不正确的结果。改用双精度,这会有所帮助。
    【解决方案2】:

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

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

    【讨论】:

    • 所以基本上必须自己做。这很令人失望。到目前为止,我不想这样做,因为数字格式可能很奇怪——我必须查找国际数字格式,看看它们中的一些在小数点分隔符后是否出乎意料地奇怪。他们肯定在它之前......
    • 是的,其中一些是。 :-) maximumFractionDigits 应该可以工作,发布您的代码,我们可以看看它。
    • 您是否将 usesSignificantDigits 设置为 true(并且还应设置 setRoundingMode,以便获得您期望的舍入行为)。
    • 这真的很基本,我编辑了这个问题。我昨天在 SO 上发现了一两个其他问题,其中还提到从字符串中读取数字时会忽略这一点,这与文档似乎表明的相反。
    • 这应该可以通过在调用 numberFromString 之前添加 formatter.usersSignificantDigits = YES; 来实现。此外,您可能不想将 generatesDecimalNumbers 设置为 YES(除非您希望它返回 NSDecimalNumbers)....
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-03
      • 1970-01-01
      • 2012-11-04
      相关资源
      最近更新 更多