在Swift中从字符串中删除所有非数字字符

123

我需要解析一些未知数据,其中应该只包含数字值,但可能包含空格或其他非字母数字字符。

在Swift中有没有新的方法来做这件事?我在网上找到的所有内容似乎都是用旧的C方式完成的。

我正在查看 stringByTrimmingCharactersInSet - 因为我确信我的输入字符串只会在开头或结尾包含空格/特殊字符。是否有任何内置的字符集可供使用?还是我需要创建自己的?

我希望有像stringFromCharactersInSet()这样的东西,它允许我指定要保留的有效字符。

17个回答

249
我希望有类似于stringFromCharactersInSet()的东西,可以让我指定只保留有效字符。
您可以使用trimmingCharacters和inverted字符集来从字符串的开头或结尾删除字符。在Swift 3及更高版本中:
let result = string.trimmingCharacters(in: CharacterSet(charactersIn: "0123456789.").inverted)

或者,如果你想在字符串中的任何位置移除非数字字符(而不仅仅是开头或结尾),你可以使用`filter`函数过滤这些字符,例如在Swift 4.2.1中:
let result = string.filter("0123456789.".contains)

或者,如果你想从字符串的任何位置删除一个字符集合CharacterSet中的字符,请使用:
let result = String(string.unicodeScalars.filter(CharacterSet.whitespaces.inverted.contains))

或者,如果你只想匹配特定格式的有效字符串(例如####.##),你可以使用正则表达式。例如,使用较新的正则表达式字面量(如WWDC 2022的视频了解 Swift 正则表达式Swift 正则表达式:超越基础知识中讨论的),将正则表达式用/字符括起来:
if let range = string.firstRange(of: /\d+(\.\d*)?/) {
    let result = string[range] // or `String(string[range])` if you need `String`
}

或者,使用旧的range(of:options:).regularExpression选项:
if let range = string.range(of: #"\d+(\.\d*)?"#, options: .regularExpression) {
    let result = string[range] // or `String(string[range])` if you need `String`
}

这些不同方法的行为略有不同,所以它取决于你想要做什么。如果你想要小数,可以包括或排除小数点;如果只需要整数,也可以如此。有很多方法可以实现这一点。
对于旧版本的Swift 2语法,请参考此答案的先前修订版本

你能解释一下为什么在Swift 3的示例中需要对字符集进行“inverted”操作吗? - Andy Ibanez
4
就像说,如果“ABC”是我想保留的字符,那么就将不是“ABC”的所有内容都删除。 - Scott McKenzie
4
在Swift 4.2.1中,let result = String(string.characters.filter { "01234567890.".characters.contains($0) }) 可以缩短为 let result = string.filter("01234567890.".contains) - Leo Dabus

47
let result = string.stringByReplacingOccurrencesOfString("[^0-9]", withString: "", options: NSStringCompareOptions.RegularExpressionSearch, range:nil).stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())

Swift 3

let result = string.replacingOccurrences( of:"[^0-9]", with: "", options: .regularExpression)

你可以给这个答案投赞成票。


3
谢谢!在获取所有Swift3更改后,我剩下了这个代码:myStr.replacingOccurrences(of:"[^0-9]", with: "", options: .regularExpression)。 - bobwki

34

我更喜欢这个解决方案,因为我喜欢扩展,并且对我来说看起来更加简洁。这里重现了解决方案:

extension String {
    var digits: String {
        return components(separatedBy: CharacterSet.decimalDigits.inverted)
            .joined()
    }
}

1
似乎会去除小数点 - markturnip

25
你可以使用范围匹配运算符来过滤字符串的UnicodeScalarView,传递一个从0到9的UnicodeScalar闭合范围,并使用得到的UnicodeScalarView初始化一个新的String:
extension String {
    private static var digits = UnicodeScalar("0")..."9"
    var digits: String {
        return String(unicodeScalars.filter(String.digits.contains))
    }
}

"abc12345".digits   // "12345"

编辑/更新:

Swift 4.2

