Rimuovi tutti i numeri tranne NSString


157

Ho un NSString (numero di telefono) con alcune parentesi e trattini poiché alcuni numeri di telefono sono formattati. Come rimuoverei tutti i caratteri tranne i numeri dalla stringa?

Risposte:


375

Vecchia domanda, ma che ne dici di:

  NSString *newString = [[origString componentsSeparatedByCharactersInSet:
                [[NSCharacterSet decimalDigitCharacterSet] invertedSet]] 
                componentsJoinedByString:@""];

Esplode la stringa di origine sull'insieme di non cifre, quindi le riassembla usando un separatore di stringhe vuoto. Non efficiente quanto la selezione dei caratteri, ma molto più compatto nel codice.


6
Grazie! Per altri principianti, puoi creare il tuo NSCharacterSet personalizzato facendoNSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]
guptron

1
Grazie mille! Solo per mia curiosità, hai idea del perché NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]non funziona?
Thomas Besnehard,

1
@Tommecpe stringByTrimmingCharactersInSet rimuove solo dall'inizio e dalla fine della stringa, quindi non ha effetto dopo il primo carattere non corrispondente o prima dell'ultimo carattere non corrispondente.
simonobo,

voglio mantenere solo numeri e alfabeto, come posso farlo?
Jacky,

1
@Jacky nell'esempio sopra lo sostituiresti [NSCharacterSet decimalDigitCharacterSet]con un altro che contiene solo numeri e lettere. È possibile costruire una per la creazione di un NSMutableCharaterSete passando una decimalDigitCharacterSet, uppercaseLetterCharacterSete lowercaseLetterCharacterSetal formUnionWithCharacterSet:. Si noti che letterCharacterSetinclude anche i segni, quindi l'uso di versioni minuscole e maiuscole.
kadam,

75

Non è necessario utilizzare una libreria di espressioni regolari come suggeriscono le altre risposte: viene chiamata la classe che stai cercando NSScanner. È usato come segue:

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"

EDIT: ho aggiornato il codice perché l'originale è stato cancellato dalla parte superiore della mia testa e ho pensato che sarebbe stato sufficiente per indirizzare le persone nella giusta direzione. Sembra che le persone siano alla ricerca di codice che possano semplicemente copiare e incollare direttamente nella loro applicazione.

Sono anche d'accordo sul fatto che la soluzione di Michael Pelz-Sherman sia più appropriata dell'uso NSScanner, quindi potresti voler dare un'occhiata a quello.


+1 Buona risposta che affronta direttamente la domanda. Ho modificato la mia risposta per sostenere questo approccio, ma lascio la seconda metà così com'è, poiché è ancora utile) che affronta il problema del rovescio della medaglia di formattare un numero di telefono per la visualizzazione. (Successivamente, puoi lasciare un commento costruttivo durante il downvoting, anche solo per i lettori successivi?)
Quinn Taylor,

4
Potrebbe essere utile sapere che esiste un metodo + decimalDigitCharacterSet di NSCharacterSet che ti fornirà tutte le cifre decimali. Questo è leggermente diverso dagli elenchi Nathan impostati, perché include tutti i simboli che rappresentano i numeri decimali, tra cui, ad esempio, le cifre arabo-indica (١٢٣٤٥ ecc.). A seconda della tua applicazione, questo potrebbe occasionalmente essere un problema, ma generalmente è buono o neutro e un po 'più breve da digitare.
Rob Napier,

Sono abbastanza sicuro che questa risposta in realtà non funziona e non è proprio l'approccio giusto al problema. Se provi effettivamente il codice come mostrato (aggiungendo prima un @ prima del primo parametro a NSLog, rendendolo una stringa objc), scoprirai che stampa <null> o si arresta in modo anomalo. Perché? Vedi la mia risposta qui sotto.
Jack Nutting,

Non c'è bisogno di un'altra risposta, ecco a cosa servono i commenti. Ho aggiornato la soluzione, incluso un riferimento alla soluzione di Michael Pelz-Sherman.
Nathan de Vries,

4
Questo è complicato.
Ryyst,

63

La risposta accettata è eccessiva per ciò che viene chiesto. Questo è molto più semplice:

NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];

2
La risposta (attualmente) accettata è più o meno identica a questa, ma è stata pubblicata 13 mesi prima.
Caleb,

Al momento in cui ho risposto, non aveva questa risposta. Anche se sembra che la risposta attuale sia già stata proposta e l'ho persa: web.archive.org/web/20101115214033/http://stackoverflow.com/…
Yacine Filali

30

Questo è fantastico, ma il codice non funziona per me sull'SDK di iPhone 3.0.

Se definisco strippedString come mostrato qui, ottengo un BAD ACCESS errorsegno quando provo a stamparlo dopo la scanCharactersFromSet:intoStringchiamata.

Se lo faccio così:

NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];

Finisco con una stringa vuota, ma il codice non va in crash.

Invece ho dovuto ricorrere alla buona vecchia C:

for (int i=0; i<[phoneNumber length]; i++) {
    if (isdigit([phoneNumber characterAtIndex:i])) {
        [strippedString appendFormat:@"%c",[phoneNumber characterAtIndex:i]];
    }
}

