Entfernen Sie alle außer Zahlen aus NSString

157

Ich habe einen NSString (Telefonnummer) mit einigen Klammern und Bindestrichen, da einige Telefonnummern formatiert sind. Wie würde ich alle Zeichen außer Zahlen aus der Zeichenfolge entfernen?

Ben Harris
quelle

Antworten:

375

Alte Frage, aber wie wäre es mit:

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

Es explodiert die Quellzeichenfolge in der Gruppe der nicht-stelligen Zeichen und setzt sie dann mithilfe eines leeren Zeichenfolgentrennzeichens wieder zusammen. Nicht so effizient wie das Durchsuchen von Zeichen, aber viel kompakter im Code.

Simonobo
quelle
6
Vielen Dank! Für andere Anfänger können Sie Ihr eigenes NSCharacterSet erstellen, indem SieNSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]
guptron
1
Vielen Dank ! Haben Sie nur aus Neugier eine Idee, warum es NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]nicht funktioniert?
Thomas Besnehard
1
@Tommecpe stringByTrimmingCharactersInSet wird nur vom Anfang und Ende der Zeichenfolge entfernt, sodass dies nicht nach dem ersten nicht übereinstimmenden Zeichen oder vor dem letzten nicht übereinstimmenden Zeichen erfolgt.
Simonobo
Ich möchte nur Zahlen und Alphabet behalten. Wie kann ich das tun?
Jacky
1
@Jacky im obigen Beispiel würden Sie durch ein [NSCharacterSet decimalDigitCharacterSet]anderes ersetzen , das nur Zahlen und Buchstaben enthält. Sie können ein Konstrukt durch eine Erstellung NSMutableCharaterSetund Weitergabe ein decimalDigitCharacterSet, uppercaseLetterCharacterSetund lowercaseLetterCharacterSetzu formUnionWithCharacterSet:. Beachten Sie, dass dies letterCharacterSetauch Markierungen enthält, daher die Verwendung von Versionen in Klein- und Großbuchstaben.
Kadam
75

Es ist nicht erforderlich, eine Bibliothek mit regulären Ausdrücken zu verwenden, wie aus den anderen Antworten hervorgeht - die Klasse, nach der Sie suchen, wird aufgerufen NSScanner. Es wird wie folgt verwendet:

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"

BEARBEITEN: Ich habe den Code aktualisiert, weil das Original von oben auf meinen Kopf geschrieben wurde und ich dachte, es würde ausreichen, um die Leute in die richtige Richtung zu weisen. Es scheint, dass die Leute nach Code suchen, den sie einfach kopieren und direkt in ihre Anwendung einfügen können.

Ich stimme auch zu, dass die Lösung von Michael Pelz-Sherman besser geeignet ist als die Verwendung NSScanner. Vielleicht möchten Sie sich das ansehen.

Nathan de Vries
quelle
+1 Gute Antwort, die die Frage direkt anspricht. Ich habe meine Antwort bearbeitet, um diesen Ansatz zu befürworten, aber ich lasse die zweite Hälfte unverändert, da sie immer noch hilfreich ist, um das Problem der Formatierung einer Telefonnummer für die Anzeige zu lösen. (Als nächstes können Sie einen konstruktiven Kommentar hinterlassen, wenn Sie nur für spätere Leser abstimmen?)
Quinn Taylor
4
Es kann nützlich sein zu wissen, dass es eine + decimalDigitCharacterSet-Methode von NSCharacterSet gibt, die Ihnen alle Dezimalstellen gibt. Dies unterscheidet sich geringfügig von den festgelegten Nathan-Listen, da es alle Symbole enthält, die Dezimalzahlen darstellen, einschließlich beispielsweise arabisch-indischer Ziffern (١٢٣٤٥ usw.). Abhängig von Ihrer Anwendung kann dies gelegentlich ein Problem sein, aber im Allgemeinen ist es entweder gut oder neutral und etwas kürzer zu tippen.
Rob Napier
Ich bin mir ziemlich sicher, dass diese Antwort nicht wirklich funktioniert und nicht wirklich die richtige Herangehensweise an das Problem ist. Wenn Sie den Code tatsächlich wie gezeigt ausprobieren (zuerst ein @ vor dem ersten Parameter zu NSLog hinzufügen, wodurch er zu einer Objektzeichenfolge wird), werden Sie feststellen, dass er entweder <null> druckt oder abstürzt. Warum? Siehe meine Antwort unten.
Jack Nutting
Keine Notwendigkeit für eine weitere Antwort - dafür sind Kommentare gedacht. Ich habe die Lösung aktualisiert, einschließlich eines Verweises auf die Lösung von Michael Pelz-Sherman.
Nathan de Vries
4
Das ist viel zu kompliziert.
Ryyst
63