extension RangeReplaceableCollection where Self: StringProtocol {
    var digits: Self {
        return filter(("0"..."9").contains)
    }
}

或作为一种可变方法

extension RangeReplaceableCollection where Self: StringProtocol {
    mutating func removeAllNonNumeric() {
        removeAll { !("0"..."9" ~= $0) }
    }
}

Swift 5.2 • Xcode 11.4或更高版本

在Swift5中,我们可以使用一个名为isWholeNumber的新字符属性:

extension RangeReplaceableCollection where Self: StringProtocol {
    var digits: Self { filter(\.isWholeNumber) }
}

extension RangeReplaceableCollection where Self: StringProtocol {
    mutating func removeAllNonNumeric() {
        removeAll { !$0.isWholeNumber }
    }
}

为了允许使用句号,我们可以扩展字符并创建一个计算属性:

extension Character {
    var isDecimalOrPeriod: Bool { "0"..."9" ~= self || self == "." }
}

extension RangeReplaceableCollection where Self: StringProtocol {
    var digitsAndPeriods: Self { filter(\.isDecimalOrPeriod) }
}

游乐场测试:

"abc12345".digits   // "12345"

var str = "123abc0"
str.removeAllNonNumeric()
print(str) //"1230"

"Testing0123456789.".digitsAndPeriods // "0123456789."

Swift 5的好解决方案!我该如何在字符串中保留“.”或“,”,以便能够将字符串转换为Double? - Hans Bondoka

18

Swift 4

我找到了一个不错的方法来从字符串中获取仅包含字母数字字符集的内容。例如:

func getAlphaNumericValue() {

    var yourString  = "123456789!@#$%^&*()AnyThingYouWant"

    let unsafeChars = CharacterSet.alphanumerics.inverted  // Remove the .inverted to get the opposite result.  

    let cleanChars  = yourString.components(separatedBy: unsafeChars).joined(separator: "")


    print(cleanChars)  // 123456789AnyThingYouWant

}

12

使用filter函数和rangeOfCharacterFromSet的解决方案

let string = "sld [f]34é7*˜µ"

let alphaNumericCharacterSet = NSCharacterSet.alphanumericCharacterSet()
let filteredCharacters = string.characters.filter {
  return  String($0).rangeOfCharacterFromSet(alphaNumericCharacterSet) != nil
}
let filteredString = String(filteredCharacters) // -> sldf34é7µ

要仅过滤数字字符,请使用

let string = "sld [f]34é7*˜µ"

let numericSet = "0123456789"
let filteredCharacters = string.characters.filter {
  return numericSet.containsString(String($0))
}
let filteredString = String(filteredCharacters) // -> 347
或者
let numericSet : [Character] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
let filteredCharacters = string.characters.filter {
  return numericSet.contains($0)
}
let filteredString = String(filteredCharacters) // -> 347

我不需要任何 alpha 值,尽管我不指望会有任何。 - Dan
请具体说明一下。标题中写的是“非字母数字字符”;-) 我已经修改了答案,使其包括数字字符。 - vadian

9

Swift 4

但没有扩展或componentsSeparatedByCharactersInSet,这不太容易理解。

let allowedCharSet = NSCharacterSet.letters.union(.whitespaces)
let filteredText = String(sourceText.unicodeScalars.filter(allowedCharSet.contains))

5
let string = "+1*(234) fds567@-8/90-"
let onlyNumbers = string.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()

print(onlyNumbers) // "1234567890"

或者

extension String {

  func removeNonNumeric() -> String {
    return self.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
  }
}

let onlyNumbers = "+1*(234) fds567@-8/90-".removeNonNumeric() 
print(onlyNumbers)// "1234567890"

4

Swift 4.2

let numericString = string.filter { (char) -> Bool in
    return char.isNumber
}

4

Swift 3,过滤除数字以外的所有内容

let myString = "dasdf3453453fsdf23455sf.2234"
let result = String(myString.characters.filter { String($0).rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789")) != nil })
print(result)

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