Hapus semua kecuali angka dari NSString

157

Saya memiliki NSString (nomor telepon) dengan beberapa tanda kurung dan tanda hubung karena beberapa nomor telepon diformat. Bagaimana saya menghapus semua karakter kecuali angka dari string?

Ben Harris
sumber

Jawaban:

375

Pertanyaan lama, tapi bagaimana dengan:

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

Ini meledak string sumber pada set non-digit, lalu pasang kembali menggunakan pemisah string kosong. Tidak seefisien memilih karakter, tetapi jauh lebih ringkas dalam kode.

simonobo
sumber
6
Terima kasih! Untuk pemula lainnya, Anda dapat membuat NSCharacterSet kustom Anda sendiri dengan melakukanNSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]
guptron
1
Terima kasih banyak! Hanya untuk keingintahuan saya, apakah Anda punya ide mengapa NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]tidak bekerja?
Thomas Besnehard
1
@Tommecpe stringByTrimmingCharactersInSet hanya menghapus dari awal dan akhir string, sehingga tidak mempengaruhi setelah karakter yang tidak cocok pertama, atau sebelum karakter terakhir yang tidak cocok.
simonobo
saya hanya ingin menyimpan angka dan alfabet, bagaimana saya bisa melakukannya?
Jacky
1
@ Jacky dalam contoh di atas Anda akan mengganti [NSCharacterSet decimalDigitCharacterSet]dengan yang lain yang hanya berisi angka dan huruf. Anda dapat membangunnya dengan membuat a NSMutableCharaterSetdan meneruskan a decimalDigitCharacterSet, uppercaseLetterCharacterSetdan lowercaseLetterCharacterSetke formUnionWithCharacterSet:. Catatan yang letterCharacterSetmenyertakan tanda juga, maka penggunaan versi huruf kecil dan besar.
kadam
75

Tidak perlu menggunakan pustaka ekspresi reguler seperti jawaban yang disarankan - kelas yang Anda cari dipanggil NSScanner. Ini digunakan sebagai berikut:

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: Saya telah memperbarui kode karena yang asli dihapus dari atas kepala saya dan saya pikir itu akan cukup untuk mengarahkan orang ke arah yang benar. Tampaknya orang-orang mengejar kode mereka hanya dapat menyalin-menempel langsung ke aplikasi mereka.

Saya juga setuju bahwa solusi Michael Pelz-Sherman lebih tepat daripada menggunakan NSScanner, jadi Anda mungkin ingin melihatnya.

Nathan de Vries
sumber
+1 Jawaban bagus yang langsung menjawab pertanyaan. Saya telah mengedit jawaban saya untuk menganjurkan pendekatan ini, tetapi saya membiarkan paruh kedua apa adanya, karena masih membantu) yang membahas masalah sisi lain dari memformat nomor telepon untuk tampilan. (Berikutnya, dapat Anda meninggalkan komentar yang konstruktif ketika downvoting, jika hanya untuk pembaca nanti?)
Quinn Taylor
4
Mungkin berguna untuk mengetahui bahwa ada metode + decimalDigitCharacterSet dari NSCharacterSet yang akan memberi Anda semua angka desimal. Ini sedikit berbeda dari daftar Nathan yang ditetapkan, karena ini mencakup semua simbol yang mewakili angka desimal termasuk, misalnya, angka Arab-Indikator (١٢٣٤٥ dll). Tergantung pada aplikasi Anda, itu kadang-kadang bisa menjadi masalah, tetapi umumnya baik atau netral, dan sedikit lebih pendek untuk diketik.
Rob Napier
Saya cukup yakin bahwa jawaban ini tidak benar-benar berfungsi, dan bukan pendekatan yang tepat untuk masalah tersebut. Jika Anda benar-benar mencoba kode seperti yang ditunjukkan (pertama-tama menambahkan @ sebelum param pertama ke NSLog, menjadikannya sebagai string objc), Anda akan menemukan bahwa ia mencetak <null> atau crash. Mengapa? Lihat jawaban saya di bawah ini.
Jack Nutting
Tidak perlu jawaban lain - untuk itulah komentar. Saya telah memperbarui solusi, termasuk referensi ke solusi Michael Pelz-Sherman.
Nathan de Vries
4
Itu sangat sulit.
ryyst
63

Jawaban yang diterima terlalu banyak untuk apa yang diminta. Ini jauh lebih sederhana:

