Видаліть усі, крім числа, NSString


157

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

Відповіді:


375

Старе питання, а як щодо:

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

Він вибухає рядок джерела на наборі нецифрових цифр, а потім знову збирає їх, використовуючи порожній роздільник рядків. Не настільки ефективний, як пробір символів, але набагато компактніший за кодом.


6
Дякую! Для інших початківців ви можете створити власний користувальницький NSCharacterSet, виконуючиNSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]
guptron

1
Велике спасибі! Тільки для моєї цікавості, чи маєте ви уявлення, чому NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]не працює?
Томас Беснехард

1
@Tommecpe stringByTrimmingCharactersInSet видаляє лише з початку та в кінці рядка, так що це не впливає після першого невідповідного символу або до останнього невідповідного символу.
simonobo

я хочу зберігати лише цифри та алфавіт, як це можна зробити?
Джекі

1
@Jacky у наведеному вище прикладі ви замінили б [NSCharacterSet decimalDigitCharacterSet]інший, який містить лише цифри та букви. Ви можете побудувати один за рахунок створення NSMutableCharaterSetі пропусканням decimalDigitCharacterSet, uppercaseLetterCharacterSetі lowercaseLetterCharacterSetдо formUnionWithCharacterSet:. Зверніть увагу, що letterCharacterSetвключає також позначення, отже, використання нижньої та великої версій.
кадам

75

Не потрібно використовувати бібліотеку регулярних виразів, як підказують інші відповіді - називається клас, який ви шукаєте 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, тому ви можете поглянути на це.


+1 Гарна відповідь, яка безпосередньо стосується питання. Я відредагував свою відповідь, щоб пропагувати такий підхід, але другу половину я залишаю такою, якою є, оскільки вона все ще корисна), яка вирішує проблему зворотного боку щодо форматування номера телефону для відображення. (Далі, чи можете ви залишити конструктивний коментар, коли зволікаєте, якщо тільки для пізніших читачів?)
Квінн Тейлор

4
Може бути корисно знати, що існує метод + decimalDigitCharacterSet NSCharacterSet, який дасть вам усі десяткові цифри. Це дещо відрізняється від набору списків Натана, оскільки він включає всі символи, що представляють десяткові числа, включаючи, наприклад, арабсько-індійські цифри (١٢٣٤٥ тощо). Залежно від вашої заявки, це може бути проблемою, але, як правило, або хорошою, або нейтральною, і трохи коротшою.
Роб Нап'єр

Я майже впевнений, що ця відповідь насправді не працює, і насправді це не правильний підхід до проблеми. Якщо ви дійсно спробуєте код, як показано (спочатку додавши @ до першого параму до NSLog, зробивши його рядком objc), ви побачите, що він друкує <null> або збої. Чому? Дивіться мою відповідь нижче.
Джек Наттінг

Немає потреби в іншій відповіді - ось для чого коментують. Я оновив рішення, включаючи посилання на рішення Майкла Пельца-Шермана.
Натан де Вріс

4
Це ваааай до складного.
ryyst

63

Прийнята відповідь є надмірною для того, що просять. Це набагато простіше:

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

2
Відповідь (на даний момент) прийнята відповідь більш-менш ідентична цій, але була розміщена на 13 місяців раніше.
Калеб

У той час, коли я відповідав на це, у неї не було такої відповіді. Хоча, здається, нинішня відповідь вже була запропонована, і я її пропустив: web.archive.org/web/20101115214033/http://stackoverflow.com/…
Yacine Filali

30

Це чудово, але код не працює для мене на 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]];
    }
}

Я працюю 3.0 і це працює для мене. Більш популярна відповідь від Вріса не спрацювала.
Neo42

Відповідь номер один для мене не підійде. Сканер зупиняється, коли він досягне () або - Ця відповідь чудово працює !! Добрий старий C !! Дякую
Jeff

2
Зауважте лише, що для номера телефону повинен бути дозволений символ "+".
Prcela

27

Хоча це старе питання з робочими відповідями, я пропустив підтримку міжнародного формату . На основі рішення 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 як загальний міжнародний формат телефонних номерів.


2
Це найкраще рішення у списку, особливо якщо вам потрібен знак плюс для міжнародних телефонних номерів.
UXUiOS

Чомусь використання перевернутого набору символів лякає мене, наскільки йде продуктивність. Хтось знає, чи це безпідставний страх?
devios1

Цей працював! Чи можете ви пояснити його роботу? @alex
Jayprakash Dubey

11

Ось версія Swift цього.

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

Swift 2.0: phoneNumber.com ComponentsSeparatedByCharactersInSet (NSCharacterSet.decimalDigitCharacterSet (). InvertedSet) .joinWithSeparator ("")
iluvatar_GR

