web-dev-qa-db-fra.com

Supprimer tout sauf les chiffres de NSString

J'ai un NSString (numéro de téléphone) avec des parenthèses et des traits d'union car certains numéros de téléphone sont formatés. Comment pourrais-je supprimer tous les caractères sauf les chiffres de la chaîne?

157
Ben Harris

Vieille question, mais qu'en est-il:

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

Il éclate la chaîne source sur l'ensemble des non-chiffres, puis les rassemble à l'aide d'un séparateur de chaîne vide. Pas aussi efficace que de cueillir des caractères, mais beaucoup plus compact dans le code.

374
simonobo

Il n'est pas nécessaire d'utiliser une bibliothèque d'expressions régulières comme le suggèrent les autres réponses - la classe que vous suivez est appelée NSScanner. Il est utilisé comme suit:

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: J'ai mis à jour le code parce que l'original a été écrit de mémoire et je me suis dit qu'il suffirait d'indiquer aux gens la bonne direction. Il semble que les gens recherchent du code qu'ils peuvent simplement copier-coller directement dans leur application.

Je conviens également que la solution de Michael Pelz-Sherman est plus appropriée que d'utiliser NSScanner. Vous voudrez peut-être jeter un coup d'œil à cela.

75
Nathan de Vries

La réponse acceptée est exagérée pour ce qui est demandé. C'est beaucoup plus simple:

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

C’est génial, mais le code ne fonctionne pas pour moi sur le SDK de l’iPhone 3.0.

Si je définis strippedString comme vous le montrez ici, je reçois un BAD ACCESS error en essayant de l’imprimer après le scanCharactersFromSet:intoString appel.

Si je le fais comme ça:

NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];

Je me retrouve avec une chaîne vide, mais le code ne plante pas.

J'ai dû recourir au bon vieux C à la place:

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

Bien que ce soit une vieille question avec des réponses de travail, j'ai manqué support de format international. Basé sur la solution de simonobo, le jeu de caractères modifié comprend un signe plus "+". Les numéros de téléphone internationaux sont également pris en charge par cet amendement.

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

Les expressions Swift sont

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

Ce qui donne +12345671000 comme format de numéro de téléphone international commun.

27
alex

Voici la version de Swift).

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

Version rapide de la réponse la plus populaire:

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

Éditer: Syntaxe pour Swift 2

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

Edit: Syntaxe for Swift 3

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

Merci pour l'exemple. Il ne manque qu’une chose qui manque l’incrément de scanLocation au cas où l’un des caractères de originalString ne se trouve pas dans l’objet numbers CharacterSet. J'ai ajouté une instruction else {} pour résoudre ce problème.

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"
5
GregoryN

Il accepte uniquement le numéro de mobile

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

Il convient de noter que la réponse acceptée basée sur componentsSeparatedByCharactersInSet: Et componentsJoinedByString: N'est pas une solution économe en mémoire. Il alloue de la mémoire pour le jeu de caractères, pour un tableau et pour une nouvelle chaîne. Même s'il ne s'agit que d'allocations temporaires, le traitement de nombreuses chaînes de cette manière peut rapidement remplir la mémoire.

Une approche plus conviviale en mémoire consisterait à utiliser une copie modifiable de la chaîne en place. Dans une catégorie sur 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];
}

Le profilage des deux approches a montré que cela nécessitait environ 2/3 de mémoire en moins.

3
kadam

Vous pouvez utiliser une expression régulière sur une chaîne mutable:

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

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

Construit la meilleure solution en tant que catégorie pour aider à résoudre des problèmes plus vastes:

Interface:

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

Mise en oeuvre:

@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

Usage:

NSString *strippedString = 
 [originalString stringByReplacingCharactersNotInSet:
   [NSCharacterSet setWithCharactersInString:@"01234567890" 
                                        with:@""];
1
BadPirate

Swift 3

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

Swift 4.1

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

Si vous cherchez seulement à saisir les nombres de la chaîne, vous pouvez utilisez certainement des expressions régulières pour les analyser. Pour faire des regex dans Objective-C, consultez RegexKit .  Éditez: Comme le fait remarquer @Nathan, utiliser NSScanner est un moyen beaucoup plus simple d'analyser tous les nombres d'une chaîne. Je n’étais absolument pas au courant de cette option, aussi, aidez-le à le suggérer. (Je n'aime même pas utiliser regex moi-même, je préfère donc les approches qui n'en ont pas besoin.)

Si vous souhaitez formater les numéros de téléphone pour l'affichage, il convient de jeter un œil à NSNumberFormatter . Je vous suggère de lire ceci =SO question) pour obtenir des conseils à cet égard. N'oubliez pas que les numéros de téléphone sont formatés différemment selon l'emplacement et/ou les paramètres régionaux.

0
Quinn Taylor

Pour ceux qui recherchent une extraction de téléphone, vous pouvez extraire les numéros de téléphone d'un texte à l'aide de NSDataDetector, par exemple:

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
Rafael Sanches

Hum La première réponse me semble totalement fausse. NSScanner est vraiment destiné à l'analyse. Contrairement à regex, il vous permet d'analyser la chaîne un petit morceau à la fois. Vous l'initialisez avec une chaîne et il conserve un index indiquant le degré d'avancement de la chaîne; Cet index est toujours son point de référence et toutes les commandes que vous lui donnez sont relatives à ce point. Vous dites: "ok, donnez-moi le prochain bloc de caractères de cet ensemble" ou "donnez-moi le nombre entier que vous trouvez dans la chaîne", et ceux-ci commencent à l'index actuel et avancent jusqu'à ce qu'ils trouvent quelque chose qui ne fonctionne pas rencontre. Si le tout premier caractère ne correspond pas déjà, la méthode retourne NO et l'index ne s'incrémente pas.

Dans le premier exemple, le code recherche "(123) 456-7890" des caractères décimaux, qui échouent déjà à partir du tout premier caractère. L'appel de scanCharactersFromSet: intoString: laisse alors le strippedString transmis et renvoie NO; Le code ignore totalement la vérification de la valeur de retour, laissant le strippedString non affecté. Même si le premier caractère était un chiffre, ce code échouerait, car il ne renverrait que les chiffres trouvés jusqu'au premier tiret, paren ou autre.

Si vous voulez vraiment utiliser NSScanner, vous pouvez mettre quelque chose comme ça dans une boucle et continuer à rechercher une valeur de retour NO. Si vous obtenez cela, vous pouvez incrémenter scanLocation et numériser à nouveau. et vous devez également vérifier isAtEnd, et yada yada yada. En bref, mauvais outil pour le travail. La solution de Michael est meilleure.

0
Jack Nutting

J'ai créé une catégorie sur NSString pour simplifier cette opération courante.

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
Ryan

Swift 5

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

Je pense qu'actuellement, le meilleur moyen est:

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