Xóa tất cả trừ số từ NSString


157

Tôi có một NSString (số điện thoại) với một số dấu ngoặc đơn và dấu gạch nối vì một số số điện thoại được định dạng. Làm thế nào tôi có thể loại bỏ tất cả các ký tự ngoại trừ số từ chuỗi?

Câu trả lời:


375

Câu hỏi cũ, nhưng làm thế nào về:

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

Nó phát nổ chuỗi nguồn trên tập hợp các chữ số không, sau đó ghép lại chúng bằng cách sử dụng một dấu tách chuỗi trống. Không hiệu quả như chọn qua các ký tự, nhưng mã nhỏ gọn hơn nhiều.


6
Cảm ơn! Đối với những người mới bắt đầu khác, bạn có thể tạo NSCharacterset tùy chỉnh của riêng mình bằng cách thực hiệnNSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]
guptron 20/03/13

1
Tuyệt vời ! Chỉ vì sự tò mò của tôi, bạn có biết tại sao NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]Không hoạt động không?
Thomas Besnehard

1
@Tommecpe chuỗiByTrimmingChar characterInset chỉ xóa từ đầu và cuối chuỗi, vì vậy nó không ảnh hưởng đến ký tự không khớp đầu tiên hoặc trước ký tự không khớp cuối cùng.
simonobo

tôi muốn chỉ giữ số và bảng chữ cái, làm thế nào tôi có thể làm điều đó?
Jacky

1
@Jacky trong ví dụ trên bạn sẽ thay thế [NSCharacterSet decimalDigitCharacterSet]bằng một cái khác chỉ chứa số và chữ cái. Bạn có thể xây dựng một bằng cách tạo một NSMutableCharaterSetvà vượt qua a decimalDigitCharacterSet, uppercaseLetterCharacterSetlowercaseLetterCharacterSetđến formUnionWithCharacterSet:. Lưu ý letterCharacterSetbao gồm cả nhãn hiệu, do đó sử dụng các phiên bản chữ thường và chữ hoa.
kadam

75

Không cần sử dụng thư viện biểu thức chính quy như các câu trả lời khác gợi ý - lớp bạn theo sau được gọi NSScanner. Nó được sử dụng như sau:

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: Tôi đã cập nhật mã bởi vì bản gốc đã được viết ra khỏi đỉnh đầu của tôi và tôi nghĩ rằng nó sẽ đủ để chỉ cho mọi người đi đúng hướng. Có vẻ như mọi người đang theo mã họ chỉ có thể sao chép-dán thẳng vào ứng dụng của họ.

Tôi cũng đồng ý rằng giải pháp của Michael Pelz-Sherman phù hợp hơn là sử dụng NSScanner, vì vậy bạn có thể muốn xem xét điều đó.


+1 Câu trả lời tốt trực tiếp giải quyết câu hỏi. Tôi đã chỉnh sửa câu trả lời của mình để ủng hộ cách tiếp cận này, nhưng tôi vẫn để nguyên nửa sau, vì nó vẫn hữu ích) giải quyết vấn đề mặt trái của việc định dạng số điện thoại để hiển thị. (Tiếp theo, bạn có thể để lại nhận xét mang tính xây dựng khi hạ cấp, nếu chỉ dành cho những độc giả sau này không?)
Quinn Taylor

4
Có thể hữu ích khi biết rằng có một phương pháp + binaryDigitCharacterset của NSCharacterSet sẽ cung cấp cho bạn tất cả các chữ số thập phân. Điều này hơi khác so với các danh sách của Nathan, vì nó bao gồm tất cả các ký hiệu đại diện cho các số thập phân bao gồm, ví dụ, các chữ số Ả Rập (, v.v.). Tùy thuộc vào ứng dụng của bạn, điều đó đôi khi có thể là một vấn đề, nhưng nói chung là tốt hoặc trung tính và ngắn hơn một chút để gõ.
Rob Napier