Die akzeptierte Antwort ist übertrieben für das, was gefragt wird. Das ist viel einfacher:

NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
Yacine Filali
quelle
2
Die (derzeit) akzeptierte Antwort ist mehr oder weniger identisch mit dieser, wurde jedoch 13 Monate zuvor veröffentlicht.
Caleb
Zu dem Zeitpunkt, als ich darauf antwortete, hatte es diese Antwort nicht. Obwohl es den Anschein hat, dass die aktuelle Antwort bereits vorgeschlagen wurde und ich sie verpasst habe: web.archive.org/web/20101115214033/http://stackoverflow.com/…
Yacine Filali
30

Das ist großartig, aber der Code funktioniert bei mir auf dem iPhone 3.0 SDK nicht.

Wenn ich strippedString so definiere, wie Sie es hier zeigen, erhalte ich eine BAD ACCESS error beim Versuch, es nach dem scanCharactersFromSet:intoStringAufruf zu drucken angezeigt.

Wenn ich es so mache:

NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];

Am Ende habe ich eine leere Zeichenfolge, aber der Code stürzt nicht ab.

Ich musste stattdessen auf das gute alte C zurückgreifen:

for (int i=0; i<[phoneNumber length]; i++) {
    if (isdigit([phoneNumber characterAtIndex:i])) {
        [strippedString appendFormat:@"%c",[phoneNumber characterAtIndex:i]];
    }
}
Michael Pelz-Sherman
quelle
Ich verwende 3.0 und das funktioniert bei mir. Die populärere Antwort von Vries hat nicht funktioniert.
Neo42
Die Antwort Nummer eins wird bei mir nicht funktionieren. Der Scanner stoppt, sobald er ein () oder - erreicht. Diese Antwort funktioniert hervorragend !! Gutes altes C !! Vielen Dank
Jeff
2
Beachten Sie nur, dass das '+' ein Zeichen für die Telefonnummer sein sollte.
Prcela
27

Obwohl dies eine alte Frage mit funktionierenden Antworten ist, habe ich die Unterstützung für internationale Formate verpasst . Basierend auf der Lösung von Simonobo enthält der geänderte Zeichensatz ein Pluszeichen "+". Auch internationale Telefonnummern werden von dieser Änderung unterstützt.

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

Die Swift-Ausdrücke sind

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

Dies ergibt +12345671000 als allgemeines internationales Telefonnummernformat.

Alex
quelle
2
Dies ist die beste Lösung in der Liste, insbesondere wenn Sie das Pluszeichen für internationale Telefonnummern benötigen.
UXUiOS
Aus irgendeinem Grund macht mir die Verwendung eines invertierten Zeichensatzes Angst, was die Leistung betrifft. Weiß jemand zufällig, ob dies eine unbegründete Angst ist?
Devios1
Dieser hat funktioniert! Könnten Sie erklären, wie es funktioniert? @alex
Jayprakash Dubey
11

Hier ist die Swift-Version davon.

import UIKit
import Foundation
var phoneNumber = " 1 (888) 555-5551    "
var strippedPhoneNumber = "".join(phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))
Christopher Wade Cantley
quelle
Swift 2.0: phoneNumber.componentsSeparatedByCharactersInSet (NSCharacterSet.decimalDigitCharacterSet (). InvertedSet) .joinWithSeparator ("")
iluvatar_GR
11

Schnelle Version der beliebtesten Antwort:

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

