Answers:
旧问题,但是如何:
NSString *newString = [[origString componentsSeparatedByCharactersInSet:
[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
componentsJoinedByString:@""];
它会爆炸一组非数字来源字符串,然后使用空字符串分隔符重新组合它们。效率不及挑选字符,但代码紧凑得多。
NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]
不工作?
[NSCharacterSet decimalDigitCharacterSet]
为仅包含数字和字母的另一个。您可以通过创建一个构造一个NSMutableCharaterSet
和传递decimalDigitCharacterSet
,uppercaseLetterCharacterSet
并lowercaseLetterCharacterSet
到formUnionWithCharacterSet:
。请注意,letterCharacterSet
它还包括标记,因此使用小写和大写版本。
正如其他答案所建议的那样,无需使用正则表达式库-您所追求的类称为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
,因此您可能需要看一下。
接受的答案是对所要求的内容的过度杀伤。这要简单得多:
NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
很好,但是代码对我而言不适用于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]];
}
}
尽管这是一个有工作答案的老问题,但我错过了国际格式的支持。基于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作为常见的国际电话号码格式。
这是Swift的版本。
import UIKit
import Foundation
var phoneNumber = " 1 (888) 555-5551 "
var strippedPhoneNumber = "".join(phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))
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: "")
谢谢你的例子。万一在数字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"
这可能是值得一提的是,接受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。
将顶级解决方案作为一个类别来解决更广泛的问题:
接口:
@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:@""];
嗯 第一个答案对我来说似乎是完全错误的。NSScanner实际上是用于解析的。与正则表达式不同,它让您一次将字符串解析为一个很小的块。您用一个字符串初始化它,并维护它沿字符串走多远的索引。该索引始终是其参考点,您提供的所有命令都相对于该索引。您告诉它,“好吧,请给我这个字符集中的下一个字符块”或“给我在字符串中找到的整数”,这些字符将从当前索引开始,然后继续前进,直到找到找不到的字符为止比赛。如果第一个字符已经不匹配,则该方法返回NO,并且索引不会递增。
第一个示例中的代码正在扫描“(123)456-7890”以查找十进制字符,该字符从第一个字符开始就已经失败,因此对scanCharactersFromSet:intoString:的调用将传入的strippedString留为单独,并返回NO;该代码完全忽略了检查返回值,而未分配strippedString。即使第一个字符是数字,该代码也会失败,因为它只会返回找到的数字,直到第一个破折号或括号或任何其他内容为止。
如果您真的想使用NSScanner,则可以将类似的内容放入循环中,并继续检查是否有NO返回值,如果得到,则可以增加scanLocation并再次扫描;并且还必须检查isAtEnd和yada yada yada。简而言之,错误的工作工具。迈克尔的解决方案更好。
对于那些搜索电话提取的用户,可以使用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
如果您只是想从字符串中获取数字,则可以使用正则表达式将其解析出来。要在Objective-C中进行正则表达式,请查看RegexKit。 编辑: 正如@Nathan指出的那样,使用NSScanner是解析字符串中所有数字的一种简单得多的方法。我完全不知道该选项,因此建议他提出建议。(我什至不喜欢自己使用正则表达式,因此我更喜欢不需要它们的方法。)
如果要格式化电话号码以进行显示,则值得一看NSNumberFormatter。我建议您通读此相关的SO问题以获取有关此操作的提示。请记住,电话号码的格式取决于位置和/或区域设置。
迅捷5
let newString = origString.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的问题,但是可以很容易地对其进行修改以保留与电话号码相关的字符,例如“,; *#+”
NSString *originalPhoneNumber = @"(123) 123-456 abc";
NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
NSString *trimmedPhoneNumber = [originalPhoneNumber stringByTrimmingCharactersInSet:numbers];
];
把事情简单化!
NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]