从NSString中删除所有数字


157

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

Answers:


375

旧问题,但是如何:

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

它会爆炸一组非数字来源字符串,然后使用空字符串分隔符重新组合它们。效率不及挑选字符,但代码紧凑得多。


6
谢谢!对于其他初学者,您可以执行以下操作来创建自己的自定义NSCharacterSetNSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]
guptron 2013年

1
非常感谢!仅出于我的好奇心,您是否知道为什么NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]不工作?
Thomas Besnehard

1
@Tommecpe stringByTrimmingCharactersInSet仅从字符串的开头和结尾删除,因此在第一个不匹配字符之后或最后一个不匹配字符之前不起作用。
simonobo

我只想保留数字和字母,我该怎么办?
Jacky

1
在上面的示例中,@ Jacky您将替换[NSCharacterSet decimalDigitCharacterSet]为仅包含数字和字母的另一个。您可以通过创建一个构造一个NSMutableCharaterSet和传递decimalDigitCharacterSetuppercaseLetterCharacterSetlowercaseLetterCharacterSetformUnionWithCharacterSet:。请注意,letterCharacterSet它还包括标记,因此使用小写和大写版本。
kadam

75

正如其他答案所建议的那样,无需使用正则表达式库-您所追求的类称为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列表略有不同,因为它包括代表十进制数字的所有符号,例如阿拉伯数字(١٢٣٤٥等)。根据您的应用程序,有时可能会出现问题,但通常情况是良好的或中性的,并且类型要短一些。
罗布·纳皮尔

我很确定这个答案实际上是行不通的,并且不是解决问题的正确方法。如果您实际尝试显示的代码(首先在NSLog的第一个参数之前添加一个@,使其成为objc字符串),您会发现它要么打印<null>要么崩溃。为什么?请参阅下面的答案。
杰克·纳丁

无需其他答案-这就是评论的意思。我已经更新了解决方案,包括对Michael Pelz-Sherman解决方案的引用。
内森·德弗里斯2009年

4
这很复杂。
ryyst

63

接受的答案是对所要求的内容的过度杀伤。这要简单得多:

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

2
(当前)接受的答案与该答案基本相同,但在13个月前发布。
Caleb 2015年

在我回答这个问题时,它没有这个回答。虽然它似乎已经提出了当前的答案,我错过了:web.archive.org/web/20101115214033/http://stackoverflow.com/...
亚辛·菲拉利

30

很好,但是代码对我而言不适用于iPhone 3.0 SDK。

如果我在这里显示的那样定义了strippedString,那么BAD ACCESS errorscanCharactersFromSet: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]];
    }
}

我正在运行3.0,这对我有效。来自Vries的更流行的答案没有用。
Neo42,2009年

第一答案对我不起作用。扫描仪到达()或-时将停止扫描-此答案非常有用!好老C!谢谢
Jeff

2
仅注意,电话号码应允许使用“ +”字符。
Prcela 2011年

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 2012年

由于某种原因,就性能而言,使用反向字符集会使我感到恐惧。有人碰巧知道这是没有根据的恐惧吗?
devios1 2013年

这个工作了!您能解释一下它的工作原理吗?@alex
Jayprakash Dubey

11

这是Swift的版本。

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

迅速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对象中找不到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"

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。


2

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

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

[regex replaceMatchesInString:str
                      options:0 
                        range:NSMakeRange(0, str.length) 
                 withTemplate:@""];

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:@""];

1

迅捷3

let notNumberCharacters = NSCharacterSet.decimalDigits.inverted
let intString = yourString.trimmingCharacters(in: notNumberCharacters)

这只会从开头和结尾修剪非数字字符。
Shebuka

1

迅捷4.1

var str = "75003 Paris, France"
var stringWithoutDigit = (str.components(separatedBy:CharacterSet.decimalDigits)).joined(separator: "")
print(stringWithoutDigit)

0

嗯 第一个答案对我来说似乎是完全错误的。NSScanner实际上是用于解析的。与正则表达式不同,它让您一次将字符串解析为一个很小的块。您用一个字符串初始化它,并维护它沿字符串走多远的索引。该索引始终是其参考点,您提供的所有命令都相对于该索引。您告诉它,“好吧,请给我这个字符集中的下一个字符块”或“给我在字符串中找到的整数”,这些字符将从当前索引开始,然后继续前进,直到找到找不到的字符为止比赛。如果第一个字符已经不匹配,则该方法返回NO,并且索引不会递增。

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

如果您真的想使用NSScanner,则可以将类似的内容放入循环中,并继续检查是否有NO返回值,如果得到,则可以增加scanLocation并再次扫描;并且还必须检查isAtEnd和yada yada yada。简而言之,错误的工作工具。迈克尔的解决方案更好。


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]);
            }
        }
    }
}

`


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

0

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

phoneNumber.replacingOccurrences(of: "\\D",
                               with: "",
                            options: String.CompareOptions.regularExpression)

0

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

如果要格式化电话号码以进行显示,则值得一看NSNumberFormatter。我建议您通读此相关的SO问题以获取有关此操作的提示。请记住,电话号码的格式取决于位置和/或区域设置。


噢,我花了很多时间来开发好的电话号码格式器和解析器。链接线程是一个好的开始,但是格式化全局电话号码以进行显示的一般情况是漫长的路要走,而且正如链接线程中指出的那样,Apple不允许您访问通讯录电话号码格式器,并且在地址簿API中如何显示电话号码非常不一致。比格式化显示电话号码更难的一件事情就是确定两个电话号码是否相等。至少OP的问题是最简单的问题。
罗布·纳皮尔

我认为,除非您对以美国为中心的原始实现方式感到满意,否则这些指向格式化电话号码的链接会产生误导。代替Apple正确的本地电话号码格式器,正确执行此操作的唯一方法是从设备(OS 2.x中的UIPhoneFormats.plist)复制格式模板,然后根据用户的区域设置自行复制模板。这是一项艰巨的任务。
内森·德弗里斯

这就是为什么我提到数字格式化程序的本地化的原因。我没有假装为此发布任何形式的完整解决方案-这是一个更长的讨论,将其作为一个单独的SO问题更有意义。
Quinn Taylor


-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的问题,但是可以很容易地对其进行修改以保留与电话号码相关的字符,例如“,; *#+”


-4
NSString *originalPhoneNumber = @"(123) 123-456 abc";
NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
NSString *trimmedPhoneNumber = [originalPhoneNumber stringByTrimmingCharactersInSet:numbers];

];

把事情简单化!


3
这样只会从头到尾修剪这些字符。
raidfive
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.