Sono in esecuzione 3.0 e questo funziona per me. La risposta più popolare di Vries non ha funzionato.
Neo42,

La risposta numero uno non funzionerà per me. Lo scanner si arresta quando raggiunge un () o - Questa risposta funziona alla grande !! Buon vecchio C !! Grazie
Jeff,

2
Nota solo che al carattere "+" dovrebbe essere consentito il numero di telefono.
Prcela,

27

Sebbene questa sia una vecchia domanda con risposte funzionanti, mi mancava il supporto per i formati internazionali . Basato sulla soluzione di simonobo, il set di caratteri modificato include un segno più "+". Anche i numeri di telefono internazionali sono supportati da questo emendamento.

NSString *condensedPhoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:
              [[NSCharacterSet characterSetWithCharactersInString:@"+0123456789"]
              invertedSet]] 
              componentsJoinedByString:@""];

Le espressioni Swift sono

var phoneNumber = " +1 (234) 567-1000 "
var allowedCharactersSet = NSMutableCharacterSet.decimalDigitCharacterSet()
allowedCharactersSet.addCharactersInString("+")
var condensedPhoneNumber = phoneNumber.componentsSeparatedByCharactersInSet(allowedCharactersSet.invertedSet).joinWithSeparator("")

Che produce +12345671000 come un formato di numero di telefono internazionale comune.


2
Questa è la migliore soluzione nell'elenco, soprattutto se è necessario il segno più per i numeri di telefono internazionali.
UXUiOS il

Per qualche ragione, usare un set di caratteri invertito mi spaventa per quanto riguarda le prestazioni. Qualcuno capisce se questa è una paura infondata?
devios1

Questo ha funzionato! Potresti spiegarne il funzionamento? @alex
Jayprakash Dubey,

11

Ecco la versione Swift di questo.

import UIKit
import Foundation
var phoneNumber = " 1 (888) 555-5551    "
var strippedPhoneNumber = "".join(phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))

Swift 2.0: phoneNumber.componentsSeparatedByCharactersInSet (NSCharacterSet.decimalDigitCharacterSet (). InvertedSet) .joinWithSeparator ("")
iluvatar_GR

11

Versione rapida della risposta più popolare:

var newString = join("", oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))

Modifica: sintassi per Swift 2

let newString = oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")

Modifica: sintassi per Swift 3

let newString = oldString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")

c'è un modo per mantenere il simbolo decimale separato? Il punto (o virgola) in funzione delle impostazioni predefinite del dispositivo? La tua soluzione elimina tutto tranne i numeri
Nicholas

5

Grazie per l'esempio Manca solo una cosa per l'incremento di scanLocation nel caso in cui uno dei caratteri in originalString non venga trovato all'interno dell'oggetto CharacterSet numeri. Ho aggiunto un'altra dichiarazione {} per risolvere questo problema.

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"

4

Accetta solo numero di cellulare

NSString * strippedNumber = [mobileNumber stringByReplacingOccurrencesOfString:@"[^0-9]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [mobileNumber length])];

3

Potrebbe valere la pena notare che l'accettato componentsSeparatedByCharactersInSet:ecomponentsJoinedByString: risposta basata non è una soluzione efficiente in termini di memoria. Alloca memoria per il set di caratteri, per un array e per una nuova stringa. Anche se si tratta solo di allocazioni temporanee, l'elaborazione di molte stringhe in questo modo può riempire rapidamente la memoria.

Un approccio più amichevole della memoria sarebbe quello di operare su una copia mutabile della stringa in atto. In una categoria su 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];
}

La profilazione dei due approcci lo ha dimostrato usando circa 2/3 di memoria in meno.


2

È possibile utilizzare l'espressione regolare su una stringa mutabile:

NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:
                                @"[^\\d]"
                                options:0
                                error:nil];

[regex replaceMatchesInString:str
                      options:0 
                        range:NSMakeRange(0, str.length) 
                 withTemplate:@""];

1

Creata la migliore soluzione come categoria per aiutare con problemi più ampi:

Interfaccia:

@interface NSString (easyReplace)
- (NSString *)stringByReplacingCharactersNotInSet:(NSCharacterSet *)set 
                                             with:(NSString *)string;
@end

Implemenation:

@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:@""];

1

Swift 3

let notNumberCharacters = NSCharacterSet.decimalDigits.inverted
let intString = yourString.trimmingCharacters(in: notNumberCharacters)

Questo eliminerà solo caratteri non numerici dall'inizio e dalla fine.
Shebuka,

1

rapido 4.1

var str = "75003 Paris, France"
var stringWithoutDigit = (str.components(separatedBy:CharacterSet.decimalDigits)).joined(separator: "")
print(stringWithoutDigit)

0

Um. La prima risposta mi sembra totalmente sbagliata. NSScanner è davvero pensato per l'analisi. A differenza di regex, ti fa analizzare la stringa un piccolo pezzo alla volta. Lo inizializzi con una stringa e mantiene un indice di quanto è lungo la stringa; Quell'indice è sempre il suo punto di riferimento e tutti i comandi che gli dai sono relativi a quel punto. Dici "ok, dammi il prossimo pezzo di caratteri in questo set" o "dammi il numero intero che trovi nella stringa", e quelli iniziano con l'indice corrente e vanno avanti finché non trovano qualcosa che non incontro. Se il primissimo carattere non corrisponde già, il metodo restituisce NO e l'indice non aumenta.

