158

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

4

22 回答 22

378

老问题,但是怎么样:

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

它在非数字集上分解源字符串,然后使用空字符串分隔符重新组合它们。不如挑选字符高效,但代码更紧凑。

于 2009-09-15T12:15:43.973 回答
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 的解决方案比 using 更合适NSScanner,所以你可能想看看。

于 2009-07-15T07:47:38.283 回答
63

接受的答案对于所要求的内容来说太过分了。这要简单得多:

NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
于 2010-10-15T00:04:26.480 回答
30

这很好,但代码在 iPhone 3.0 SDK 上对我不起作用。

如果我像您在此处显示的那样定义 strippedString,则在调用BAD ACCESS error后尝试打印它时会得到一个。scanCharactersFromSet:intoString

如果我这样做:

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]];
    }
}
于 2009-07-22T07:22:15.590 回答
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 作为常见的国际电话号码格式。

于 2012-02-24T11:38:21.420 回答
11

这是这个的 Swift 版本。

import UIKit
import Foundation
var phoneNumber = " 1 (888) 555-5551    "
var strippedPhoneNumber = "".join(phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))
于 2014-12-26T16:35:12.180 回答
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: "")
于 2015-07-14T22:25:36.787 回答
5

谢谢你的例子。如果在数字 CharacterSet 对象中找不到 originalString 中的字符之一,它只有一件事会丢失 scanLocation 的增量。我添加了一个 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"
于 2009-11-24T11:15:53.453 回答
4

它只接受手机号码

NSString * strippedNumber = [mobileNumber stringByReplacingOccurrencesOfString:@"[^0-9]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [mobileNumber length])];
于 2014-12-02T10:18:17.743 回答
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 的内存。

于 2014-09-17T17:26:28.540 回答
2

您可以在可变字符串上使用正则表达式:

NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:
                                @"[^\\d]"
                                options:0
                                error:nil];

[regex replaceMatchesInString:str
                      options:0 
                        range:NSMakeRange(0, str.length) 
                 withTemplate:@""];
于 2014-06-17T08:04:46.327 回答
1

将顶级解决方案作为一个类别来帮助解决更广泛的问题:

界面:

@interface NSString (easyReplace)
- (NSString *)stringByReplacingCharactersNotInSet:(NSCharacterSet *)set 
                                             with:(NSString *)string;
@end

实施:

@implementation NSString (easyReplace)
- (NSString *)stringByReplacingCharactersNotInSet:(NSCharacterSet *)set 
                                             with:(NSString *)string
{
    NSMutableString *strippedString = [NSMutableString
                                       stringWithCapacity:self.length];

    NSScanner *scanner = [NSScanner scannerWithString:self];

    while ([scanner isAtEnd] == NO) {
        NSString *buffer;
        if ([scanner scanCharactersFromSet:set intoString:&buffer]) {
            [strippedString appendString:buffer];
        } else {
            [scanner setScanLocation:([scanner scanLocation] + 1)];
            [strippedString appendString:string];
        }
    }
    return [NSString stringWithString:strippedString];
}
@end

用法:

