Elimina todos los caracteres no numéricos de una cadena de forma rápida

84

Tengo la necesidad de analizar algunos datos desconocidos que deberían ser solo un valor numérico, pero pueden contener espacios en blanco u otros caracteres no alfanuméricos.

¿Existe una nueva forma de hacer esto en Swift? Todo lo que puedo encontrar en línea parece ser la antigua forma de C de hacer las cosas.

Estoy mirando stringByTrimmingCharactersInSet, ya que estoy seguro de que mis entradas solo tendrán espacios en blanco / caracteres especiales al principio o al final de la cadena. ¿Hay algún conjunto de caracteres incorporado que pueda usar para esto? ¿O necesito crear el mío propio?

Esperaba que hubiera algo así como stringFromCharactersInSet()que me permitiera especificar solo caracteres válidos para mantener

Dan
fuente

Respuestas:

183

Esperaba que hubiera algo como stringFromCharactersInSet () que me permitiera especificar solo caracteres válidos para mantener.

Puede utilizar trimmingCharacterscon el invertedjuego de caracteres para eliminar caracteres del principio o del final de la cadena. En Swift 3 y posteriores:

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

O bien, si desea eliminar caracteres no numéricos en cualquier lugar de la cadena (no sólo al inicio o final), puede filterel characters, por ejemplo, en Swift 4.2.1:

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

O, si desea eliminar caracteres de un CharacterSet desde cualquier lugar de la cadena, use:

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

O, si solo desea hacer coincidir cadenas válidas de un determinado formato (por ejemplo ####.##), puede usar una expresión regular. Por ejemplo:

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

El comportamiento de estos diferentes enfoques difiere ligeramente, por lo que depende precisamente de lo que intente hacer. Incluya o excluya el punto decimal si desea números decimales o solo enteros. Hay muchas formas de lograrlo.


Para la sintaxis anterior de Swift 2, consulte la revisión anterior de esta respuesta .

Robar
fuente
¿Puede explicar por qué tiene que hacerlo inverteden el juego de caracteres en el ejemplo de Swift 3?
Andy Ibanez
4
@AndyIbanez Es como decir, si "ABC" son los personajes que quiero conservar entonces recorta todo lo que no sea "ABC".
Scott McKenzie
1
En Swift 4.2.1 let result = String(string.characters.filter { "01234567890.".characters.contains($0) })se puede acortar alet result = string.filter("01234567890.".contains)
Leo Dabus
41
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)

Puedes votar esta respuesta .

Tom Howard
fuente
3
¡Gracias! después de obtener todos los cambios de Swift3, me quedé con esto: myStr.replacingOccurrences (de: "[^ 0-9]", con: "", opciones: .regularExpression)
bobwki
29

Prefiero esta solución , porque me gustan las extensiones, y me parece un poco más limpia. Solución reproducida aquí:

extension String {
    var digits: String {
        return components(separatedBy: CharacterSet.decimalDigits.inverted)
            .joined()
    }
}
Jake Cronin
fuente
parece quitar decimales
markturnip
18

Swift 4

Encontré una forma decente de obtener solo caracteres alfanuméricos configurados a partir de una cadena. Por ejemplo:-

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
fuente
17

Puede filtrar el UnicodeScalarView de la cadena utilizando el operador de coincidencia de patrones para rangos, pasar un UnicodeScalar ClosedRange de 0 a 9 e inicializar una nueva Cadena con el UnicodeScalarView resultante:

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

"abc12345".digits   // "12345"

editar / actualizar:

Rápido 4.2

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

o como método mutante

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

Swift 5.2 • Xcode 11.4 o posterior

En Swift5 podemos usar una nueva propiedad de carácter llamada isWholeNumber:

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

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

Para permitir un período también, podemos extender Character y crear una propiedad calculada:

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

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

Prueba de patio de recreo:

"abc12345".digits   // "12345"

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

"Testing0123456789.".digitsAndPeriods // "0123456789."
Leo Dabus
fuente
¡Gran solución para Swift 5! ¿Cómo puedo dejar "." o "," dentro del String, para poder convertir un String en un Double?
Hans Bondoka
1
@HansBondoka Gracias filter { $0.isNumber || $0 == "." }. Prueba así .
Leo Dabus
11

Una solución que usa la filterfunción yrangeOfCharacterFromSet

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µ

Para filtrar solo por caracteres numéricos, use

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

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

o

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
fuente
No quiero ningún valor alfa, aunque no espero ninguno
Dan
Por favor sé más específico. El titular dice non-alphanumeric;-) Edité la respuesta para caracteres numéricos.
vadian
8

Rápido 4

Pero sin extensiones o componentesSeparatedByCharactersInSet que no lee tan bien.

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

Swift 3, filtra todos excepto los números

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

Rápido 4.2

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

Puedes hacer algo como esto ...

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
fuente
No tengo una idea completa de qué caracteres pueden estar en la cadena, y solo quiero valores numéricos al final. Esto me necesitaría a la lista de todos los personajes que no quiero .. hay una gran cantidad de caracteres que podría ser
Dan
2

El problema con la primera solución de Rob es que stringByTrimmingCharactersInSetsolo filtra los extremos de la cadena en lugar de todo, como se indica en la documentación de Apple:

Devuelve una nueva cadena creada al eliminar de ambos extremos los caracteres del receptor contenidos en un conjunto de caracteres dado.

En su lugar, utilice componentsSeparatedByCharactersInSetpara aislar primero todas las no ocurrencias del juego de caracteres en matrices y luego únalas con un separador de cadena vacío:

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

Que vuelve 123456789

Ryan Brodie
fuente
No es necesario usarlo NSCharacterSet. Pero tu respuesta es la mejor. Aquí hay una versión en general:extension String { func removingCharactersNot(in charSet: CharacterSet) -> String { return self.components(separatedBy: charSet.inverted).joined(separator: "") } }
xaphod
2

Swift 3

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

print(onlyNumbers) // "1234567890"

o

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
fuente
1

Versión Swift 4.0

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

Rápido 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
fuente
Inicializar un nuevo UnicodeScalarView no tiene sentido. El resultado del filtro ya es UnicodeScalarView. return String(passed)
Leo Dabus
1

Rápido 4.2

let digitChars  = yourString.components(separatedBy:
        CharacterSet.decimalDigits.inverted).joined(separator: "")
Debaprio B
fuente
0

Versión 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
fuente