Edit: Syntax für Swift 2

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

Edit: Syntax für Swift 3

let newString = oldString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
Jon Vogel
quelle
Gibt es eine Möglichkeit, das durch Dezimalstellen getrennte Symbol beizubehalten? Der Punkt (oder das Komma) als Funktion der Standardeinstellungen des Geräts? Ihre Lösung beseitigt alles außer Zahlen
Nicholas
5

Danke für das Beispiel. Es fehlt nur eines, das Inkrement der scanLocation, falls eines der Zeichen in originalString nicht im Zeichen CharacterSet-Objekt gefunden wird. Ich habe eine else {} -Anweisung hinzugefügt, um dies zu beheben.

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"
GregoryN
quelle
4

Es akzeptiert nur Handynummer

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

Es kann erwähnenswert sein, dass die akzeptierte componentsSeparatedByCharactersInSet:und componentsJoinedByString:basierte Antwort keine speichereffiziente Lösung ist. Es reserviert Speicher für den Zeichensatz, für ein Array und für eine neue Zeichenfolge. Selbst wenn es sich nur um temporäre Zuweisungen handelt, kann die Verarbeitung vieler Zeichenfolgen auf diese Weise den Speicher schnell füllen.

Ein speicherfreundlicherer Ansatz wäre, eine veränderbare Kopie der vorhandenen Zeichenfolge zu bearbeiten. In einer Kategorie über 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];
}

Die Profilerstellung der beiden Ansätze hat gezeigt, dass etwa 2/3 weniger Speicher benötigt wird.

Kadam
quelle
2

Sie können reguläre Ausdrücke für veränderbare Zeichenfolgen verwenden:

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

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

Entwickelte die Top-Lösung als Kategorie, um bei umfassenderen Problemen zu helfen:

Schnittstelle:

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

Implementierung:

@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

Verwendung:

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

Swift 3

let notNumberCharacters = NSCharacterSet.decimalDigits.inverted
let intString = yourString.trimmingCharacters(in: notNumberCharacters)
Guido
quelle
Dadurch werden nur nichtstellige Zeichen vom Anfang bis zum Ende abgeschnitten.
Shebuka
1

schnell 4.1

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

Äh. Die erste Antwort scheint mir völlig falsch. NSScanner ist wirklich zum Parsen gedacht. Im Gegensatz zu Regex müssen Sie die Zeichenfolge jeweils um einen winzigen Block analysieren. Sie initialisieren es mit einer Zeichenfolge und es wird ein Index darüber beibehalten, wie weit es entlang der Zeichenfolge gekommen ist. Dieser Index ist immer sein Referenzpunkt, und alle Befehle, die Sie ihm geben, beziehen sich auf diesen Punkt. Sie sagen: "Okay, geben Sie mir den nächsten Teil der Zeichen in diesem Satz" oder "Geben Sie mir die Ganzzahl, die Sie in der Zeichenfolge finden". Diese beginnen am aktuellen Index und bewegen sich vorwärts, bis sie etwas finden, das dies nicht tut Spiel. Wenn das allererste Zeichen bereits nicht übereinstimmt, gibt die Methode NO zurück und der Index wird nicht erhöht.

Der Code im ersten Beispiel durchsucht "(123) 456-7890" nach Dezimalzeichen, was bereits ab dem ersten Zeichen fehlschlägt. Daher ruft der Aufruf von scanCharactersFromSet: intoString: den übergebenen strippedString in Ruhe und gibt NO zurück. Der Code ignoriert die Überprüfung des Rückgabewerts vollständig und lässt den strippedString nicht zugewiesen. Selbst wenn das erste Zeichen eine Ziffer wäre, würde dieser Code fehlschlagen, da er nur die Ziffern zurückgibt, die er bis zum ersten Strich oder Paren oder was auch immer findet.

Wenn Sie NSScanner wirklich verwenden möchten, können Sie so etwas in eine Schleife einfügen und weiter nach einem NO-Rückgabewert suchen. Wenn Sie dies erhalten, können Sie den scanLocation erhöhen und erneut scannen. und Sie müssen auch isAtEnd und yada yada yada überprüfen. Kurz gesagt, falsches Werkzeug für den Job. Michaels Lösung ist besser.

