我有一个带括号和连字符的 NSString (电话号码) ,因为一些电话号码是格式化的。如何从字符串中删除除数字以外的所有字符?
如果您只是想从字符串中获取数字,那么 可以当然会使用正则表达式来解析它们。要在 Objective-C 中执行正则表达式,请查看 RegexKit。正如@Nathan 所指出的,使用 NSScanner 解析字符串中的所有数字是一种更简单的方法。我完全不知道这个选项,所以赞扬他的建议。(我自己甚至不喜欢使用 regex,所以我更喜欢不需要它们的方法。)
如果您想格式化电话号码以便显示,那么值得一看 NSNumberFormatter。我建议你通读 这个相关的 SO 问题以获得这样做的技巧。请记住,电话号码的格式根据位置和/或地区的不同而不同。
没有必要像其他答案所建议的那样使用正则表达式库——您要使用的类名为 NSScanner。用法如下:
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更合适,所以您可能想看看这个。
这很棒,但是这些代码在 iPhone 3.0 SDK 上对我不起作用。
如果我像您在这里显示的那样定义 strippedString,那么在 scanCharactersFromSet:intoString调用之后尝试打印它时,我会得到一个 BAD ACCESS error。
scanCharactersFromSet:intoString
BAD ACCESS error
如果我这样做:
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]]; } }
嗯。第一个答案在我看来完全错了。NSScanner 实际上是用于解析的。与正则表达式不同,它让您一次解析一小块字符串。你用一个字符串初始化它,它维护一个索引,表示它沿着字符串走了多远; 这个索引总是它的参考点,你给它的任何命令都是相对于这个点的。你告诉它,“好的,给我这个集合中的下一个字符块”或者“给我你在字符串中找到的整数”,这些都从当前索引开始,然后向前移动,直到找到不匹配的字符。如果第一个字符已经不匹配,那么该方法返回 NO,而索引不会增加。
第一个例子中的代码是扫描“(123-RRB-456-7890)十进制字符,从第一个字符开始就已经失败了,所以调用 scanPersontersFromSet: intoString: 只留下传入的 strippedString,并返回 NO; 代码完全忽略检查返回值,留下 strippedString 未分配。即使第一个字符是一个数字,这个代码也会失败,因为它只会返回找到的数字,直到第一个破折号或括号或其他什么。
如果你真的想要使用 NSScanner,你可以把这样的东西放在一个循环中,并不断检查一个 NO 返回值,如果你得到的,你可以增加 ScanLocation 并再次扫描; 你还必须检查 isAtEnd,和 yada yada yada。简而言之,这项工作的错误工具。Michael 的解决方案更好。
老问题了,不如这样:
NSString *newString = [[origString componentsSeparatedByCharactersInSet: [[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
它在非数字集合上爆炸源字符串,然后使用空字符串分隔符重新组合它们。不像挑选字符那样有效,但代码更紧凑。
谢谢你的例子。它只有一个地方缺少 ScanLocation 的增量,以防在数字 CharterSet 对象中找不到 OrigalString 中的一个字符。我已经添加了 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"
NSString *originalPhoneNumber = @"(123) 123-456 abc"; NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet]; NSString *trimmedPhoneNumber = [originalPhoneNumber stringByTrimmingCharactersInSet:numbers];
];
简单点!
对于被问到的问题,人们普遍接受的答案有些过头了。这个问题要简单得多:
NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
虽然这是一个老问题与工作的答案,我错过了 国际格式支持。基于 Simonobo 解,修改后的字符集包含一个加号“ +”。这项修正案也支持国际电话号码。
NSString *condensedPhoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet: [[NSCharacterSet characterSetWithCharactersInString:@"+0123456789"] invertedSet]] componentsJoinedByString:@""];
斯威夫特的表达式是
var phoneNumber = " +1 (234) 567-1000 " var allowedCharactersSet = NSMutableCharacterSet.decimalDigitCharacterSet() allowedCharactersSet.addCharactersInString("+") var condensedPhoneNumber = phoneNumber.componentsSeparatedByCharactersInSet(allowedCharactersSet.invertedSet).joinWithSeparator("")
它产生 + 12345671000作为一个通用的国际电话号码格式。
对于那些搜索电话提取的人,您可以使用 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]); } } } }
`
我在 NSString 上创建了一个类别来简化这个常见操作。
@interface NSString (AllowCharactersInSet) - (NSString *)stringByAllowingOnlyCharactersInSet:(NSCharacterSet *)characterSet; @end
@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
将顶级解决方案作为一个类别来帮助解决更广泛的问题:
界面:
@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:@""];
可以对可变字符串使用正则表达式:
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: @"[^\\d]" options:0 error:nil]; [regex replaceMatchesInString:str options:0 range:NSMakeRange(0, str.length) withTemplate:@""];
值得注意的是,公认的基于 componentsSeparatedByCharactersInSet:和 componentsJoinedByString:的答案并不是一种节省内存的解决方案。它为字符集、数组和新字符串分配内存。即使这些只是临时分配,以这种方式处理大量字符串也可以快速填满内存。
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。
只接受手机号码
NSString * strippedNumber = [mobileNumber stringByReplacingOccurrencesOfString:@"[^0-9]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [mobileNumber length])];
这是斯威夫特的版本。
import UIKit import Foundation var phoneNumber = " 1 (888) 555-5551 " var strippedPhoneNumber = "".join(phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))
最受欢迎答案的快速版本:
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: "")
基于 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 的问题,但是它可以很容易地修改为留下电话号码相关的字符,如“ ,; * # +”
let notNumberCharacters = NSCharacterSet.decimalDigits.inverted let intString = yourString.trimmingCharacters(in: notNumberCharacters)
我认为目前最好的办法是:
phoneNumber.replacingOccurrences(of: "\\D", with: "", options: String.CompareOptions.regularExpression)
Swift 4.1
var str = "75003 Paris, France" var stringWithoutDigit = (str.components(separatedBy:CharacterSet.decimalDigits)).joined(separator: "") print(stringWithoutDigit)
Swift 5
let newString = origString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")