NSString *pureNumbers = [[phoneNumberString componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
Yacine Filali
sumber
2
Jawaban (saat ini) diterima kurang lebih identik dengan yang ini, tetapi telah diposting 13 bulan sebelumnya.
Caleb
Pada saat saya menjawab ini, tidak ada jawaban. Walaupun sepertinya jawaban saat ini telah diajukan dan saya melewatkannya: web.archive.org/web/20101115214033/http://stackoverflow.com/…
Yacine Filali
30

Ini bagus, tetapi kodenya tidak berfungsi untuk saya di iPhone 3.0 SDK.

Jika saya mendefinisikan strippedString seperti yang Anda tunjukkan di sini, saya mendapatkan BAD ACCESS errorketika mencoba mencetaknya setelah scanCharactersFromSet:intoStringpanggilan.

Jika saya melakukannya seperti itu:

NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];

Saya berakhir dengan string kosong, tetapi kode tidak macet.

Saya harus menggunakan C lama yang baik sebagai gantinya:

for (int i=0; i<[phoneNumber length]; i++) {
    if (isdigit([phoneNumber characterAtIndex:i])) {
        [strippedString appendFormat:@"%c",[phoneNumber characterAtIndex:i]];
    }
}
Michael Pelz-Sherman
sumber
Saya menjalankan 3.0 dan ini berfungsi untuk saya. Jawaban yang lebih populer dari Vries tidak berhasil.
Neo42
Jawaban nomor satu tidak akan bekerja untuk saya. Pemindai berhenti setelah mencapai () atau - Jawaban ini berhasil !! C tua yang baik !! Terima kasih
Jeff
2
Hanya perhatikan bahwa karakter '+' harus diizinkan untuk nomor telepon.
Prcela
27

Meskipun ini adalah pertanyaan lama dengan jawaban yang berfungsi, saya kehilangan dukungan format internasional . Berdasarkan solusi simonobo, set karakter yang diubah mencakup tanda tambah "+". Nomor telepon internasional juga didukung oleh amandemen ini.

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

Ekspresi Swift adalah

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

Yang menghasilkan +12345671000 sebagai format nomor telepon internasional umum.

alex
sumber
2
Ini adalah solusi terbaik dalam daftar, terutama jika Anda memerlukan tanda tambah untuk nomor telepon internasional.
UXUiOS
Untuk beberapa alasan, menggunakan set karakter terbalik membuat saya takut sejauh kinerja berjalan. Adakah yang tahu kalau ini adalah ketakutan yang tidak berdasar?
devios1
Yang ini berhasil! Bisakah Anda jelaskan cara kerjanya? @alex
Jayprakash Dubey
11

Ini adalah versi Swift dari ini.

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

Versi cepat dari jawaban paling populer:

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

Sunting: Sintaks untuk Swift 2

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

Sunting: Sintaks untuk Swift 3

let newString = oldString.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
Jon Vogel
sumber
apakah ada cara untuk menjaga simbol dipisahkan desimal? Titik (atau koma) sebagai fungsi dari pengaturan default perangkat? Solusi Anda menghilangkan semuanya kecuali angka
Nicholas
5

Terima kasih untuk contohnya. Itu hanya memiliki satu hal yang hilang penambahan scanLocation jika salah satu karakter dalam originalString tidak ditemukan di dalam objek nomor CharacterSet. Saya telah menambahkan pernyataan lain {} untuk memperbaikinya.

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
sumber
4

Hanya menerima nomor ponsel

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

Mungkin perlu dicatat bahwa jawaban yang diterima componentsSeparatedByCharactersInSet:dan componentsJoinedByString:berbasis bukan solusi yang efisien memori. Ini mengalokasikan memori untuk set karakter, untuk array dan untuk string baru. Bahkan jika ini hanya alokasi sementara, memproses banyak string dengan cara ini dapat dengan cepat mengisi memori.

Sebuah pendekatan yang lebih ramah memori adalah untuk beroperasi pada salinan string yang dapat berubah di tempatnya. Dalam kategori di atas 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];
}

Profil kedua pendekatan telah menunjukkan ini menggunakan sekitar 2/3 lebih sedikit memori.

kadam
sumber
2

Anda dapat menggunakan ekspresi reguler pada string yang dapat diubah:

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

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

Membangun solusi teratas sebagai kategori untuk membantu masalah yang lebih luas:

Antarmuka:

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

Implemenasi:

@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

Pemakaian:

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

Cepat 3

let notNumberCharacters = NSCharacterSet.decimalDigits.inverted
let intString = yourString.trimmingCharacters(in: notNumberCharacters)
guido
sumber
Ini hanya akan memangkas karakter non digit dari awal dan akhir.
Shebuka
1

cepat 4.1

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

Um Jawaban pertama tampaknya sama sekali salah bagi saya. NSScanner benar-benar dimaksudkan untuk parsing. Tidak seperti regex, ini membuat Anda mengurai string satu potongan kecil sekaligus. Anda menginisialisasi dengan string, dan mempertahankan indeks seberapa jauh sepanjang string itu didapat; Indeks itu selalu menjadi titik rujukannya, dan perintah apa pun yang Anda berikan relatif terhadap titik itu. Anda mengatakannya, "ok, beri saya potongan karakter berikutnya dalam set ini" atau "beri saya bilangan bulat yang Anda temukan dalam string", dan mereka mulai pada indeks saat ini, dan bergerak maju sampai mereka menemukan sesuatu yang tidak pertandingan. Jika karakter pertama sudah tidak cocok, maka metode mengembalikan NO, dan indeks tidak bertambah.

