Objective c 检查NSString是否包含有效的数字,但已本地化

Objective c 检查NSString是否包含有效的数字,但已本地化,objective-c,nsnumberformatter,Objective C,Nsnumberformatter,如何在考虑本地化数字格式的情况下检查字符串是否包含有效数字 这个问题不是关于转换数字的主要问题。我可以用NSNumberFormatter实现这一点。如果我做不到,我甚至不需要这样做,并且可以将字符串作为字符串保留。但我需要检查它是否包含有效的数字

如何在考虑本地化数字格式的情况下检查字符串是否包含有效数字

这个问题不是关于转换数字的主要问题。我可以用NSNumberFormatter实现这一点。如果我做不到,我甚至不需要这样做,并且可以将字符串作为字符串保留。但我需要检查它是否包含有效的数字

这是我的代码:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

    // Only test for numbers if it is a certain text field. 
    if (textField == self.numValueTextField) {
        NSString *resultingString = [textField.text stringByReplacingCharactersInRange: range withString: string];

        // The user deleting all input is perfectly acceptable.
        if ([resultingString length] == 0) {
            return true;
        }

        double holder;

        NSScanner *scan = [NSScanner scannerWithString: resultingString];

        BOOL isNumeric = [scan scanDouble: &holder] && [scan isAtEnd];

        if (isNumeric) {

            [self.detailItem setValue:number forKey:kValueDouble];
        }

        return isNumeric;
    }
    return YES;  // default for any other text field - if any.
}
这很好,但它暗示了英语符号。这意味着浮点必须是一个pont。但在世界上的某些地方,它可能是逗号或其他什么

我知道如何检查某些字符。所以我可以检查0-9,逗号和点。但是,有没有一种“适当”的方法可以做到这一点


如果这很重要:我在一个iOS环境中

我就是这样做的。您需要的关键信息是
[[NSLocale currentLocale]objectForKey:nslocaledecimalsepator]
,它将告诉您当前的十进制分隔符。然后查找任何不在合法集合中的字符

- (BOOL)isLegalDigitString:(NSString *)string
              forTextField:(UITextField *)textField
           hasDecimalPoint:(BOOL)hasDecimalPoint
{
  // It's always legal to delete
  if ([string length] == 0)
  {
    return YES;
  }

  // Only legal characters
  NSString *decimalSeparator = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];

  NSString *legalCharacters = [@"1234567890" stringByAppendingString:
                               (hasDecimalPoint ? decimalSeparator
                                : @"")];
  NSCharacterSet *forbiddenCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:legalCharacters] invertedSet];
  if ([string rangeOfCharacterFromSet:forbiddenCharacterSet].location != NSNotFound)
  {
    return NO;
  }

  // Only one decimal point
  if (hasDecimalPoint &&
      [string rangeOfString:decimalSeparator].location != NSNotFound &&
      [[textField text] rangeOfString:decimalSeparator].location != NSNotFound)
  {
    return NO;
  }

  return YES;
}
作为我如何使用此功能的示例:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
  if (textField == [self integerTextField])
  {
    return [self isLegalDigitString:string forTextField:textField hasDecimalPoint:NO];
  }
  else if (textField == [self floatTextField])
  {
    return [self isLegalDigitString:string forTextField:textField hasDecimalPoint:YES];
  }

  return YES;
}

有一件事做得并不好,那就是管理有限的精度(例如,一个只能有十分之一精度的字段)。这需要重新设计。

默认情况下,数字格式化程序将使用当前语言环境的信息,因此它应该能够为您执行此检查。请尝试格式化字符串。如果结果为
nil
,则它无效

static NSNumberFormatter * formatter = [NSNumberFormatter new];
//... build string
NSNumber * num = [formatter numberFromString:resultingString];
BOOL isNumeric = (num != nil);
例如:

NSNumberFormatter * formatter = [NSNumberFormatter new];

// US uses period as decimal separator
[formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];

NSLog(@"%@", [formatter numberFromString:@"3.1415"]);   // Valid NSNumber
NSLog(@"%@", [formatter numberFromString:@"3.14.15"]);  // nil
NSLog(@"%@", [formatter numberFromString:@"31415"]);    // Valid
NSLog(@"%@", [formatter numberFromString:@"3,1415"]);   // nil