Tôi khá chắc chắn rằng câu trả lời này không thực sự hiệu quả và không thực sự là cách tiếp cận đúng cho vấn đề. Nếu bạn thực sự thử mã như được hiển thị (lần đầu tiên thêm @ trước thông số đầu tiên vào NSLog, biến nó thành chuỗi objc), bạn sẽ thấy rằng nó in <null> hoặc gặp sự cố. Tại sao? Xem câu trả lời của tôi dưới đây.
Jack Nting

Không cần câu trả lời khác - đó là những gì bình luận dành cho. Tôi đã cập nhật giải pháp, bao gồm một tham chiếu đến giải pháp của Michael Pelz-Sherman.
Nathan de Vries

4
Đó là waaaay phức tạp.
ryyst

63

Câu trả lời được chấp nhận là quá mức cho những gì đang được hỏi. Điều này đơn giản hơn nhiều:

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

2
Câu trả lời (hiện tại) được chấp nhận ít nhiều giống với câu trả lời này, nhưng đã được đăng trước đó 13 tháng.
Caleb

Tại thời điểm tôi trả lời, nó không có câu trả lời này. Mặc dù có vẻ như câu trả lời hiện tại đã được đề xuất và tôi đã bỏ lỡ nó: web.archive.org/web/20101115214033/http://stackoverflow.com/
Yacine Filali

30

Điều này thật tuyệt, nhưng mã không hoạt động với tôi trên SDK iPhone 3.0.

Nếu tôi xác định tước sọc khi bạn hiển thị ở đây, tôi sẽ nhận được BAD ACCESS errorkhi cố gắng in nó sau scanCharactersFromSet:intoStringcuộc gọi.

Nếu tôi làm như vậy:

NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];

Tôi kết thúc với một chuỗi rỗng, nhưng mã không bị lỗi.

Thay vào đó, tôi đã phải dùng đến C cũ tốt:

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

Tôi đang chạy 3.0 và điều này làm việc cho tôi. Câu trả lời phổ biến hơn từ Vries đã không hoạt động.
Neo42

Câu trả lời số một sẽ không làm việc cho tôi. Máy quét dừng lại khi nó đạt đến () hoặc - Câu trả lời này hoạt động rất tốt !! Tốt lắm C !! Cảm ơn bạn
Jeff

2
Chỉ lưu ý rằng ký tự '+' phải được phép cho số điện thoại.
Prcela

27

Mặc dù đây là một câu hỏi cũ với câu trả lời làm việc, tôi đã bỏ lỡ hỗ trợ định dạng quốc tế . Dựa trên giải pháp của simonobo, bộ ký tự được thay đổi bao gồm dấu cộng "+". Số điện thoại quốc tế cũng được hỗ trợ bởi sửa đổi này.

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

Các biểu thức Swift là

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

Mà mang lại +12345671000 như một định dạng số điện thoại quốc tế phổ biến.


2
Đây là giải pháp tốt nhất trong danh sách, đặc biệt nếu bạn cần dấu cộng cho số điện thoại quốc tế.
UXUiOS

Vì một số lý do, việc sử dụng một bộ ký tự đảo ngược làm tôi sợ về hiệu suất. Có ai tình cờ biết nếu đây là một nỗi sợ vô căn cứ?
devios1

Cái này làm việc! Bạn có thể giải thích nó hoạt động? @alex
Jayprakash Dubey

11

Đây là phiên bản Swift của cái này.

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

Swift 2.0: phoneNumber.componentsSeparatedByChar characterInset (NSCharacterSet.decimalDigitCharacterSet (). Invertedset) .joinWithSeparator ("")
iluvatar_GR

11

Phiên bản Swift của câu trả lời phổ biến nhất:

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

Chỉnh sửa: Cú pháp cho Swift 2

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

Chỉnh sửa: Cú pháp cho Swift 3

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

Có cách nào để giữ biểu tượng tách thập phân? Dấu chấm (hoặc dấu phẩy) là một chức năng của cài đặt mặc định của thiết bị? Giải pháp của bạn loại bỏ mọi thứ trừ những con số
Nicholas

