从字符串中过滤非数字字符

69

仅使用 Swift 代码,我无法弄清如何获取“(555) 555-5555”并仅返回数字值以获得“5555555555”。我需要删除所有括号、空格和破折号。我能找到的唯一示例都是在 Objective-C 中,并且它们似乎都使用 .trim() 方法。似乎 Swift 没有这种方法,但它确实具有 .stringByTrimmingCharacters 方法,但这只会修剪数据前后的空格。


4
由于“trim”方法只会从字符串的开头或结尾删除字符,所以这些方法都不适用。 - rmaddy
12个回答

130
Swift 3和4
extension String {
    var digits: String {
        return components(separatedBy: CharacterSet.decimalDigits.inverted)
            .joined()
    }
}

Swift 5
您应该能够省略 return 关键字。
另外: 请阅读 @onmyway133 的评论,注意一些问题。

16
return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()翻译:将字符串按非数字字符分割,并连接所有分割后的子串。 - Leo Dabus
3
请注意,decimalDigits 可能包含不仅仅是数字 https://ravron.com/2019/07/how-many-decimal-digits-are-there-anyways/ - onmyway133

65

将字符串按非数字字符拆分为数字数组,然后将它们连接起来成为一个字符串:

Swift 1:

let stringArray = origString.componentsSeparatedByCharactersInSet(
    NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let newString = NSArray(array: stringArray).componentsJoinedByString("")

Swift 2:

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

Swift 3 & 4:

Swift 3和4:

let newString = origString
    .components(separatedBy:CharacterSet.decimalDigits.inverted)
    .joined()

不错的想法,翻转了decimalDigitCharacterSet。 - NSWill
1
这为我节省了一些时间,作为建议,第二行可以被简单地替换为stringArray.joinWithSeperator("")。 - Derek Hewitt
不过滤'&'或'$'! - Ivan

22

我喜欢正则表达式:

var s = "(555) 555-5555"
s = s.stringByReplacingOccurrencesOfString(
    "\\D", withString: "", options: .RegularExpressionSearch, 
    range: s.startIndex..<s.endIndex)

6
谢谢你的回答!只是有一个小补充:我发现更好的正则表达式是[^\\d+],这个正则会保留加号的有效性。 - jeppeb

15

在 Swift 4 中,解决方案更加优美:

import Foundation

let sourceText = "+5 (555) 555-5555"

let allowedCharset = CharacterSet
    .decimalDigits
    .union(CharacterSet(charactersIn: "+"))

let filteredText = String(sourceText.unicodeScalars.filter(allowedCharset.contains))

print(filteredText) // +55555555555

10

以下是@Tapani的Swift 2.0答案,作为一个方便的String扩展(长度属性不是解决方案的一部分,但我在示例中保留它,因为它也很方便):

import Foundation

extension String {

    var length : Int {
        return self.characters.count
    }

    func digitsOnly() -> String{
        let stringArray = self.componentsSeparatedByCharactersInSet(
            NSCharacterSet.decimalDigitCharacterSet().invertedSet)
        let newString = stringArray.joinWithSeparator("")

        return newString
    }

}

使用方法:

let phone = "(123)-123 - 1234"
print(phone.digitsOnly())

7

我遇到了类似的问题,但是需要保留小数点。我对这个顶部答案进行了微调:

extension String {

    /// Returns a string with all non-numeric characters removed
    public var numericString: String {
        let characterSet = CharacterSet(charactersIn: "0123456789.").inverted
        return components(separatedBy: characterSet)
            .joined()
    }
}

6

细节

  • Xcode 版本 10.2.1 (10E1001),Swift 5

解决方案

import Foundation

extension String {

    private func filterCharacters(unicodeScalarsFilter closure: (UnicodeScalar) -> Bool) -> String {
        return String(String.UnicodeScalarView(unicodeScalars.filter { closure($0) }))
    }

    private func filterCharacters(definedIn charSets: [CharacterSet], unicodeScalarsFilter: (CharacterSet, UnicodeScalar) -> Bool) -> String {
        if charSets.isEmpty { return self }
        let charSet = charSets.reduce(CharacterSet()) { return $0.union($1) }
        return filterCharacters { unicodeScalarsFilter(charSet, $0) }
    }

    func removeCharacters(charSets: [CharacterSet]) -> String { return filterCharacters(definedIn: charSets) { !$0.contains($1) } }
    func removeCharacters(charSet: CharacterSet) -> String { return removeCharacters(charSets: [charSet]) }

    func onlyCharacters(charSets: [CharacterSet]) -> String { return filterCharacters(definedIn: charSets) { $0.contains($1) } }
    func onlyCharacters(charSet: CharacterSet) -> String { return onlyCharacters(charSets: [charSet]) }
}

使用方法

let string = "23f45gdor#@%#i425v wer 24 1+DWEJwi 3u09ru49w*()9uE2R_)$I#Q)_ U383q04+RFJO{dgnkvlj b`kefl;nwdl qsa`WKFSA,.E"
print("original string:                                 \(string)")
print("only .decimalDigits:                             \(string.onlyCharacters(charSet: .decimalDigits))")
print("only [.lowercaseLetters, .symbols]:              \(string.onlyCharacters(charSets: [.lowercaseLetters, .symbols]))")
print("remove .letters:                                 \(string.removeCharacters(charSet: .letters))")
print("remove [.decimalDigits, .lowercaseLetters]:      \(string.removeCharacters(charSets: [.decimalDigits, .lowercaseLetters]))")

结果

original string:                                 23f45gdor#@%#i425v wer 24 1+DWEJwi 3u09ru49w*()9uE2R_)$I#Q)_ U383q04+RFJO{dgnkvlj b`kefl;nwdl qsa`WKFSA,.E
only .decimalDigits:                             2345425241309499238304
only [.lowercaseLetters, .symbols]:              fgdorivwer+wiuruwu$q+dgnkvljb`keflnwdlqsa`
remove .letters:                                 2345#@%#425  24 1+ 30949*()92_)$#)_ 38304+{ `; `,.
remove [.decimalDigits, .lowercaseLetters]:      #@%#   +DWEJ *()ER_)$I#Q)_ U+RFJO{ `; `WKFSA,.E

(可选) 字符串扩展

extension String {
    var onlyDigits: String { return onlyCharacters(charSets: [.decimalDigits]) }
    var onlyLetters: String { return onlyCharacters(charSets: [.letters]) }
}

(可选) 字符串扩展用法

let string = "23f45gdor#@%#i425v wer 24 1+DWEJwi 3u09ru49w*()9uE2R_)$I#Q)_ U383q04+RFJO{dgnkvlj b`kefl;nwdl qsa`WKFSA,.E"
print("original string:     \(string)")
print(".onlyDigits:         \(string.onlyDigits)")
print(".onlyLetters:        \(string.onlyLetters)")

(可选)字符串扩展使用结果

original string:     23f45gdor#@%#i425v wer 24 1+DWEJwi 3u09ru49w*()9uE2R_)$I#Q)_ U383q04+RFJO{dgnkvlj b`kefl;nwdl qsa`WKFSA,.E
.onlyDigits:         2345425241309499238304
.onlyLetters:        fgdorivwerDWEJwiuruwuERIQUqRFJOdgnkvljbkeflnwdlqsaWKFSAE

4

试试这个:

let string = "(555) 555-5555"
let digitString = string.filter { ("0"..."9").contains($0) }
print(digitString) // 5555555555

安装扩展:

extension String
{
    var digitString: String { filter { ("0"..."9").contains($0) } }
}

print("(555) 555-5555".digitString) // 5555555555

1
多么简单优雅,这里是最佳答案!更短的写法是 string.filter(("0"..."9").contains) - ramzesenok

2

您需要使用NSCharacterSet:

请查看此NSHipster链接以获取Swift和Obj-C实现:http://nshipster.com/nscharacterset/

类似的示例:

var string = "  Lorem    ipsum dolar   sit  amet. "

let components = string.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).filter({!isEmpty($0)})

string = join(" ", components)

请参阅:punctuationCharacterSet

描述:

返回一个包含标点符号类别中的字符集。简而言之,此集合是用于在脚本中分隔语言单元的所有非空格字符的集合,例如句号、破折号、括号等。

@Tapani 给出了很好的建议:NSCharacterSet.decimalDigitCharacterSet().invertedSet


1

我用 filter 函数找到了最佳解决方案。请看一下。

let string = "(555) 555-5555"

let onlyDigits = string.filter({ (char) -> Bool in

    if Int("\(char)") != nil {

        return true
    }
    else {

        return false
    }

})

let onlyDigits = string.filter{ Int("\($0)") != nil } - b m gevariya
让 onlyDigits = string.filter{ch in "0123456789".contains(ch)}。 - altimes
@altimes 这会增加运行时的复杂度。 - Naresh
有趣的想法。我一直以为解析和构建Int会更加复杂。但是,仔细看看,它只需要处理一个单一字符,可能在底层只需要进行一些简单的ASCII值操作。 - altimes

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