11

Швидка версія найпопулярнішої відповіді:

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: "")

чи є спосіб зберегти відокремлений знак десяткової? Крапка (або кома) як функція налаштувань пристрою за замовчуванням? Ваше рішення усуне все, крім цифр
Микола

5

Дякую за приклад. У ньому є лише одне, у чому відсутня приріст 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"

4

Він приймає лише мобільний номер

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

3

Можливо, варто відзначити, що прийнята 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 менше пам'яті.


2

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

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

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

1

Побудував найкраще рішення як категорію для вирішення більш широких проблем:

Інтерфейс:

@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:@""];

1

Швидкий 3

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

Це дозволить обрізати лише нецифрові символи від початку та до кінця.
Шебука

1

стрімкий 4.1

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

0

Гм. Перша відповідь мені здається абсолютно неправильною. NSScanner дійсно призначений для розбору. На відміну від регулярного вираження, він дозволяє вам розбирати рядок по одному крихітному фрагменту за раз. Ви ініціалізуєте його за допомогою рядка, і він підтримує індекс того, як далеко уздовж рядка він потрапив; Цей індекс завжди є його орієнтиром, і будь-які команди, які ви йому даєте, відносяться до цієї точки. Ви скажете це: "добре, дайте мені наступний фрагмент символів у цьому наборі" або "дайте мені ціле число, яке ви знайдете в рядку", і ті починаються з поточного індексу, і рухайтеся вперед, поки не знайдуть щось, що не матч. Якщо найперший символ вже не відповідає, метод повертає НІ, а індекс не збільшується.

Код у першому прикладі - це сканування "(123) 456-7890" на десяткові символи, яке вже не вдається від самого першого символу, тому виклик scanCharactersFromSet: intoString: залишає переданий strippedString у спокої і повертає НІ; Код повністю ігнорує перевірку повернутого значення, залишаючи strippedString без призначення. Навіть якби перший символ був цифрою, цей код буде невдалим, оскільки він поверне лише знайдені ним цифри до першого тире або батьківського чи іншого.

Якщо ви дійсно хотіли скористатися NSScanner, можете помістити щось подібне у цикл і продовжувати перевіряти відсутність поверненого значення NO. і ви також повинні перевірити isAtEnd і yada yada yada. Словом, неправильний інструмент для роботи. Рішення Майкла краще.


0

Для тих, хто шукає вилучення телефону, ви можете дістати телефонні номери з тексту за допомогою 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]);
            }
        }
    }
}

`


0

Я створив категорію на 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

0

Я думаю, що на даний момент найкращим способом є:

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

0

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

Якщо ви хочете відформатувати номери телефонів для відображення, варто поглянути на NSNumberFormatter . Я пропоную вам ознайомитись із цим пов'язаним питанням ТА, щоб отримати поради щодо цього. Пам’ятайте, що номери телефонів форматуються по-різному, залежно від місця та / або місцеположення.


О, болісні години, які я провів, розробляючи хороші форматори номерів телефонів і аналізатори. Пов’язані теми - це гарний початок, але загальний випадок форматування глобальних телефонних номерів для відображення - це довга дорога, і як зазначається в пов'язаних потоках, Apple не надає вам жодного доступу до форматорів телефонних номерів Адресної книги, і є дуже непослідовний у поданні телефонних номерів із API адресної книги. Єдине, що важче, ніж форматувати номер телефону для відображення, - це визначити, чи однакові два телефонні номери. Принаймні питання ОП - це найпростіша з проблем.
Роб Нап'єр

Я вважаю, що ці посилання на форматування номерів телефонів вводять в оману, якщо ви не задоволені примітивною, орієнтованою на США реалізацією. Замість належного локалізованого форматора форматування телефонних номерів від Apple, єдиний спосіб зробити це належним чином - скопіювати шаблони форматування з пристрою (UIPhoneFormats.plist в ОС 2.x) та відтворити шаблони самостійно залежно від місцевості користувачів. Це нетривіальне завдання.
Натан де Вріс

Тому я згадав про локалізацію форматів чисел. Я не претендував на публікацію будь-якої форми повного рішення для цього - це набагато довше обговорення, і було б більше сенсу робити це окремим питанням SO.
Квінн Тейлор


-1

Виходячи з відповіді Джона Фогеля, тут це розширення 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 == "")
    }
}

Це відповідає на питання ОП, але його можна легко змінити, залишаючи символи, пов'язані з номером телефону, наприклад ",; * # +"


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

];

Не ускладнювати!


3
це лише обріже ці символи від початку та до кінця.
raidfive
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.