【问题标题】:Check if last characters of an NSString are numbers检查 NSString 的最后一个字符是否为数字
【发布时间】:2011-02-03 23:29:18
【问题描述】:

是否可以看到以未知长度的数字结尾的字符串?

  1. “字符串 1”-> 1
  2. “字符串 4356”-> 4356
  3. “字符串”-> 无

如果是这样,我如何确定这个数字?

【问题讨论】:

    标签: cocoa nsstring numbers


    【解决方案1】:

    要测试字符串是否以数字结尾,可以使用NSPredicate,例如:

    NSPredicate endsNumerically = [NSPredicate predicateWithFormat:@"SELF matches %@", @"\\d+$"];
    [endsNumerically evaluateWithObject:string]; // returns TRUE if predicate succeeds
    

    NSScanner 有时可用于从字符串中提取内容,但它不会向后扫描。您可以定义一个 Gnirts(反向字符串)类并将其与 NSScanner 一起使用,但这可能比它的价值更麻烦。

    我希望使用的 NSString 的 rangeOfCharacterFromSet:options: 只查找单个字符(就像 strchr and strrchr,如果您熟悉 C 的话),但我们可以滚动自己的返回连续字符范围从一组(有点像strspn)作为category 上的NSString。在此过程中,让我们包含返回子字符串而不是范围的方法。

    RangeOfCharacters.h:

    @interface NSString (RangeOfCharacters)
    /* note "Characters" is plural in the methods. It has poor readability, hard to 
     * distinguish from the rangeOfCharacterFromSet: methods, but it's standard Apple 
     * convention.
     */
    -(NSRange)rangeOfCharactersFromSet:(NSCharacterSet*)aSet;
    -(NSRange)rangeOfCharactersFromSet:(NSCharacterSet*)aSet options:(NSStringCompareOptions)mask;
    -(NSRange)rangeOfCharactersFromSet:(NSCharacterSet*)aSet options:(NSStringCompareOptions)mask range:(NSRange)range;
    
    // like the above, but return a string rather than a range
    -(NSString*)substringFromSet:(NSCharacterSet*)aSet;
    -(NSString*)substringFromSet:(NSCharacterSet*)aSet options:(NSStringCompareOptions)mask;
    -(NSString*)substringFromSet:(NSCharacterSet*)aSet options:(NSStringCompareOptions)mask range:(NSRange)range;
    @end
    

    RangeOfCharacters.m:

    @implementation NSString (RangeOfCharacters)
    -(NSRange)rangeOfCharactersFromSet:(NSCharacterSet*)aSet {
        return [self rangeOfCharactersFromSet:aSet options:0];
    }
    
    -(NSRange)rangeOfCharactersFromSet:(NSCharacterSet*)aSet options:(NSStringCompareOptions)mask {
        NSRange range = {0,[self length]};
        return [self rangeOfCharactersFromSet:aSet options:mask range:range];
    }
    
    -(NSRange)rangeOfCharactersFromSet:(NSCharacterSet*)aSet options:(NSStringCompareOptions)mask range:(NSRange)range {
        NSInteger start, curr, end, step=1;
        if (mask & NSBackwardsSearch) {
            step = -1;
            start = range.location + range.length - 1;
            end = range.location-1;
        } else {
            start = range.location;
            end = start + range.length;
        }
        if (!(mask & NSAnchoredSearch)) {
            // find first character in set
            for (;start != end; start += step) {
                if ([aSet characterIsMember:[self characterAtIndex:start]]) {
    #ifdef NOGOTO
                    break;
    #else
                    // Yeah, a goto. If you don't like them, define NOGOTO.
                    // Method will work the same, it will just make unneeded
                    // test whether character at start is in aSet
                    goto FoundMember;
    #endif
                }
            }
    #ifndef NOGOTO
            goto NoSuchMember;
    #endif
        }
        if (![aSet characterIsMember:[self characterAtIndex:start]]) {
        NoSuchMember:
            // no characters found within given range
            range.location = NSNotFound;
            range.length = 0;
            return range;
        }
    
    FoundMember:
        for (curr = start; curr != end; curr += step) {
            if (![aSet characterIsMember:[self characterAtIndex:curr]]) {
                break;
            }
        }
        if (curr < start) {
            // search was backwards
            range.location = curr+1;
            range.length = start - curr;
        } else {
            range.location = start;
            range.length = curr - start;
        }
        return range;
    }
    
    -(NSString*)substringFromSet:(NSCharacterSet*)aSet {
        return [self substringFromSet:aSet options:0];
    }
    
    -(NSString*)substringFromSet:(NSCharacterSet*)aSet options:(NSStringCompareOptions)mask  {
        NSRange range = {0,[self length]};
        return [self substringFromSet:aSet options:mask range:range];
    }
    -(NSString*)substringFromSet:(NSCharacterSet*)aSet options:(NSStringCompareOptions)mask range:(NSRange)range {
        NSRange range = [self rangeOfCharactersFromSet:aSet options:mask range:range];
        if (NSNotFound == range.location) {
            return nil;
        }
        return [self substringWithRange:range]; 
    }
    @end
    

    使用新类别检查字符串是否以数字结尾或提取数字:

    NSString* number = [string substringFromSet:[NSCharacterSet decimalDigitCharacterSet] 
                                 options:NSBackwardsSearch|NSAnchoredSearch];
    if (number != nil) {
        return [number intValue];
    } else {
        // string doesn't end with a number.
    }
    

    最后,您可以使用第三方正则表达式库,例如RegexKitRegexkitLite

    【讨论】:

    • 感谢您的回复。我认为您的第二个 sn-p 中有两个错别字。 range 应该是 digitRange 并且 == 应该是 != 对吗?此外,第二个 sn-p 为值 12 找到 2。我想找到所有与密码数量无关的正数。您的第二个 sn-p 是否也可以这样做,还是我应该使用正则表达式库?
    • @Michael:是的。最初,我首先有“字符串不以数字结尾”分支;显然,我忽略了反转测试。我会用一个 sn-p 更新我的答案,一旦我休息一下,它会提取整个数字。
    • 感谢您帮我查看。
    • 感谢超级超级百万倍!它在 substringFromSet:options:range: 中包含一个错字,范围在那里声明了两次。从那段中删除 NSRange 成功了:D
    • +1 用于使用 goto 并在没有它的情况下使代码工作的真棒
    【解决方案2】:

    我无法让上面的NSPredicate 代码正常工作,尽管它看起来应该正常工作。相反,我用

    完成了同样的事情
    if ([string rangeOfString:@"\\d+$" options:NSRegularExpressionSearch].location != NSNotFound) {
        // string ends with a number
    }
    

    this answer

    【讨论】:

      猜你喜欢
      • 2020-01-27
      • 1970-01-01
      • 1970-01-01
      • 2013-01-06
      • 2018-10-15
      • 1970-01-01
      • 2020-07-15
      • 1970-01-01
      • 2023-03-28
      相关资源
      最近更新 更多