5

Cảm ơn ví dụ. Nó chỉ có một thứ thiếu sự gia tăng của scanLocation trong trường hợp một trong các ký tự trong gốcString không được tìm thấy bên trong các đối tượng Ký tự số. Tôi đã thêm một câu lệnh {} khác để sửa lỗi này.

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

Nó chỉ chấp nhận số điện thoại

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

3

Có thể đáng lưu ý rằng sự chấp nhận componentsSeparatedByCharactersInSet:componentsJoinedByString: câu trả lời dựa trên không phải là một giải pháp hiệu quả bộ nhớ. Nó phân bổ bộ nhớ cho bộ ký tự, cho một mảng và cho một chuỗi mới. Ngay cả khi đây chỉ là phân bổ tạm thời, xử lý nhiều chuỗi theo cách này có thể nhanh chóng lấp đầy bộ nhớ.

Một cách tiếp cận thân thiện với bộ nhớ sẽ là vận hành trên một bản sao có thể thay đổi của chuỗi. Trong một danh mục trên 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];
}

Cấu hình hai cách tiếp cận đã cho thấy điều này bằng cách sử dụng khoảng 2/3 bộ nhớ.


2

Bạn có thể sử dụng biểu thức chính quy trên chuỗi có thể thay đổi:

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

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

1

Xây dựng giải pháp hàng đầu như một danh mục để giúp giải quyết các vấn đề rộng hơn:

Giao diện:

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

Thực hiện:

@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

Sử dụng:

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

1

Swift 3

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

Điều này sẽ chỉ cắt các ký tự không chữ số từ đầu và cuối.
Shebuka

1

nhanh 4,1

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

0

Ừm. Câu trả lời đầu tiên dường như hoàn toàn sai với tôi. NSScanner thực sự có nghĩa là để phân tích cú pháp. Không giống như regex, nó có một lần phân tích cú pháp chuỗi nhỏ. Bạn khởi tạo chuỗi đó bằng một chuỗi và nó duy trì một chỉ số về khoảng cách dọc theo chuỗi; Chỉ mục đó luôn là điểm tham chiếu của nó và bất kỳ lệnh nào bạn đưa ra đều liên quan đến điểm đó. Bạn nói với nó, "ok, đưa cho tôi đoạn ký tự tiếp theo trong tập hợp này" hoặc "đưa cho tôi số nguyên bạn tìm thấy trong chuỗi" và chúng bắt đầu ở chỉ mục hiện tại và tiến về phía trước cho đến khi chúng tìm thấy thứ gì đó không trận đấu. Nếu chính ký tự đầu tiên không khớp, thì phương thức trả về NO và chỉ mục không tăng.

Mã trong ví dụ đầu tiên là quét "(123) 456-7890" cho các ký tự thập phân, đã bị lỗi từ ký tự đầu tiên, do đó, lệnh gọi để quétChar characterFromset: inString: để lại một mình bị tước đi và trả về NO; Mã hoàn toàn bỏ qua việc kiểm tra giá trị trả về, không để lại chuỗi bị tước. Ngay cả khi ký tự đầu tiên là một chữ số, mã đó sẽ thất bại, vì nó sẽ chỉ trả về các chữ số mà nó tìm thấy cho đến dấu gạch đầu tiên hoặc paren hoặc bất cứ thứ gì.

Nếu bạn thực sự muốn sử dụng NSScanner, bạn có thể đặt một cái gì đó tương tự vào một vòng lặp và tiếp tục kiểm tra giá trị trả về KHÔNG, và nếu bạn nhận được rằng bạn có thể tăng quétLocation và quét lại; và bạn cũng phải kiểm tra isAtEnd và yada yada yada. Tóm lại, sai công cụ cho công việc. Giải pháp của Michael là tốt hơn.


0

