Удалить все, кроме номеров, из NSString

У меня есть NSString (номер телефона) с некоторыми скобками и дефисами, так как некоторые номера телефонов отформатированы. Как удалить все символы, кроме чисел из строки?

Ответ 1

Старый вопрос, но как насчет:

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

Он взрывает исходную строку в наборе нецифровых цифр, затем повторно собирает их с помощью пустого разделителя строк. Не так эффективно, как подбор персонажей, но гораздо более компактный в коде.

Ответ 2

Нет необходимости использовать библиотеку регулярных выражений, как предлагают другие ответы - класс, который вы используете, называется NSScanner. Он используется следующим образом:

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"

РЕДАКТИРОВАТЬ: Я обновил код, потому что оригинал был сложен с моей головы, и я подумал, что этого достаточно, чтобы указать людям в правильном направлении. Кажется, что люди после кода могут просто скопировать-вставить прямо в свое приложение.

Я также согласен с тем, что решение Майкла Пельца-Шермана более подходит, чем использование NSScanner, поэтому вы можете взглянуть на это.

Ответ 3

Принятый ответ является излишним для того, что задается. Это намного проще:

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

Ответ 4

Это замечательно, но код для меня не работает на iPhone 3.0 SDK.

Если я определяю strippedString, как вы здесь показываете, я получаю BAD ACCESS error при попытке распечатать его после вызова scanCharactersFromSet:intoString.

Если я сделаю это так:

NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];

Я получаю пустую строку, но код не сбой.

Мне пришлось прибегнуть к добрым старым C:

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

Ответ 5

Хотя это старый вопрос с рабочими ответами, я пропустил поддержку международного формата. На основе решения simonobo измененный набор символов включает знак плюса "+". Международные номера телефонов также поддерживаются этой поправкой.

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

Свитки Swift:

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

Что дает +12345671000 в качестве общего формата международного телефонного номера.

Ответ 6

Вот версия Swift.

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

Ответ 7

Быстрая версия самого популярного ответа:

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

Изменить: Синтаксис для Swift 2

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

Изменить: Синтаксис для Swift 3

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

Ответ 8

Спасибо за пример. Единственное, что не хватает приращению scanLocation в случае, если один из символов в originalString не найден внутри номера объекта CharacterSet. Я добавил else {}, чтобы исправить это.

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"

Ответ 9

Принимает только номер мобильного телефона

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

Ответ 10

Возможно, стоит отметить, что принятый ответ componentsSeparatedByCharactersInSet: и componentsJoinedByString: не является решением для экономии памяти. Он выделяет память для набора символов, для массива и для новой строки. Даже если это только временные выделения, обработка множества строк таким образом может быстро заполнить память.

Другим подходом к памяти будет работа с изменяемой копией строки на месте. В категории над 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];
}

Профилирование двух подходов показало, что это использует примерно 2/3 меньше памяти.

Ответ 11

Вы можете использовать регулярное выражение для изменяемой строки:

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

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

Ответ 12

Построено верхнее решение как категорию, чтобы помочь с более широкими проблемами:

Интерфейс:

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

реализации внешних:

@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

Использование:

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

Ответ 13

Swift 3

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

Ответ 14

Свифт 4.1

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

Ответ 15

Если вы просто хотите получить числа из строки, вы можете использовать регулярные выражения для их анализа. Для выполнения регулярных выражений в Objective-C, проверьте RegexKit. Редактировать: как указывает @Nathan, использование NSScanner - это гораздо более простой способ анализа всех чисел в строке. Я совершенно не знал об этом варианте, так что подпишите его за предложение. (Я даже не люблю использовать регулярные выражения, поэтому я предпочитаю подходы, которые не требуют их.)

Если вы хотите отформатировать телефонные номера для отображения, стоит взглянуть на NSNumberFormatter. Я предлагаю вам прочитать этот связанный вопрос SO для советов по этому вопросу. Помните, что номера телефонов отформатированы по-разному в зависимости от местоположения и/или локали.

Ответ 16

Um. Первый ответ кажется мне совершенно неправильным. NSScanner действительно предназначен для синтаксического анализа. В отличие от регулярного выражения, он разбирает строку на один крошечный кусок за раз. Вы инициализируете его строкой, и она поддерживает индекс того, как далеко по полученной строке; Этот индекс всегда является его точкой отсчета, и любые команды, которые вы ему даете, относятся к этой точке. Вы говорите: "Хорошо, дайте мне следующий кусок символов в этом наборе" или "дайте мне целое число, которое вы найдете в строке", и те, которые начинаются с текущего индекса, и двигайтесь вперед, пока не найдут что-то, что не совпадение. Если первый символ уже не совпадает, тогда метод возвращает NO, а индекс не увеличивается.

Код в первом примере - это сканирование "(123) 456-7890" для десятичных символов, которое уже не выполняется с самого первого символа, поэтому вызов scanCharactersFromSet: inString: оставляет только переданную strippedString и возвращает НЕТ; Код полностью игнорирует проверку возвращаемого значения, оставляя strippedString неназначенным. Даже если первый символ был цифрой, этот код не сработает, так как он только вернет цифры, которые он найдет, до первой тире или паре или что-то еще.

Если вы действительно хотели использовать NSScanner, вы могли бы поместить что-то подобное в цикле и продолжать проверять возвращаемое значение NO, и если вы это сделаете, вы можете увеличить скорость сканирования и повторить сканирование; и вам также нужно проверить isAtEnd и yada yada yada. Короче говоря, неправильный инструмент для работы. Майкл лучше.

Ответ 17

Для тех, кто ищет извлечение телефона, вы можете извлечь телефонные номера из текста с помощью NSDataDetector, например:

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

`

Ответ 18

Я создал категорию в NSString, чтобы упростить эту общую операцию.

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

Ответ 19

Я думаю, что в настоящее время лучший способ:

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

Ответ 20

Swift 5

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

Ответ 21

Основываясь на Jon Vogel, ответьте здесь, это как расширение Swift String вместе с некоторыми базовыми тестами.

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

И некоторые тесты, доказывающие хотя бы базовые функции:

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 = "[email protected]"
        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 == "")
    }
}

Это отвечает на вопрос OP, но его можно легко изменить, чтобы оставить в номерах, связанных с номером, таких как ",; * # +"

Ответ 22

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

];

Держите это просто!