У мене є 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"
EDIT: Я оновив код, тому що оригінал був списаний у верхній частині голови, і я подумав, що цього буде достатньо, щоб вказати людей у правильному напрямку. Здається, що за кодом люди можуть просто скопіювати та вставити прямо у свою програму.
Я також погоджуюся з тим, що рішення Майкла Пельца-Шермана є більш доречним, ніж використання NSScanner
, тому ви можете поглянути на це.
Прийнята відповідь є надмірною для того, що просять. Це набагато простіше:
NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
Це чудово, але код не працює для мене на iPhone 3.0 SDK.
Якщо я визначаю strippedString, як ви показуєте тут, я отримую знак BAD ACCESS error
при спробі роздрукувати його після scanCharactersFromSet:intoString
дзвінка.
Якщо я це роблю так:
NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];
Я закінчую порожню рядок, але код не виходить з ладу.
Я мав замість цього вдатися до старого доброго С:
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:@""];
Вираз Swift є
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 не знайдеться всередині числа об'єкта CharacterSet. Я додав ще {{} заяву, щоб виправити це.
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)
Гм. Перша відповідь мені здається абсолютно неправильною. NSScanner дійсно призначений для розбору. На відміну від регулярного вираження, він дозволяє вам розбирати рядок по одному крихітному фрагменту за раз. Ви ініціалізуєте його за допомогою рядка, і він підтримує індекс того, як далеко уздовж рядка він потрапив; Цей індекс завжди є його орієнтиром, і будь-які команди, які ви йому даєте, відносяться до цієї точки. Ви скажете це: "добре, дайте мені наступний фрагмент символів у цьому наборі" або "дайте мені ціле число, яке ви знайдете в рядку", і ті починаються з поточного індексу, і рухайтеся вперед, поки не знайдуть щось, що не матч. Якщо найперший символ вже не відповідає, метод повертає НІ, а індекс не збільшується.
Код у першому прикладі - це сканування "(123) 456-7890" на десяткові символи, яке вже не вдається від самого першого символу, тому виклик scanCharactersFromSet: intoString: залишає переданий strippedString у спокої і повертає НІ; Код повністю ігнорує перевірку повернутого значення, залишаючи strippedString без призначення. Навіть якби перший символ був цифрою, цей код буде невдалим, оскільки він поверне лише знайдені ним цифри до першого тире або батьківського чи іншого.
Якщо ви дійсно хотіли скористатися NSScanner, можете помістити щось подібне у цикл і продовжувати перевіряти відсутність поверненого значення NO. і ви також повинні перевірити 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 . Я пропоную вам ознайомитись із цим пов'язаним питанням ТА, щоб отримати поради щодо цього. Пам’ятайте, що номери телефонів форматуються по-різному, залежно від місця та / або місцеположення.
Швидкий 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"]