Jack Nutting
quelle
0

Für diejenigen, die nach Telefonextraktion suchen, können Sie die Telefonnummern mit NSDataDetector aus einem Text extrahieren, zum Beispiel:

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

`

Rafael Sanches
quelle
0

Ich habe eine Kategorie in NSString erstellt, um diesen allgemeinen Vorgang zu vereinfachen.

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
Ryan
quelle
0

Ich denke derzeit ist der beste Weg:

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

Wenn Sie nur versuchen, die Zahlen aus der Zeichenfolge zu entnehmen , können Sie sie auch mit regulären Ausdrücken analysieren. Informationen zum Ausführen von Regex in Objective-C finden Sie in RegexKit . Bearbeiten: Wie @Nathan hervorhebt, ist die Verwendung von NSScanner eine viel einfachere Möglichkeit, alle Zahlen aus einer Zeichenfolge zu analysieren. Ich war mir dieser Option überhaupt nicht bewusst, also bitte ich ihn, sie vorzuschlagen. (Ich mag es nicht einmal, Regex selbst zu verwenden, deshalb bevorzuge ich Ansätze, die sie nicht erfordern.)

Wenn Sie Telefonnummern für die Anzeige formatieren möchten, sollten Sie sich NSNumberFormatter ansehen . Ich schlage vor, dass Sie diese verwandte SO-Frage lesen , um Tipps dazu zu erhalten. Denken Sie daran, dass Telefonnummern je nach Standort und / oder Gebietsschema unterschiedlich formatiert sind.

Quinn Taylor
quelle
Oh, die schmerzhaften Stunden, die ich damit verbracht habe, gute Formatierer und Parser für Telefonnummern zu entwickeln. Die verknüpften Threads sind ein guter Anfang, aber der allgemeine Fall der Formatierung globaler Telefonnummern für die Anzeige ist ein langer Weg, und wie in den verknüpften Threads angegeben, gewährt Apple Ihnen keinen Zugriff auf die Adressbuch-Telefonnummernformatierer und ist dies auch Sehr inkonsistent bei der Darstellung von Telefonnummern in der Adressbuch-API. Das einzige, was schwieriger ist, als eine Telefonnummer für die Anzeige zu formatieren, ist festzustellen, ob zwei Telefonnummern gleich sind. Zumindest ist die Frage des OP das einfachste der Probleme.
Rob Napier
Ich glaube, dass diese Links zum Formatieren von Telefonnummern irreführend sind, es sei denn, Sie sind mit einer primitiven, US-zentrierten Implementierung zufrieden. Anstelle eines ordnungsgemäß lokalisierten Telefonnummernformatierers von Apple besteht die einzige Möglichkeit, dies ordnungsgemäß zu tun, darin, die Formatierungsvorlagen vom Gerät (UIPhoneFormats.plist in OS 2.x) zu kopieren und die Vorlagen je nach Gebietsschema des Benutzers selbst zu reproduzieren. Dies ist eine nicht triviale Aufgabe.
Nathan de Vries
Deshalb habe ich die Lokalisierung von Zahlenformatierern erwähnt. Ich habe nicht vorgetäuscht, irgendeine Form einer vollständigen Lösung dafür zu veröffentlichen - es ist eine viel längere Diskussion und es wäre sinnvoller, es zu einer separaten SO-Frage zu machen.
Quinn Taylor
0

Swift 5

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

Basierend auf Jon Vogels Antwort hier handelt es sich um eine Swift String-Erweiterung zusammen mit einigen grundlegenden Tests.

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

Und einige Tests, die zumindest grundlegende Funktionen belegen:

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

Dies beantwortet die Frage des OP, kann jedoch leicht geändert werden, um telefonnummernbezogene Zeichen wie ",; * # +" zu belassen.

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

];

Halte es einfach!

Ryyst
quelle
3
Dadurch werden diese Zeichen nur von Anfang bis Ende abgeschnitten.
Raidfive