Швидко видаліть усі нечислові символи з рядка


84

Мені потрібно проаналізувати деякі невідомі дані, які мають бути лише числовим значенням, але можуть містити пробіли або інші нелітерально-цифрові символи.

Чи є новий спосіб зробити це у Свіфті? Мені здається, що все, що я можу знайти в Інтернеті, - це старий спосіб роботи на С.

Я розглядаю stringByTrimmingCharactersInSet- оскільки я впевнений, що мої вводи матимуть лише пробіли / спеціальні символи на початку або в кінці рядка. Чи є якісь вбудовані набори символів, які я можу використовувати для цього? Або мені потрібно створити свою власну?

Я сподівався, що буде щось подібне, stringFromCharactersInSet()що дозволить мені вказати лише дійсні символи, які слід зберегти

Відповіді:


183

Я сподівався, що буде щось на зразок stringFromCharactersInSet (), що дозволить мені вказати лише допустимі символи, які слід зберегти.

Ви можете використовувати або trimmingCharactersз invertedнабором символів, щоб видалити символи з початку або кінця рядка. У Swift 3 і пізніших версіях:

let result = string.trimmingCharacters(in: CharacterSet(charactersIn: "0123456789.").inverted)

Або, якщо ви хочете видалити Ні-цифрові символи в будь-якому місці в рядку ( а не тільки в початку або в кінці), ви можете , наприклад , в Swift 4.2.1:filtercharacters

let result = string.filter("0123456789.".contains)

Або, якщо ви хочете видалити символи з набору символів з будь-якого місця рядка, використовуйте:

let result = String(string.unicodeScalars.filter(CharacterSet.whitespaces.inverted.contains))

Або, якщо ви хочете збігатись лише з дійсними рядками певного формату (наприклад ####.##), ви можете використовувати регулярний вираз. Наприклад:

if let range = string.range(of: #"\d+(\.\d*)?"#, options: .regularExpression) {
    let result = string[range] // or `String(string[range])` if you need `String`
}

Поведінка цих різних підходів дещо відрізняється, тому це залежить лише від того, що ви намагаєтесь зробити. Включіть або виключіть десяткову крапку, якщо ви хочете десяткові числа, або просто цілі числа. Існує безліч способів досягти цього.


Щодо старшого синтаксису Swift 2, див. Попередню редакцію цієї відповіді .


Чи можете ви пояснити, чому це потрібно робити invertedна наборі символів у прикладі Swift 3?
Andy Ibanez,

4
@AndyIbanez Це все одно, що сказати, якщо "ABC" - це персонажі, яких я хочу зберегти, тоді обріжте все, що не є "ABC".
Скотт Маккензі,

1
У Swift 4.2.1 let result = String(string.characters.filter { "01234567890.".characters.contains($0) })можна скоротити доlet result = string.filter("01234567890.".contains)
Лео Дабус

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

Стрімкий 3

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

Ви можете підтримати цю відповідь .


3
Дякую! після отримання всіх змін Swift3 у мене залишилось таке: myStr.replacingOccurrences (of: "[^ 0-9]", with: "", options: .regularExpression)
bobwki

29

Я віддаю перевагу цьому рішенню , тому що мені подобаються розширення, і воно мені здається трохи чистішим. Рішення відтворено тут:

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

здається позбавляє десяткових знаків
Markturnip

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

}

17

Ви можете відфільтрувати UnicodeScalarView рядка, використовуючи оператор збігу шаблонів для діапазонів, передати UnicodeScalar ClosedRange від 0 до 9 та ініціалізувати новий рядок з отриманим UnicodeScalarView:

extension String {
    private static var digits = UnicodeScalar("0")..."9"
    var digits: String {
        return String(unicodeScalars.filter(String.digits.contains))
    }
}

"abc12345".digits   // "12345"

редагувати / оновити:

Свіфт 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 ми можемо використовувати нову властивість Character під назвою 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! Як я можу залишити "." або "," у рядку, щоб мати можливість перетворити рядок у подвійний?
Ганс Бондока,

1
@HansBondoka Спасибі. Спробуйте так filter { $0.isNumber || $0 == "." }.
Лео Дабус

11

Рішення з використанням 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

Я не хочу жодних альфа-значень, хоча і не сподіваюся
Ден

Будь ласка, будьте більш конкретні. Заголовок говорить non-alphanumeric;-) Я відредагував відповідь для числових символів.
вадіан

8

Стрімкий 4

Але без розширень або componentsSeparatedByCharactersInSet, які також не читаються.

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

4

Свіфт 3, фільтрує всі крім цифр

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

3

Свіфт 4.2

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

2

Ви можете зробити щось подібне ...

let string = "[,myString1. \"" // string : [,myString1. " 
let characterSet = NSCharacterSet(charactersInString: "[,. \"")
let finalString = (string.componentsSeparatedByCharactersInSet(characterSet) as NSArray).componentsJoinedByString("") 
print(finalString)   
//finalString will be "myString1"

Я не маю повного уявлення про те, які символи можуть бути в рядку - і я хочу лише числові значення в кінці. Для цього мені потрібно було б перерахувати всіх персонажів, яких я не хочу. Є багато персонажів, які можуть бути
Ден

2

Проблема з першим рішенням Роба полягає stringByTrimmingCharactersInSetлише у фільтрації кінців рядка, а не у всьому, як зазначено в документації Apple:

Повертає новий рядок, зроблений шляхом видалення з обох кінців одержувачів символів, що містяться в заданому наборі символів.

Натомість використовуйте, componentsSeparatedByCharactersInSetщоб спочатку ізолювати всі випадки набору символів у масиви, а потім приєднати їх до порожнього роздільника рядків:

"$$1234%^56()78*9££".componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "0123456789").invertedSet)).joinWithSeparator("")

Яка повертається 123456789


Не потрібно використовувати NSCharacterSet. Але ваша відповідь найкраща. Ось загальна версія:extension String { func removingCharactersNot(in charSet: CharacterSet) -> String { return self.components(separatedBy: charSet.inverted).joined(separator: "") } }
xaphod

2

Стрімкий 3

extension String {
    var keepNumericsOnly: String {
        return self.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined(separator: "")
    }
}

2
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"

1

Версія Swift 4.0

extension String {
    var numbers: String {
        return String(describing: filter { String($0).rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789")) != nil })
    }
}

1

Стрімкий 4

String.swift

import Foundation

extension String {

    func removeCharacters(from forbiddenChars: CharacterSet) -> String {
        let passed = self.unicodeScalars.filter { !forbiddenChars.contains($0) }
        return String(String.UnicodeScalarView(passed))
    }

    func removeCharacters(from: String) -> String {
        return removeCharacters(from: CharacterSet(charactersIn: from))
    }
}

ViewController.swift

let character = "1Vi234s56a78l9"
        let alphaNumericSet = character.removeCharacters(from: CharacterSet.decimalDigits.inverted)
        print(alphaNumericSet) // will print: 123456789

        let alphaNumericCharacterSet = character.removeCharacters(from: "0123456789")
        print("no digits",alphaNumericCharacterSet) // will print: Vishal

Ініціалізація нового UnicodeScalarView безглузда. Результатом роботи фільтра є вже UnicodeScalarView. return String(passed)
Лео Дабус


0

Версія Swift 3

extension String
{
    func trimmingCharactersNot(in charSet: CharacterSet) -> String
    {
        var s:String = ""
        for unicodeScalar in self.unicodeScalars
        {
            if charSet.contains(unicodeScalar)
            {
                s.append(String(unicodeScalar))
            }
        }
        return s
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.