从NSString中删除除数字以外的所有内容

158

我有一个NSString(电话号码),其中一些电话号码被格式化为带有括号和连字符。 我该如何从字符串中删除除数字以外的所有字符?

22个回答

379

虽然这是一个旧问题,但是有一个解决方案:

  NSString *newString = [[origString componentsSeparatedByCharactersInSet:
                [[NSCharacterSet decimalDigitCharacterSet] invertedSet]] 
                componentsJoinedByString:@""];

它将源字符串根据非数字字符进行拆分,然后使用空字符串分隔符重新组装。虽然不如逐个字符检索效率高,但代码更加简洁。


6
谢谢!对于其他初学者而言,你可以通过执行 NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"] 来创建自己的定制 NSCharacterSet。 - abc123
1
非常感谢!只是出于好奇,你有没有想法为什么 NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet] 不起作用? - Thomas Besnehard
1
@Tommecpe stringByTrimmingCharactersInSet 只会从字符串的开头和结尾删除字符,因此它不会影响第一个不匹配字符之后或最后一个不匹配字符之前的部分。 - boblicious
我想保留数字和字母,该怎么做? - LiangWang
1
在上面的例子中,您可以将“[NSCharacterSet decimalDigitCharacterSet]”替换为仅包含数字和字母的其他字符集。您可以通过创建一个NSMutableCharaterSet并将decimalDigitCharacterSetuppercaseLetterCharacterSetlowercaseLetterCharacterSet传递给formUnionWithCharacterSet:来构建一个字符集。请注意,letterCharacterSet也包括标记,因此使用小写和大写版本。 - kadam
哇,出于某种原因这个非常慢。如果你正在迭代一个数据集,请小心。 - JRam13

76

无需像其他答案中建议的那样使用正则表达式库--你需要使用的类名为NSScanner。使用方法如下:

NSString *originalString = @"(123) 123123 abc";
NSMutableString *strippedString = [NSMutableString 
        stringWithCapacity:originalString.length];

NSScanner *scanner = [NSScanner scannerWithString:originalString];
NSCharacterSet *numbers = [NSCharacterSet 
        characterSetWithCharactersInString:@"0123456789"];

while ([scanner isAtEnd] == NO) {
  NSString *buffer;
  if ([scanner scanCharactersFromSet:numbers intoString:&buffer]) {
    [strippedString appendString:buffer];

  } else {
    [scanner setScanLocation:([scanner scanLocation] + 1)];
  }
}

NSLog(@"%@", strippedString); // "123123123"

编辑:我已经更新了代码,因为原始代码是凭空想象的,我认为指导人们正确方向就足够了。看起来人们想要的是可以直接复制粘贴到他们的应用程序中的代码。

我也同意Michael Pelz-Sherman的解决方案比使用NSScanner更合适,所以你可能想看一下它。


+1 优秀的回答,直接回答了问题。我编辑了我的回答来倡导这种方法,但我仍然保留了后半部分,因为它仍然有帮助,解决了电话号码显示格式的问题。 (接下来,如果您只是为了日后的读者,能否在投票否定时留下建设性的评论?) - Quinn Taylor
4
知道 NSCharacterSet 的 +decimalDigitCharacterSet 方法可以获取所有十进制数字字符集可能很方便。这个字符集与 Nathan 列出的有些不同,因为它包括所有表示十进制数字的符号,包括如阿拉伯-印度数字(١٢٣٤٥等)。根据您的应用程序,这可能偶尔会成为一个问题,但通常它要么很好要么中性,并且缩短了输入长度。 - Rob Napier
4
太复杂了。 - ryyst
使用您的答案(最有效率的)在一个类别中,使其更容易实现 - https://dev59.com/OXNA5IYBdhLWcg3wAI3d#17458705 - BadPirate
当您希望过滤除了任意字符集之外的所有字符时,此答案适用。例如,我需要过滤掉十六进制字符串"<ab73f109 e87700bc>"中的额外字符。我的characterSetWithCharactersInString是"0123456789ABCDEFabcdef"。 - Blisterpeanuts
显示剩余7条评论

64

被采纳的答案对于问题来说过于复杂了。这个更简单:

NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];

3
目前被接受的答案与这个答案基本相同,但是早在13个月前就已经发布了。 - Caleb
在我回答这个问题的时候,它还没有这个答案。虽然现在似乎已经有了一个提出的答案,但我错过了它:https://web.archive.org/web/20101115214033/https://dev59.com/OXNA5IYBdhLWcg3wAI3d - Yacine Filali

30

这很好,但是这段代码在我的 iPhone 3.0 SDK 上无法工作。

如果我像你展示的这样定义 strippedString,当我尝试在 scanCharactersFromSet:intoString 调用后打印它时,会出现 BAD ACCESS 错误