Đối với những người tìm kiếm trích xuất điện thoại, bạn có thể trích xuất các số điện thoại từ một văn bản bằng NSDataDetector, ví dụ:

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

Tôi đã tạo một danh mục trên NSString để đơn giản hóa thao tác phổ biến này.

NSString + AllowChar characterInset.h

@interface NSString (AllowCharactersInSet)

- (NSString *)stringByAllowingOnlyCharactersInSet:(NSCharacterSet *)characterSet;

@end

NSString + AllowChar characterInset.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

Tôi nghĩ cách tốt nhất hiện nay là:

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

0

Nếu bạn chỉ muốn lấy các số từ chuỗi, bạn chắc chắn có thể sử dụng các biểu thức thông thường để phân tích chúng. Để thực hiện regex trong Objective-C, hãy xem RegexKit . Chỉnh sửa: Như @Nathan chỉ ra, sử dụng NSScanner là cách đơn giản hơn nhiều để phân tích tất cả các số từ một chuỗi. Tôi hoàn toàn không biết về lựa chọn đó, vì vậy các đạo cụ cho anh ấy đã gợi ý nó. (Tôi thậm chí không thích sử dụng regex cho mình, vì vậy tôi thích các phương pháp không yêu cầu chúng.)

Nếu bạn muốn định dạng số điện thoại để hiển thị, đáng để xem qua NSNumberFormatter . Tôi đề nghị bạn đọc qua câu hỏi SO liên quan này để biết các mẹo làm như vậy. Hãy nhớ rằng các số điện thoại được định dạng khác nhau tùy thuộc vào vị trí và / hoặc miền địa phương.


Ôi những giờ phút đau đớn tôi đã dành để phát triển các trình định dạng và phân tích số điện thoại tốt. Các chuỗi được liên kết là một khởi đầu tốt, nhưng trường hợp chung về định dạng số điện thoại toàn cầu để hiển thị là một con đường dài và như đã lưu ý trong các chuỗi được liên kết, Apple không cấp cho bạn quyền truy cập vào các trình định dạng số điện thoại trong Sổ địa chỉ và rất không nhất quán về cách số điện thoại được trình bày từ API Sổ địa chỉ. Điều duy nhất khó hơn định dạng một số điện thoại để hiển thị là xác định xem hai số điện thoại có bằng nhau không. Ít nhất câu hỏi của OP là vấn đề dễ nhất.
Rob Napier

Tôi tin rằng các liên kết đến định dạng số điện thoại là sai lệch trừ khi bạn hài lòng với cách triển khai nguyên thủy, tập trung vào Hoa Kỳ. Thay vì một công cụ định dạng số điện thoại được bản địa hóa phù hợp từ Apple, cách duy nhất để thực hiện việc này là sao chép các mẫu định dạng khỏi thiết bị (UIPhoneFormats.plist trong OS 2.x) và tự sao chép các mẫu tùy thuộc vào địa điểm của người dùng. Đây là một nhiệm vụ không hề nhỏ.
Nathan de Vries

Đó là lý do tại sao tôi đề cập đến việc bản địa hóa các trình định dạng số. Tôi đã không giả vờ đăng bất kỳ hình thức giải pháp hoàn chỉnh nào cho điều đó - đó là một cuộc thảo luận dài hơn nhiều và sẽ có ý nghĩa hơn để biến nó thành một câu hỏi SO riêng biệt.
Quinn Taylor

0

Swift 5

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

-1

Dựa trên câu trả lời của Jon Vogel ở đây, đây là phần mở rộng Chuỗi Swift cùng với một số thử nghiệm cơ bản.

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

Và một số thử nghiệm chứng minh ít nhất chức năng cơ bản:

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

Điều này trả lời câu hỏi của OP nhưng có thể dễ dàng sửa đổi để để lại các ký tự liên quan đến số điện thoại như ",; * # +"


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

];

Giữ cho nó đơn giản!


3
điều này sẽ chỉ cắt những ký tự đó từ đầu và cuối.
raidfive
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.