// Italy uses a comma as decimal separator
[formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"it"]];
NSLog(@"%@", [formatter numberFromString:@"3.1415"]);    // nil
NSLog(@"%@", [formatter numberFromString:@"3.14.15"]);   // nil
NSLog(@"%@", [formatter numberFromString:@"31415"]);     // Valid
NSLog(@"%@", [formatter numberFromString:@"3,1415"]);    // Valid

或者您可能更喜欢使用
getObjectValue:forString:range:error:
,它将简单地返回
YES
NO
以指示解析成功,如果您对进一步的详细信息感兴趣,还可以给您一个错误对象。

您可以使用NSLocale获取本地化的分组分隔符,然后将其从字符串中删除,因为NSScanner不处理分组

NSString *sep = [[NSLocale currentLocale] objectForKey:NSLocaleGroupingSeparator];
NSString *string2 = [string1 stringByReplacingOccurrencesOfString:sep withString:@""];
初始化考虑本地化小数分隔符的NSScanner

NSScanner *scan = [NSScanner localizedScannerWithString:string2];

然后按原样继续使用NSScanner代码。使用NSScanner比手工编码方法更可靠,因为它可以处理所有符合IEEE标准的数字,包括+/-和科学符号。它也很短。

坦率地说,任何有效数字的测试,即使是对部分数字也应该有效,但实现起来非常困难

  • 不要尝试重新执行已在
    NSNumberFormatter
    中执行的检查。不同的语言使用不同的十进制分隔符,但也使用不同的数字(编辑:很遗憾,
    NSNumberFormatter
    也无法验证非阿拉伯数字)

  • 有许多特殊情况不是有效数字,但可以成为有效数字,例如空字符串、减号、十进制分隔符、减号与十进制分隔符组合。如果你允许科学记数法,它会变得复杂得多

  • 最简单的解决方案是只检查最终数字。用户确认后检查输入(点击进入,点击输入视图外部)


    但是,还要注意,
    NSNumberFromatter
    实际上可以验证部分字符串。有关更多信息,请参阅其超类
    NSFormatter

    谢谢。我觉得不错。只有一个问题。为什么要将文本字段和字符串分别传递到方法中?其中一个不足以完成这项工作吗?字符串是建议的更改,而不是已经存在的字符串。我传递文本字段是因为我想检查现有十进制分隔符的当前值(因此用户不能键入其中两个)。谢谢,是的,我今晚花了一些时间才弄明白。因为它是建议的更改,所以它不能是文本字段文本的一部分。只有在返回YES时,才会将其添加到文本字段的属性中。这是最简单的解决方案,在90%的情况下都可以使用。为什么只有90%?因为不是所有的语言都使用阿拉伯数字
    NSNumberFormatter
    使用起来更安全。有趣。谢谢没有文件证明它在“非法”格式时返回零。嗯,我想是的。您还可以使用
    getObjectValue:forString:range:error:
    ,如果无法解析输入,它将显式返回
    NO
    。非常确定
    numberFromString:
    是一个包装器。对于“”您需要一些特殊情况,您总是希望它是合法的(这意味着用户正在删除)和首字母“.”,它可能是合法的,但不会格式化为数字。您还需要正确构建测试字符串,替换传递给您的字符(不要忘记测试粘贴多个字符)。这并不难,但这就是我直接测试输入的原因。谢谢。这已经被考虑过了。查看我的代码。我只是想为
    isNumeric=…
    语句寻找一个好的自动本地化的替代品。到目前为止,我已经实现了它,今晚将做一些测试。任何允许分组分隔符输入的输入都是错误的。分组分隔符应由应用程序添加,而不是由用户添加。我总是同意。代码是关于将字符串作为数字进行计算,而不是关于这些字符串是如何到达那里的。如果存在分组分隔符,它们将被剥离。如果没有,没有伤害。我认为这样做更完整、更普遍,但如果不适合您的情况,您可以删除该部分。不幸的是,这造成了糟糕的用户体验。既然他们输入了一个无效的数字并点击了enter,你该怎么办?显示警报?首先要防止错误,而不是强迫用户以后修复错误。支持非阿拉伯数字是一项需要测试的承诺。在文本字段中接受非阿拉伯数字,但不实际测试它是否可以端到端工作,这对每个人来说都是一种糟糕的体验。最好是明确不支持