Kode dalam contoh pertama adalah pemindaian "(123) 456-7890" untuk karakter desimal, yang sudah gagal dari karakter pertama, sehingga panggilan untuk memindaiCharactersFromSet: keString: membiarkan strippedString yang dilewati saja, dan mengembalikan TIDAK; Kode benar-benar mengabaikan memeriksa nilai kembali, meninggalkan strippedString tidak ditetapkan. Bahkan jika karakter pertama adalah digit, kode itu akan gagal, karena hanya akan mengembalikan digit yang ditemukannya hingga tanda hubung pertama atau paren atau apa pun.

Jika Anda benar-benar ingin menggunakan NSScanner, Anda dapat memasukkan sesuatu seperti itu dalam satu lingkaran, dan terus memeriksa nilai pengembalian NO, dan jika Anda mendapatkannya Anda dapat menambahkan scanLocation dan memindai lagi; dan Anda juga harus memeriksa isAtEnd, dan yada yada yada. Singkatnya, alat yang salah untuk pekerjaan itu. Solusi Michael lebih baik.

Jack Nutting
sumber
0

Untuk mereka yang mencari ekstraksi telepon, Anda dapat mengekstrak nomor telepon dari teks menggunakan NSDataDetector, misalnya:

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
sumber
0

Saya membuat kategori pada NSString untuk menyederhanakan operasi umum ini.

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
sumber
0

Saya pikir cara terbaik saat ini adalah:

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

Jika Anda hanya ingin mengambil angka dari string, Anda tentu bisa menggunakan ekspresi reguler untuk menguraikannya. Untuk melakukan regex di Objective-C, periksa RegexKit . Sunting: Seperti yang ditunjukkan Nathan, menggunakan NSScanner adalah cara yang lebih sederhana untuk mem-parsing semua angka dari sebuah string. Saya benar-benar tidak menyadari pilihan itu, jadi dia menyarankan untuk menyarankannya. (Saya bahkan tidak suka menggunakan regex sendiri, jadi saya lebih suka pendekatan yang tidak memerlukannya.)

Jika Anda ingin memformat nomor telepon untuk tampilan, ada baiknya melihat NSNumberFormatter . Saya sarankan Anda membaca pertanyaan SO terkait ini untuk tips tentang melakukannya. Ingat bahwa nomor telepon diformat berbeda tergantung pada lokasi dan / atau lokal.

Quinn Taylor
sumber
Oh, jam-jam menyakitkan yang saya habiskan untuk mengembangkan pemformat dan parser nomor telepon yang baik. Utas yang ditautkan adalah awal yang baik, tetapi kasus umum untuk memformat nomor telepon global untuk tampilan adalah jalan yang panjang, dan seperti yang tercantum dalam utas yang ditautkan, Apple tidak memberi Anda akses ke pemformat nomor telepon Buku Alamat, dan sangat tidak konsisten dalam bagaimana nomor telepon disajikan dari API Buku Alamat. Satu-satunya hal yang lebih sulit daripada memformat nomor telepon untuk tampilan adalah menentukan apakah dua nomor telepon sama. Setidaknya pertanyaan OP adalah masalah yang paling mudah.
Rob Napier
Saya percaya bahwa tautan-tautan ke pemformatan nomor telepon itu menyesatkan kecuali Anda puas dengan penerapan yang primitif dan berpusat pada AS. Sebagai pengganti formatter nomor telepon lokal yang tepat dari Apple, satu-satunya cara untuk melakukan ini dengan benar adalah menyalin template format dari perangkat (UIPhoneFormats.plist di OS 2.x) dan mereproduksi template sendiri tergantung pada lokal pengguna. Ini adalah tugas yang tidak sepele.
Nathan de Vries
Itu sebabnya saya menyebutkan lokalisasi jumlah formatters. Saya tidak berpura-pura memposting segala bentuk solusi lengkap untuk itu - ini adalah diskusi yang jauh lebih lama dan akan lebih masuk akal untuk membuatnya menjadi pertanyaan SO yang terpisah.
Quinn Taylor
0

Cepat 5

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

Berdasarkan jawaban Jon Vogel di sini itu adalah sebagai ekstensi Swift String bersama dengan beberapa tes dasar.

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

Dan beberapa tes membuktikan setidaknya fungsi dasar:

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

Ini menjawab pertanyaan OP tetapi bisa dengan mudah dimodifikasi untuk meninggalkan karakter terkait nomor telepon seperti ",; * # +"

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

];

Tetap sederhana!

ryyst
sumber
3
ini hanya akan memangkas karakter-karakter itu dari awal dan akhir.
raidfive