Tengo un NSString (número de teléfono) con algunos paréntesis y guiones, ya que algunos números de teléfono están formateados. ¿Cómo eliminaría todos los caracteres, excepto los números de la cadena?
Tengo un NSString (número de teléfono) con algunos paréntesis y guiones, ya que algunos números de teléfono están formateados. ¿Cómo eliminaría todos los caracteres, excepto los números de la cadena?
Respuestas:
Antigua pregunta, pero ¿qué tal:
NSString *newString = [[origString componentsSeparatedByCharactersInSet:
[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
componentsJoinedByString:@""];
Explota la cadena de origen en el conjunto de no dígitos, luego los vuelve a ensamblar usando un separador de cadena vacío. No es tan eficiente como seleccionar caracteres, pero es mucho más compacto en código.
NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]
no funciona?
[NSCharacterSet decimalDigitCharacterSet]
con otro que solo contenga números y letras. Puede construir uno creando a NSMutableCharaterSet
y pasando a decimalDigitCharacterSet
, uppercaseLetterCharacterSet
y lowercaseLetterCharacterSet
a formUnionWithCharacterSet:
. Tenga en cuenta que también letterCharacterSet
incluye marcas, de ahí el uso de versiones en mayúsculas y minúsculas.
No es necesario usar una biblioteca de expresiones regulares como sugieren las otras respuestas: se llama a la clase que busca NSScanner
. Se usa de la siguiente manera:
NSString *originalString = @"(123) 123123 abc";
NSMutableString *strippedString = [NSMutableString
stringWithCapacity:originalString.length];
NSScanner *scanner = [NSScanner scannerWithString:originalString];
NSCharacterSet *numbers = [NSCharacterSet
characterSetWithCharactersInString:@"0123456789"];
while ([scanner isAtEnd] == NO) {
NSString *buffer;
if ([scanner scanCharactersFromSet:numbers intoString:&buffer]) {
[strippedString appendString:buffer];
} else {
[scanner setScanLocation:([scanner scanLocation] + 1)];
}
}
NSLog(@"%@", strippedString); // "123123123"
EDITAR: He actualizado el código porque el original estaba escrito en la parte superior de mi cabeza y pensé que sería suficiente para señalar a las personas en la dirección correcta. Parece que las personas buscan el código, simplemente pueden copiar y pegar directamente en su aplicación.
También estoy de acuerdo en que la solución de Michael Pelz-Sherman es más apropiada que usarla NSScanner
, por lo que es posible que desee echarle un vistazo.
La respuesta aceptada es exagerada para lo que se pregunta. Esto es mucho más simple:
NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
Esto es genial, pero el código no funciona para mí en el iPhone 3.0 SDK.
Si defino strippedString como se muestra aquí, recibo un mensaje BAD ACCESS error
al intentar imprimirlo después de la scanCharactersFromSet:intoString
llamada.
Si lo hago así:
NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];
Termino con una cadena vacía, pero el código no se bloquea.
Tuve que recurrir a la buena C en su lugar:
for (int i=0; i<[phoneNumber length]; i++) {
if (isdigit([phoneNumber characterAtIndex:i])) {
[strippedString appendFormat:@"%c",[phoneNumber characterAtIndex:i]];
}
}
Aunque esta es una vieja pregunta con respuestas de trabajo, me perdí el soporte de formato internacional . Basado en la solución de simonobo, el conjunto de caracteres alterado incluye un signo más "+". Los números de teléfono internacionales también son compatibles con esta enmienda.
NSString *condensedPhoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:
[[NSCharacterSet characterSetWithCharactersInString:@"+0123456789"]
invertedSet]]
componentsJoinedByString:@""];
Las expresiones rápidas son
var phoneNumber = " +1 (234) 567-1000 "
var allowedCharactersSet = NSMutableCharacterSet.decimalDigitCharacterSet()
allowedCharactersSet.addCharactersInString("+")
var condensedPhoneNumber = phoneNumber.componentsSeparatedByCharactersInSet(allowedCharactersSet.invertedSet).joinWithSeparator("")
Lo que produce +12345671000 como un formato de número de teléfono internacional común.
Aquí está la versión rápida de esto.
import UIKit
import Foundation
var phoneNumber = " 1 (888) 555-5551 "
var strippedPhoneNumber = "".join(phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))
Versión rápida de la respuesta más popular:
var newString = join("", oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))
Editar: sintaxis para Swift 2
let newString = oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
Editar: sintaxis para Swift 3
let newString = oldString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
Gracias por el ejemplo Solo le falta una cosa al incremento de scanLocation en caso de que uno de los caracteres en originalString no se encuentre dentro del objeto NumberSet CharacterSet. He agregado una declaración else {} para arreglar esto.
NSString *originalString = @"(123) 123123 abc";
NSMutableString *strippedString = [NSMutableString
stringWithCapacity:originalString.length];
NSScanner *scanner = [NSScanner scannerWithString:originalString];
NSCharacterSet *numbers = [NSCharacterSet
characterSetWithCharactersInString:@"0123456789"];
while ([scanner isAtEnd] == NO) {
NSString *buffer;
if ([scanner scanCharactersFromSet:numbers intoString:&buffer]) {
[strippedString appendString:buffer];
}
// --------- Add the following to get out of endless loop
else {
[scanner setScanLocation:([scanner scanLocation] + 1)];
}
// --------- End of addition
}
NSLog(@"%@", strippedString); // "123123123"
Vale la pena señalar que la respuesta basada componentsSeparatedByCharactersInSet:
y aceptada componentsJoinedByString:
no es una solución eficiente en la memoria. Asigna memoria para el conjunto de caracteres, para una matriz y para una nueva cadena. Incluso si estas son solo asignaciones temporales, el procesamiento de muchas cadenas de esta manera puede llenar rápidamente la memoria.
Un enfoque más amigable con la memoria sería operar con una copia mutable de la cadena en su lugar. En una categoría sobre NSString:
-(NSString *)stringWithNonDigitsRemoved {
static NSCharacterSet *decimalDigits;
if (!decimalDigits) {
decimalDigits = [NSCharacterSet decimalDigitCharacterSet];
}
NSMutableString *stringWithNonDigitsRemoved = [self mutableCopy];
for (CFIndex index = 0; index < stringWithNonDigitsRemoved.length; ++index) {
unichar c = [stringWithNonDigitsRemoved characterAtIndex: index];
if (![decimalDigits characterIsMember: c]) {
[stringWithNonDigitsRemoved deleteCharactersInRange: NSMakeRange(index, 1)];
index -= 1;
}
}
return [stringWithNonDigitsRemoved copy];
}
El perfil de los dos enfoques ha demostrado esto usando aproximadamente 2/3 menos memoria.
Desarrollé la mejor solución como categoría para ayudar con problemas más amplios:
Interfaz:
@interface NSString (easyReplace)
- (NSString *)stringByReplacingCharactersNotInSet:(NSCharacterSet *)set
with:(NSString *)string;
@end
Implementación
@implementation NSString (easyReplace)
- (NSString *)stringByReplacingCharactersNotInSet:(NSCharacterSet *)set
with:(NSString *)string
{
NSMutableString *strippedString = [NSMutableString
stringWithCapacity:self.length];
NSScanner *scanner = [NSScanner scannerWithString:self];
while ([scanner isAtEnd] == NO) {
NSString *buffer;
if ([scanner scanCharactersFromSet:set intoString:&buffer]) {
[strippedString appendString:buffer];
} else {
[scanner setScanLocation:([scanner scanLocation] + 1)];
[strippedString appendString:string];
}
}
return [NSString stringWithString:strippedString];
}
@end
Uso:
NSString *strippedString =
[originalString stringByReplacingCharactersNotInSet:
[NSCharacterSet setWithCharactersInString:@"01234567890"
with:@""];
rápido 4.1
var str = "75003 Paris, France"
var stringWithoutDigit = (str.components(separatedBy:CharacterSet.decimalDigits)).joined(separator: "")
print(stringWithoutDigit)
Um. La primera respuesta me parece totalmente incorrecta. NSScanner es realmente para analizar. A diferencia de la expresión regular, tiene que analizar la cadena un pequeño fragmento a la vez. Lo inicializa con una cadena, y mantiene un índice de cuánto avanza la cadena; Ese índice es siempre su punto de referencia, y cualquier comando que le des es relativo a ese punto. Usted le dice: "ok, deme la siguiente porción de caracteres en este conjunto" o "deme el número entero que encuentre en la cadena", y esos comienzan en el índice actual, y avanzan hasta que encuentran algo que no partido. Si el primer carácter ya no coincide, entonces el método devuelve NO y el índice no se incrementa.
El código en el primer ejemplo está escaneando "(123) 456-7890" en busca de caracteres decimales, que ya fallan desde el primer carácter, por lo que la llamada a scanCharactersFromSet: intoString: deja solo la strippedString pasada y devuelve NO; El código ignora totalmente la comprobación del valor de retorno, dejando stripsString sin asignar. Incluso si el primer carácter fuera un dígito, ese código fallaría, ya que solo devolvería los dígitos que encuentre hasta el primer guión o par o lo que sea.
Si realmente quisiera usar NSScanner, podría poner algo así en un bucle y seguir buscando un valor de retorno NO, y si lo consigue, puede incrementar el scanLocation y escanear nuevamente; y también tienes que verificar isAtEnd y yada yada yada. En resumen, herramienta incorrecta para el trabajo. La solución de Michael es mejor.
Para aquellos que buscan extracción de teléfono, puede extraer los números de teléfono de un texto usando NSDataDetector, por ejemplo:
NSString *userBody = @"This is a text with 30612312232 my phone";
if (userBody != nil) {
NSError *error = NULL;
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypePhoneNumber error:&error];
NSArray *matches = [detector matchesInString:userBody options:0 range:NSMakeRange(0, [userBody length])];
if (matches != nil) {
for (NSTextCheckingResult *match in matches) {
if ([match resultType] == NSTextCheckingTypePhoneNumber) {
DbgLog(@"Found phone number %@", [match phoneNumber]);
}
}
}
}
``
Creé una categoría en NSString para simplificar esta operación común.
@interface NSString (AllowCharactersInSet)
- (NSString *)stringByAllowingOnlyCharactersInSet:(NSCharacterSet *)characterSet;
@end
@implementation NSString (AllowCharactersInSet)
- (NSString *)stringByAllowingOnlyCharactersInSet:(NSCharacterSet *)characterSet {
NSMutableString *strippedString = [NSMutableString
stringWithCapacity:self.length];
NSScanner *scanner = [NSScanner scannerWithString:self];
while (!scanner.isAtEnd) {
NSString *buffer = nil;
if ([scanner scanCharactersFromSet:characterSet intoString:&buffer]) {
[strippedString appendString:buffer];
} else {
scanner.scanLocation = scanner.scanLocation + 1;
}
}
return strippedString;
}
@end
Si solo está buscando obtener los números de la cadena, ciertamente podría usar expresiones regulares para analizarlos. Para hacer expresiones regulares en Objective-C, echa un vistazo a RegexKit . Editar: como señala @Nathan, usar NSScanner es una forma mucho más simple de analizar todos los números de una cadena. No estaba al tanto de esa opción, así que le apoyo por sugerirla. (Ni siquiera me gusta usar regex, así que prefiero enfoques que no los requieran).
Si desea formatear números de teléfono para mostrar, vale la pena echarle un vistazo a NSNumberFormatter . Le sugiero que lea esta pregunta SO relacionada para obtener consejos sobre cómo hacerlo. Recuerde que los números de teléfono tienen un formato diferente según la ubicación y / o la ubicación.
Swift 5
let newString = origString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
Basado en la respuesta de Jon Vogel aquí, es como una extensión de Swift String junto con algunas pruebas básicas.
import Foundation
extension String {
func stringByRemovingNonNumericCharacters() -> String {
return self.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
}
}
Y algunas pruebas que prueban al menos la funcionalidad básica:
import XCTest
class StringExtensionTests: XCTestCase {
func testStringByRemovingNonNumericCharacters() {
let baseString = "123"
var testString = baseString
var newString = testString.stringByRemovingNonNumericCharacters()
XCTAssertTrue(newString == testString)
testString = "a123b"
newString = testString.stringByRemovingNonNumericCharacters()
XCTAssertTrue(newString == baseString)
testString = "a=1-2_3@b"
newString = testString.stringByRemovingNonNumericCharacters()
XCTAssertTrue(newString == baseString)
testString = "(999) 999-9999"
newString = testString.stringByRemovingNonNumericCharacters()
XCTAssertTrue(newString.characters.count == 10)
XCTAssertTrue(newString == "9999999999")
testString = "abc"
newString = testString.stringByRemovingNonNumericCharacters()
XCTAssertTrue(newString == "")
}
}
Esto responde a la pregunta del OP, pero podría modificarse fácilmente para dejar en el número de teléfono caracteres relacionados como ",; * # +"
NSString *originalPhoneNumber = @"(123) 123-456 abc";
NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
NSString *trimmedPhoneNumber = [originalPhoneNumber stringByTrimmingCharactersInSet:numbers];
];
¡Mantenlo simple!
NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]