J'ai une NSString (numéro de téléphone) avec des parenthèses et des tirets car certains numéros de téléphone sont formatés. Comment supprimer tous les caractères sauf les nombres de la chaîne?
J'ai une NSString (numéro de téléphone) avec des parenthèses et des tirets car certains numéros de téléphone sont formatés. Comment supprimer tous les caractères sauf les nombres de la chaîne?
Réponses:
Ancienne question, mais que diriez-vous:
NSString *newString = [[origString componentsSeparatedByCharactersInSet:
[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
componentsJoinedByString:@""];
Il explose la chaîne source sur l'ensemble des non-chiffres, puis les réassemble à l'aide d'un séparateur de chaîne vide. Pas aussi efficace que la sélection de caractères, mais beaucoup plus compact dans le code.
NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]
ne fonctionne pas?
[NSCharacterSet decimalDigitCharacterSet]
par un autre contenant uniquement des chiffres et des lettres. Vous pouvez en créer un en créant un NSMutableCharaterSet
et en passant un decimalDigitCharacterSet
, uppercaseLetterCharacterSet
et lowercaseLetterCharacterSet
à formUnionWithCharacterSet:
. Notez que cela letterCharacterSet
inclut également des marques, d'où l'utilisation de versions minuscules et majuscules.
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 recherchez 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"
ÉDITER: J'ai mis à jour le code parce que l'original a été écrit sur le dessus de ma tête et j'ai pensé que ce serait suffisant pour orienter les gens dans la bonne direction. Il semble que les gens recherchent du code, ils peuvent simplement copier-coller directement dans leur application.
Je conviens également que la solution de Michael Pelz-Sherman est plus appropriée que l'utilisation NSScanner
, alors vous voudrez peut-être y jeter un coup d'œil.
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:@""];
C'est génial, mais le code ne fonctionne pas pour moi sur le SDK iPhone 3.0.
Si je définis strippedString comme vous le montrez ici, j'obtiens un BAD ACCESS error
en essayant de l'imprimer après l' 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]];
}
}
Bien que ce soit une vieille question avec des réponses fonctionnelles, j'ai manqué le support du 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 courant.
Voici la version Swift de ceci.
import UIKit
import Foundation
var phoneNumber = " 1 (888) 555-5551 "
var strippedPhoneNumber = "".join(phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))
Version Swift de la réponse la plus populaire:
var newString = join("", oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))
Edit: Syntaxe pour Swift 2
let newString = oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
Edit: Syntaxe pour Swift 3
let newString = oldString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
Merci pour l'exemple. Il ne manque qu'une seule chose, l'incrément de scanLocation au cas où l'un des caractères de originalString ne serait pas trouvé à l'intérieur de l'objet CharacterSet numbers. 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"
Il peut être intéressant de noter que la réponse acceptée componentsSeparatedByCharactersInSet:
et componentsJoinedByString:
basée n'est pas une solution efficace 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 façon peut rapidement remplir la mémoire.
Une approche plus conviviale pour la mémoire serait d'opérer sur une copie mutable 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é cela en utilisant environ 2/3 de mémoire en moins.
Création de la meilleure solution en tant que catégorie pour résoudre des problèmes plus larges:
Interface:
@interface NSString (easyReplace)
- (NSString *)stringByReplacingCharactersNotInSet:(NSCharacterSet *)set
with:(NSString *)string;
@end
Mise en œuvre:
@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:@""];
rapide 4.1
var str = "75003 Paris, France"
var stringWithoutDigit = (str.components(separatedBy:CharacterSet.decimalDigits)).joined(separator: "")
print(stringWithoutDigit)
Hum. La première réponse me semble totalement erronée. NSScanner est vraiment destiné à l'analyse. Contrairement à regex, il vous permet d'analyser la chaîne un tout petit morceau à la fois. Vous l'initialisez avec une chaîne et il conserve un index de la distance parcourue le long 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 lui dites, "ok, donnez-moi le prochain morceau de caractères dans cet ensemble" ou "donnez-moi l'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 renvoie NON et l'index ne s'incrémente pas.
Le code dans le premier exemple scanne "(123) 456-7890" pour les caractères décimaux, ce qui échoue déjà à partir du tout premier caractère, donc l'appel à scanCharactersFromSet: intoString: laisse le strippedString passé seul et retourne NO; Le code ignore totalement la vérification de la valeur de retour, laissant la chaîne strippedString non attribuée. Même si le premier caractère était un chiffre, ce code échouerait, car il ne renverrait que les chiffres qu'il trouve jusqu'au premier tiret ou paren ou autre.
Si vous voulez vraiment utiliser NSScanner, vous pouvez mettre quelque chose comme ça dans une boucle, et continuer à vérifier une valeur de retour NO, et si vous obtenez cela, vous pouvez incrémenter le scanLocation et scanner à 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.
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]);
}
}
}
}
»
J'ai créé une catégorie sur NSString pour simplifier cette opération courante.
@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 vous cherchez simplement à récupérer les nombres de la chaîne, vous pouvez certainement utiliser des expressions régulières pour les analyser. Pour faire des regex en Objective-C, consultez RegexKit . Éditer: Comme le souligne @Nathan, l'utilisation de 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, alors des accessoires à lui pour l'avoir suggérée. (Je n'aime même pas utiliser les regex moi-même, donc je préfère les approches qui n'en nécessitent pas.)
Si vous souhaitez formater les numéros de téléphone pour l'affichage, il vaut la peine de jeter un œil à NSNumberFormatter . Je vous suggère de lire cette question SO connexe pour obtenir des conseils à ce sujet. 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.
Swift 5
let newString = origString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
Basé sur la réponse de Jon Vogel ici, il s'agit d'une extension Swift String avec quelques tests de base.
import Foundation
extension String {
func stringByRemovingNonNumericCharacters() -> String {
return self.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
}
}
Et quelques tests prouvant au moins les fonctionnalités de 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 == "")
}
}
Cela répond à la question du PO, mais il peut être facilement modifié pour laisser des caractères liés au numéro de téléphone comme ",; * # +"
NSString *originalPhoneNumber = @"(123) 123-456 abc";
NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
NSString *trimmedPhoneNumber = [originalPhoneNumber stringByTrimmingCharactersInSet:numbers];
];
Rester simple!
NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]