Usuń wszystkie nieliczbowe znaki z ciągu w swift

84

Muszę przeanalizować nieznane dane, które powinny być tylko wartością liczbową, ale mogą zawierać spacje lub inne znaki niealfanumeryczne.

Czy jest na to nowy sposób w Swift? Wszystko, co mogę znaleźć w Internecie, to stary sposób robienia rzeczy w C.

Patrzę na stringByTrimmingCharactersInSet- ponieważ jestem pewien, że moje dane wejściowe będą miały tylko białe znaki / znaki specjalne na początku lub na końcu ciągu. Czy są jakieś wbudowane zestawy znaków, których mogę w tym celu użyć? Czy muszę stworzyć własne?

Miałem nadzieję, że będzie coś takiego, stringFromCharactersInSet()co pozwoli mi określić tylko prawidłowe znaki do zachowania

Dan
źródło

Odpowiedzi:

183

Miałem nadzieję, że będzie coś takiego jak stringFromCharactersInSet (), które pozwolą mi określić tylko prawidłowe znaki do zachowania.

Możesz użyć trimmingCharactersz invertedzestawem znaków, aby usunąć znaki z początku lub końca łańcucha. W wersji Swift 3 i nowszych:

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

Albo, jeśli chcesz usunąć nienumeryczny znaków w dowolnym miejscu w ciągu (nie tylko początek lub koniec), można , np Swift 4.2.1:filtercharacters

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

Lub, jeśli chcesz usunąć znaki z zestawu znaków z dowolnego miejsca w ciągu, użyj:

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

Lub, jeśli chcesz dopasować tylko prawidłowe ciągi znaków w określonym formacie (np. ####.##), Możesz użyć wyrażenia regularnego. Na przykład:

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

Zachowanie tych różnych podejść nieco się różni, więc zależy tylko od tego, co dokładnie próbujesz zrobić. Uwzględnij lub wyklucz przecinek dziesiętny, jeśli chcesz mieć liczby dziesiętne lub tylko liczby całkowite. Można to osiągnąć na wiele sposobów.


W przypadku starszej składni Swift 2 zobacz poprzednią wersję tej odpowiedzi .

Obrabować
źródło
Czy możesz wyjaśnić, dlaczego musisz zrobić invertedna zestawie znaków w przykładzie Swift 3?
Andy Ibanez
4
@AndyIbanez To tak, jakby powiedzieć, że jeśli „ABC” to znaki, które chcę zachować, przyciąć wszystko, co nie jest „ABC”.
Scott McKenzie,
1
W Swift 4.2.1 let result = String(string.characters.filter { "01234567890.".characters.contains($0) })można skrócić dolet result = string.filter("01234567890.".contains)
Leo Dabus
41
let result = string.stringByReplacingOccurrencesOfString("[^0-9]", withString: "", options: NSStringCompareOptions.RegularExpressionSearch, range:nil).stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())

Szybki 3

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

Możesz zagłosować za tą odpowiedzią .

Tom Howard
źródło
3
Dzięki! po uzyskaniu wszystkich zmian w Swift3 zostałem z tym: myStr.replacingOccurrences (of: "[^ 0-9]", with: "", options: .regularExpression)
bobwki
29

Wolę to rozwiązanie , ponieważ lubię rozszerzenia i wydaje mi się trochę czystsze. Rozwiązanie odtworzone tutaj:

extension String {
    var digits: String {
        return components(separatedBy: CharacterSet.decimalDigits.inverted)
            .joined()
    }
}
Jake Cronin
źródło
zdaje się
usuwać liczby
18

Swift 4

Znalazłem przyzwoity sposób, aby uzyskać tylko znaki alfanumeryczne ustawione z ciągu. Na przykład:-

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

}
Aashish
źródło
17

Możesz filtrować UnicodeScalarView ciągu za pomocą operatora dopasowania wzorca dla zakresów, przekazać UnicodeScalar ClosedRange od 0 do 9 i zainicjować nowy ciąg z wynikowym UnicodeScalarView:

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

"abc12345".digits   // "12345"

edytuj / aktualizuj:

Swift 4.2

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

lub jako metoda mutacji

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

Swift 5.2 • Xcode 11.4 lub nowszy

W Swift5 możemy użyć nowej właściwości Character o nazwie isWholeNumber:

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

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

Aby uwzględnić również okres, możemy przedłużyć Character i utworzyć obliczoną właściwość:

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

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

Testowanie placu zabaw:

"abc12345".digits   // "12345"

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