NSString *strippedString = 
 [originalString stringByReplacingCharactersNotInSet:
   [NSCharacterSet setWithCharactersInString:@"01234567890" 
                                        with:@""];
于 2013-07-03T21:50:51.580 回答
1

斯威夫特 3

let notNumberCharacters = NSCharacterSet.decimalDigits.inverted
let intString = yourString.trimmingCharacters(in: notNumberCharacters)
于 2016-11-15T21:09:12.800 回答
1

迅捷4.1

var str = "75003 Paris, France"
var stringWithoutDigit = (str.components(separatedBy:CharacterSet.decimalDigits)).joined(separator: "")
print(stringWithoutDigit)
于 2018-08-14T17:47:07.833 回答
0

如果您只是想从字符串中获取数字,您当然可以使用正则表达式来解析它们。要在 Objective-C 中执行正则表达式,请查看RegexKit 编辑: 正如@Nathan 指出的那样,使用 NSScanner 是一种更简单的方法来解析字符串中的所有数字。我完全不知道这个选项,所以支持他的建议。(我什至不喜欢自己使用正则表达式,所以我更喜欢不需要它们的方法。)

如果您想格式化电话号码以供显示,值得一看NSNumberFormatter。我建议您通读这个相关的 SO question以获取有关这样做的提示。请记住,电话号码的格式因位置和/或区域设置而异。

于 2009-07-15T05:36:53.120 回答
0

嗯。第一个答案对我来说似乎完全错误。NSScanner 真的是用来解析的。与正则表达式不同,它让您一次解析一小块字符串。你用一个字符串初始化它,它会维护一个索引,说明它沿着字符串有多远;该索引始终是它的参考点,您给它的任何命令都与该点相关。你告诉它,“好吧,给我这个集合中的下一个字符块”或“给我你在字符串中找到的整数”,然后这些从当前索引开始,然后继续前进,直到他们找到不匹配。如果第一个字符已经不匹配,则该方法返回 NO,并且索引不会增加。

第一个示例中的代码正在扫描“(123)456-7890”中的十进制字符,这已经从第一个字符开始就失败了,所以对 scanCharactersFromSet:intoString: 的调用只留下传入的 strippedString,并返回 NO;该代码完全忽略了检查返回值,而未分配 strippedString。即使第一个字符是数字,该代码也会失败,因为它只会返回找到的数字,直到第一个破折号或括号或其他任何东西。

如果你真的想使用 NSScanner,你可以把类似的东西放在一个循环中,并继续检查 NO 返回值,如果你得到了,你可以增加 scanLocation 并再次扫描;你还必须检查 isAtEnd 和 yada yada yada。简而言之,错误的工作工具。迈克尔的解决方案更好。

于 2009-07-22T13:24:49.190 回答
0

对于那些搜索电话提取的人,您可以使用 NSDataDetector 从文本中提取电话号码,例如:

NSString *userBody = @"This is a text with 30612312232 my phone";
if (userBody != nil) {
    NSError *error = NULL;
    NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypePhoneNumber error:&error];
    NSArray *matches = [detector matchesInString:userBody options:0 range:NSMakeRange(0, [userBody length])];
    if (matches != nil) {
        for (NSTextCheckingResult *match in matches) {
            if ([match resultType] == NSTextCheckingTypePhoneNumber) {
                DbgLog(@"Found phone number %@", [match phoneNumber]);
            }
        }
    }
}

`

于 2012-04-14T22:44:59.757 回答
0

我在 NSString 上创建了一个类别来简化这个常见的操作。

NSString+AllowCharactersInSet.h

@interface NSString (AllowCharactersInSet)

- (NSString *)stringByAllowingOnlyCharactersInSet:(NSCharacterSet *)characterSet;

@end

NSString+AllowCharactersInSet.m

@implementation NSString (AllowCharactersInSet)

- (NSString *)stringByAllowingOnlyCharactersInSet:(NSCharacterSet *)characterSet {
    NSMutableString *strippedString = [NSMutableString
                                   stringWithCapacity:self.length];

    NSScanner *scanner = [NSScanner scannerWithString:self];

    while (!scanner.isAtEnd) {
        NSString *buffer = nil;

        if ([scanner scanCharactersFromSet:characterSet intoString:&buffer]) {
            [strippedString appendString:buffer];
        } else {
            scanner.scanLocation = scanner.scanLocation + 1;
        }
    }

    return strippedString;
}

@end
于 2012-09-05T00:35:12.703 回答
0

我认为目前最好的方法是:

phoneNumber.replacingOccurrences(of: "\\D",
                               with: "",
                            options: String.CompareOptions.regularExpression)
于 2017-04-17T11:31:42.583 回答
0

斯威夫特 5

let newString = origString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
于 2019-06-21T04:54:04.997 回答
-1

根据 Jon Vogel 的回答,它是一个 Swift String 扩展以及一些基本测试。

import Foundation
extension String {
    func stringByRemovingNonNumericCharacters() -> String {
        return self.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
    }
}

一些测试至少证明了基本功能:

import XCTest

class StringExtensionTests: XCTestCase {

    func testStringByRemovingNonNumericCharacters() {

        let baseString = "123"
        var testString = baseString
        var newString = testString.stringByRemovingNonNumericCharacters()
        XCTAssertTrue(newString == testString)

        testString = "a123b"
        newString = testString.stringByRemovingNonNumericCharacters()
        XCTAssertTrue(newString == baseString)

        testString = "a=1-2_3@b"
        newString = testString.stringByRemovingNonNumericCharacters()
        XCTAssertTrue(newString == baseString)

        testString = "(999) 999-9999"
        newString = testString.stringByRemovingNonNumericCharacters()
        XCTAssertTrue(newString.characters.count == 10)
        XCTAssertTrue(newString == "9999999999")

        testString = "abc"
        newString = testString.stringByRemovingNonNumericCharacters()
        XCTAssertTrue(newString == "")
    }
}

这回答了 OP 的问题,但可以很容易地修改它以保留与电话号码相关的字符,如“,;*#+”

于 2016-04-23T15:36:17.960 回答
-4
NSString *originalPhoneNumber = @"(123) 123-456 abc";
NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
NSString *trimmedPhoneNumber = [originalPhoneNumber stringByTrimmingCharactersInSet:numbers];

];

把事情简单化!

于 2010-08-20T20:00:17.190 回答