Il codice nel primo esempio è la scansione di "(123) 456-7890" alla ricerca di caratteri decimali, che non riescono già dal primo carattere, quindi la chiamata a scanCharactersFromSet: intoString: lascia da solo lo strippedString passato e restituisce NO; Il codice ignora totalmente il controllo del valore restituito, lasciando strippedString non assegnato. Anche se il primo carattere fosse una cifra, quel codice fallirebbe, poiché restituirebbe solo le cifre che trova fino al primo trattino o parentesi o altro.

Se davvero volessi usare NSScanner, potresti mettere qualcosa del genere in un ciclo e continuare a cercare un valore di ritorno NO, e se lo ottieni puoi incrementare scanLocation ed eseguire nuovamente la scansione; e devi anche controllare isAtEnd e yada yada yada. In breve, strumento sbagliato per il lavoro. La soluzione di Michael è migliore.


0

Per coloro che cercano l'estrazione del telefono, è possibile estrarre i numeri di telefono da un testo utilizzando NSDataDetector, ad esempio:

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]);
            }
        }
    }
}

`


0

Ho creato una categoria su NSString per semplificare questa operazione comune.

NSString + AllowCharactersInSet.h

@interface NSString (AllowCharactersInSet)

- (NSString *)stringByAllowingOnlyCharactersInSet:(NSCharacterSet *)characterSet;

@end

NSString + AllowCharactersInSet.m

@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

0

Penso che attualmente il modo migliore sia:

phoneNumber.replacingOccurrences(of: "\\D",
                               with: "",
                            options: String.CompareOptions.regularExpression)

0

Se stai solo cercando di afferrare i numeri dalla stringa, si potrebbe certamente utilizzare le espressioni regolari per analizzare fuori. Per eseguire regex in Objective-C, controlla RegexKit . Modifica: Come sottolinea @Nathan, l'uso di NSScanner è un modo molto più semplice per analizzare tutti i numeri da una stringa. Non ero assolutamente a conoscenza di quell'opzione, quindi sono propenso a lui per averlo suggerito. (Non mi piace nemmeno usare regex da solo, quindi preferisco approcci che non li richiedono.)

Se vuoi formattare i numeri di telefono per la visualizzazione, vale la pena dare un'occhiata a NSNumberFormatter . Ti suggerisco di leggere questa domanda SO relativa per suggerimenti su come farlo. Ricorda che i numeri di telefono sono formattati in modo diverso a seconda della posizione e / o delle impostazioni internazionali.


Oh, le ore dolorose che ho trascorso a sviluppare buoni formatter e parser di numeri di telefono. I thread collegati sono un buon inizio, ma il caso generale della formattazione dei numeri di telefono globali per la visualizzazione è una lunga strada e, come notato nei thread collegati, Apple non ti dà alcun accesso ai formattatori dei numeri di telefono della Rubrica e sono molto incoerente nel modo in cui i numeri di telefono sono presentati dall'API della Rubrica. L'unica cosa più difficile della formattazione di un numero di telefono per la visualizzazione è determinare se due numeri di telefono sono uguali. Almeno la domanda del PO è il più semplice dei problemi.
Rob Napier,

Credo che quei collegamenti alla formattazione dei numeri di telefono siano fuorvianti se non si è soddisfatti di un'implementazione primitiva, incentrata sugli Stati Uniti. Al posto di un adeguato formattatore di numeri di telefono localizzato da Apple, l'unico modo per farlo correttamente è copiare i modelli di formattazione dal dispositivo (UIPhoneFormats.plist in OS 2.x) e riprodurre i modelli da soli a seconda delle impostazioni locali degli utenti. Questo è un compito non banale.
Nathan de Vries,

Ecco perché ho citato la localizzazione dei formattatori di numeri. Non ho fatto finta di pubblicare qualsiasi forma di soluzione completa per questo - è una discussione molto più lunga e avrebbe più senso trasformarla in una domanda separata.
Quinn Taylor,

0

Swift 5

let newString = origString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")

-1

Basato sulla risposta di Jon Vogel qui è come estensione Swift String insieme ad alcuni test di base.

import Foundation
extension String {
    func stringByRemovingNonNumericCharacters() -> String {
        return self.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
    }
}

E alcuni test che dimostrano almeno la funzionalità di base:

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 == "")
    }
}

Questo risponde alla domanda del PO ma potrebbe essere facilmente modificato per lasciare nei caratteri relativi al numero di telefono come ",; * # +"


-4
NSString *originalPhoneNumber = @"(123) 123-456 abc";
NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
NSString *trimmedPhoneNumber = [originalPhoneNumber stringByTrimmingCharactersInSet:numbers];

];

Mantienilo semplice!


3
questo eliminerà solo quei personaggi dall'inizio e dalla fine.
raidfive,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.