"Testing0123456789.".digitsAndPeriods // "0123456789."
Leo Dabus
źródło
Świetne rozwiązanie dla Swift 5! Jak mogę wyjść "." lub „,” w ciągu, aby móc przekonwertować ciąg na Double?
Hans Bondoka
1
@HansBondoka Thanks. Spróbuj w ten sposób filter { $0.isNumber || $0 == "." }.
Leo Dabus
11

Rozwiązanie wykorzystujące filterfunkcję irangeOfCharacterFromSet

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µ

Aby odfiltrować tylko znaki numeryczne, użyj

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

let numericSet = "0123456789"
let filteredCharacters = string.characters.filter {
  return numericSet.containsString(String($0))
}
let filteredString = String(filteredCharacters) // -> 347

lub

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
vadian
źródło
Nie chcę żadnych wartości alfa, chociaż nie spodziewam się żadnych
Dan,
Proszę być bardziej konkretnym. Nagłówek mówi non-alphanumeric;-) Zredagowałem odpowiedź dla znaków numerycznych.
vadian
8

Szybki 4

Ale bez rozszerzeń lub komponentówSeparatedByCharactersInSet, który nie czyta tak dobrze.

let allowedCharSet = NSCharacterSet.letters.union(.whitespaces)
let filteredText = String(sourceText.unicodeScalars.filter(allowedCharSet.contains))
Steve Moser
źródło
4

Swift 3, filtruje wszystkie oprócz liczb

let myString = "dasdf3453453fsdf23455sf.2234"
let result = String(myString.characters.filter { String($0).rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789")) != nil })
print(result)
CodeOverRide
źródło
3

Swift 4.2

let numericString = string.filter { (char) -> Bool in
    return char.isNumber
}
mef27
źródło
2

Możesz zrobić coś takiego ...

let string = "[,myString1. \"" // string : [,myString1. " 
let characterSet = NSCharacterSet(charactersInString: "[,. \"")
let finalString = (string.componentsSeparatedByCharactersInSet(characterSet) as NSArray).componentsJoinedByString("") 
print(finalString)   
//finalString will be "myString1"
vivek takrani
źródło
Nie mam pełnego pojęcia, jakie znaki mogą znajdować się w ciągu - i chcę tylko wartości liczbowe na końcu. To wymagałoby, bym wymienił wszystkie postacie, których nie chcę ... jest wiele znaków, które mogą być
Dan
2

Problem z pierwszym rozwiązaniem Roba polega na stringByTrimmingCharactersInSetfiltrowaniu tylko końców łańcucha, a nie w całości, jak stwierdzono w dokumentacji Apple:

Zwraca nowy ciąg utworzony przez usunięcie z obu końców znaków odbiorcy zawartych w danym zestawie znaków.

Zamiast tego użyj, componentsSeparatedByCharactersInSetaby najpierw wyodrębnić wszystkie niewystępujące się wystąpienia zestawu znaków w tablice, a następnie połączyć je za pomocą pustego separatora ciągu:

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

Który powraca 123456789

Ryan Brodie
źródło
Nie ma potrzeby używania NSCharacterSet. Ale twoja odpowiedź jest najlepsza. Oto ogólna wersja:extension String { func removingCharactersNot(in charSet: CharacterSet) -> String { return self.components(separatedBy: charSet.inverted).joined(separator: "") } }
xaphod
2

Szybki 3

extension String {
    var keepNumericsOnly: String {
        return self.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined(separator: "")
    }
}
Ehab Saifan
źródło
2
let string = "+1*(234) fds567@-8/90-"
let onlyNumbers = string.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()

print(onlyNumbers) // "1234567890"

lub

extension String {

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

let onlyNumbers = "+1*(234) fds567@-8/90-".removeNonNumeric() 
print(onlyNumbers)// "1234567890"
Vladimir Pchelyakov
źródło
1

Wersja Swift 4.0

extension String {
    var numbers: String {
        return String(describing: filter { String($0).rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789")) != nil })
    }
}
rockdaswift
źródło
1

Szybki 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
Vishal Vaghasiya
źródło
Inicjowanie nowego UnicodeScalarView nie ma sensu. Wynikiem filtru jest już UnicodeScalarView. return String(passed)
Leo Dabus
1

Swift 4.2

let digitChars  = yourString.components(separatedBy:
        CharacterSet.decimalDigits.inverted).joined(separator: "")
Debaprio B
źródło
0

Wersja 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
    }
}
hhamm
źródło