如果我这样做:

NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];

我的代码最终产生了一个空字符串,但是程序没有崩溃。

我不得不转而使用老派的C语言:

for (int i=0; i<[phoneNumber length]; i++) {
    if (isdigit([phoneNumber characterAtIndex:i])) {
        [strippedString appendFormat:@"%c",[phoneNumber characterAtIndex:i]];
    }
}

我正在运行3.0版本,这对我有效。来自Vries的更受欢迎的答案没有起作用。 - Neo42
第一个答案对我不起作用。扫描器一旦到达()或-就会停止。 这个答案非常好!!好老的C语言!! 谢谢 - Jeff
2
请注意,电话号码中应允许使用“+”字符。 - Krešimir Prcela

27

尽管这是一个已有可行答案的老问题,但我错过了国际格式支持。基于simonobo的解决方案,修改后的字符集包括加号“+”。这个修正也支持国际电话号码。

NSString *condensedPhoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:
              [[NSCharacterSet characterSetWithCharactersInString:@"+0123456789"]
              invertedSet]] 
              componentsJoinedByString:@""];

Swift表达式是:

var phoneNumber = " +1 (234) 567-1000 "
var allowedCharactersSet = NSMutableCharacterSet.decimalDigitCharacterSet()
allowedCharactersSet.addCharactersInString("+")
var condensedPhoneNumber = phoneNumber.componentsSeparatedByCharactersInSet(allowedCharactersSet.invertedSet).joinWithSeparator("")

这会产生+12345671000作为常见的国际电话号码格式。


2
这是列表中最好的解决方案,特别是如果您需要国际电话号码的加号。 - UXUiOS
由于某些原因,使用反转字符集让我感到性能方面有些担忧。不知道是否有人知道这种担忧是否是没有根据的? - devios1
这个有效了!你能解释一下它的工作原理吗?@alex - Jayprakash Dubey

11

这是它的Swift版本。

import UIKit
import Foundation
var phoneNumber = " 1 (888) 555-5551    "
var strippedPhoneNumber = "".join(phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))

Swift 2.0:phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("") - iluvatar_GR

11

最受欢迎答案的Swift版本:

var newString = join("", oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))

编辑:适用于Swift 2的语法

let newString = oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")

编辑:Swift 3的语法

let newString = oldString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")

有没有办法保留小数分隔符?点号(或逗号)作为设备默认设置的一部分?您的解决方案仅保留数字。 - Nicholas

5

谢谢提供这个例子。唯一缺少的是在原始字符串中有一个字符未被发现在数字CharacterSet对象中时,扫描位置的增量。我添加了一个else {}语句来解决这个问题。

NSString *originalString = @"(123) 123123 abc";
NSMutableString *strippedString = [NSMutableString 
        stringWithCapacity:originalString.length];

NSScanner *scanner = [NSScanner scannerWithString:originalString];
NSCharacterSet *numbers = [NSCharacterSet 
        characterSetWithCharactersInString:@"0123456789"];

while ([scanner isAtEnd] == NO) {
  NSString *buffer;
  if ([scanner scanCharactersFromSet:numbers intoString:&buffer]) {
    [strippedString appendString:buffer];
  }
  // --------- Add the following to get out of endless loop
  else {
     [scanner setScanLocation:([scanner scanLocation] + 1)];
  }    
  // --------- End of addition
}

NSLog(@"%@", strippedString); // "123123123"

4

只接受手机号码

NSString * strippedNumber = [mobileNumber stringByReplacingOccurrencesOfString:@"[^0-9]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [mobileNumber length])];

3

值得注意的是,使用componentsSeparatedByCharactersInSet:componentsJoinedByString:方法来实现此功能并不是一种内存高效的解决方案。这会为字符集、数组和新字符串分配内存。即使这些只是临时分配,用这种方式处理大量字符串可能会迅速占满内存。

更加内存友好的方法是在原地对可变副本进行操作。可以在NSString的类别中实现此方法:

-(NSString *)stringWithNonDigitsRemoved {
    static NSCharacterSet *decimalDigits;
    if (!decimalDigits) {
        decimalDigits = [NSCharacterSet decimalDigitCharacterSet];
    }
    NSMutableString *stringWithNonDigitsRemoved = [self mutableCopy];
    for (CFIndex index = 0; index < stringWithNonDigitsRemoved.length; ++index) {
        unichar c = [stringWithNonDigitsRemoved characterAtIndex: index];
        if (![decimalDigits characterIsMember: c]) {
            [stringWithNonDigitsRemoved deleteCharactersInRange: NSMakeRange(index, 1)];
            index -= 1;
        }
    }
    return [stringWithNonDigitsRemoved copy];
}

对这两种方法进行分析表明,使用的内存约减少了2/3。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接