У меня есть NSString (номер телефона) с некоторыми скобками и дефисами, так как некоторые номера телефонов отформатированы. Как бы я удалил все символы, кроме чисел, из строки?
У меня есть NSString (номер телефона) с некоторыми скобками и дефисами, так как некоторые номера телефонов отформатированы. Как бы я удалил все символы, кроме чисел, из строки?
Ответы:
Старый вопрос, но как насчет:
NSString *newString = [[origString componentsSeparatedByCharactersInSet:
[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
componentsJoinedByString:@""];
Он взрывает исходную строку на множестве нецифровых чисел, а затем собирает их, используя пустой разделитель строк. Не так эффективно, как выбор символов, но гораздо компактнее в коде.
NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]
не работает?
[NSCharacterSet decimalDigitCharacterSet]
другой, который содержит только цифры и буквы. Вы можете создать один, создавая NSMutableCharaterSet
и передавая decimalDigitCharacterSet
, uppercaseLetterCharacterSet
и lowercaseLetterCharacterSet
к formUnionWithCharacterSet:
. Обратите внимание, что также letterCharacterSet
включает в себя метки, следовательно, использование строчных и прописных версий.
Нет необходимости использовать библиотеку регулярных выражений, как подсказывают другие ответы - вызывается класс, который вам нужен 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
, так что вы можете взглянуть на это.
Принятый ответ является излишним для того, что просят. Это намного проще:
NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
Это здорово, но код не работает для меня на 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]];
}
}
Хотя это старый вопрос с рабочими ответами, я пропустил поддержку международного формата . Основанный на решении simonobo, измененный набор символов включает в себя знак плюс "+". Международные телефонные номера также поддерживаются этой поправкой.
NSString *condensedPhoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:
[[NSCharacterSet characterSetWithCharactersInString:@"+0123456789"]
invertedSet]]
componentsJoinedByString:@""];
Свифт выражений
var phoneNumber = " +1 (234) 567-1000 "
var allowedCharactersSet = NSMutableCharacterSet.decimalDigitCharacterSet()
allowedCharactersSet.addCharactersInString("+")
var condensedPhoneNumber = phoneNumber.componentsSeparatedByCharactersInSet(allowedCharactersSet.invertedSet).joinWithSeparator("")
Что приводит к +12345671000 в качестве общего формата международного телефонного номера.
Вот Swift-версия этого.
import UIKit
import Foundation
var phoneNumber = " 1 (888) 555-5551 "
var strippedPhoneNumber = "".join(phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))
Свифт версия самого популярного ответа:
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: "")
Спасибо за пример. У него есть только одна вещь, пропускающая приращение scanLocation в случае, если один из символов в originalString не найден внутри объекта NumberSet. Я добавил оператор 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"
Возможно, стоит отметить, что принятый 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 меньше памяти.
Создано топовое решение как категория, чтобы помочь с более широкими проблемами:
Интерфейс:
@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:@""];
Свифт 4.1
var str = "75003 Paris, France"
var stringWithoutDigit = (str.components(separatedBy:CharacterSet.decimalDigits)).joined(separator: "")
print(stringWithoutDigit)
Um. Первый ответ кажется мне совершенно неверным. NSScanner действительно предназначен для разбора. В отличие от регулярных выражений, вы разбираете строку по одному крошечному фрагменту за раз. Вы инициализируете его строкой, и он поддерживает индекс того, как далеко вдоль строки он получен; Этот показатель всегда его точка отсчета, и все команды, которые вы даете его по сравнению с этой точкой. Вы говорите: «Хорошо, дайте мне следующий кусок символов в этом наборе» или «Дайте мне целое число, которое вы найдете в строке», и они начинаются с текущего индекса и продвигаются, пока не найдут то, что не соответствие. Если самый первый символ уже не совпадает, то метод возвращает NO, и индекс не увеличивается.
Код в первом примере сканирует "(123) 456-7890" на наличие десятичных символов, что уже не выполняется с самого первого символа, поэтому при вызове scanCharactersFromSet: intoString: оставляется переданная strippedString в одиночку и возвращается NO; Код полностью игнорирует проверку возвращаемого значения, оставляя strippedString неназначенным. Даже если бы первый символ был цифрой, этот код потерпел бы неудачу, так как он возвращал бы только те цифры, которые он обнаружил, до первой черты или скобки или чего-то еще.
Если вы действительно хотите использовать NSScanner, вы можете поместить что-то подобное в цикл и продолжать проверять возвращаемое значение NO, а если вы его получите, вы можете увеличивать scanLocation и сканировать снова; и вы также должны проверить isAtEnd и yada yada yada. Короче говоря, неправильный инструмент для работы. Решение Майкла лучше.
Для тех, кто ищет извлечение телефона, вы можете извлечь телефонные номера из текста, используя 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]);
}
}
}
}
`
Я создал категорию на NSString, чтобы упростить эту обычную операцию.
@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
Если вы просто хотите получить числа из строки, вы можете использовать регулярные выражения для их анализа. Для выполнения регулярных выражений в Objective-C, проверьте RegexKit . Редактировать: как указывает @Nathan, использование NSScanner - это гораздо более простой способ анализа всех чисел из строки. Я совершенно не знал об этом варианте, так что подпишите его за предложение. (Я даже не люблю использовать регулярные выражения, поэтому я предпочитаю подходы, которые не требуют их.)
Если вы хотите отформатировать телефонные номера для отображения, стоит взглянуть на NSNumberFormatter . Я предлагаю вам прочитать этот связанный вопрос SO для советов по этому вопросу. Помните, что номера телефонов отформатированы по-разному в зависимости от местоположения и / или локали.
Swift 5
let newString = origString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
Основываясь на ответе Джона Фогеля, он представляет собой расширение 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 = "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 == "")
}
}
Это отвечает на вопрос ОП, но его можно легко изменить, чтобы оставить в телефонном номере связанные символы, такие как ",; * # +"
NSString *originalPhoneNumber = @"(123) 123-456 abc";
NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
NSString *trimmedPhoneNumber = [originalPhoneNumber stringByTrimmingCharactersInSet:numbers];
];
Будь проще!
NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]