web-dev-qa-db-ja.com

NSStringから数字以外のすべてを削除します

一部の電話番号がフォーマットされているため、NSString(電話番号)に括弧とハイフンが付いています。文字列から数字以外のすべての文字を削除するにはどうすればよいですか?

157
Ben Harris

古い質問ですが、どうですか:

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

数字以外のセットでソース文字列を展開し、空の文字列セパレータを使用してそれらを再構築します。文字を選択するほど効率的ではありませんが、コードははるかにコンパクトです。

374
simonobo

他の答えが示唆するように、正規表現ライブラリを使用する必要はありません-後のクラスは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"

編集:オリジナルを頭の上から書いたのでコードを更新しました。人々を正しい方向に向けるのに十分だと思いました。人々は自分のアプリケーションにそのままコピーアンドペーストできるコードを求めているようです。

また、Michael Pelz-ShermanのソリューションはNSScannerを使用するよりも適切であることに同意します。

75
Nathan de Vries

受け入れられた答えは、求められているものに対してはやり過ぎです。これははるかに簡単です:

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

これはすばらしいことですが、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]];
    }
}
30

これは実用的な答えのある古い質問ですが、私は国際形式のサポートを逃しました。シモノボのソリューションに基づいて、変更された文字セットにはプラス記号「+」が含まれます。国際電話番号もこの修正によりサポートされます。

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が生成されます。

27
alex

これが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: "")
11
Jon Vogel

例に感謝します。 originalStringの文字の1つが数字のCharacterSetオブジェクト内で見つからない場合に備えて、scanLocationの増分が欠落していることが1つだけあります。これを修正するために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"
5
GregoryN

携帯電話番号のみ受け入れます

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

受け入れられている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つのアプローチをプロファイリングすると、メモリ使用量が約2/3少なくなります。

3
kadam

可変文字列で正規表現を使用できます:

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

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

より広範な問題を支援するカテゴリとしてトップソリューションを構築しました。

インタフェース:

@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
BadPirate

スイフト3

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

Swift 4.1

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

文字列から数字を取得するだけの場合、couldは確かに正規表現を使用してそれらを解析します。 Objective-Cで正規表現を実行するには、 RegexKit を確認してください。 編集:@ Nathanが指摘しているように、NSScannerを使用すると、文字列からすべての数値を解析するはるかに簡単な方法です。私は完全にそのオプションを知らなかったので、提案するために彼に小道具を。 (私は自分で正規表現を使用することさえ好きではないので、それらを必要としないアプローチを好みます。)

電話番号を表示用にフォーマットする場合は、 NSNumberFormatter をご覧ください。 これに関連するSO質問 を読み、ヒントを読むことをお勧めします。電話番号の形式は、場所やロケールに応じて異なることに注意してください。

0
Quinn Taylor

電話の抽出を検索する場合は、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
Rafael Sanches

あの最初の答えは私にはまったく間違っているようです。 NSScannerは、本当に構文解析を目的としています。正規表現とは異なり、一度に1つの小さなチャンクで文字列を解析します。文字列で初期化し、文字列に沿ってどれだけ取得したかのインデックスを維持します。そのインデックスは常にその参照ポイントであり、指定するコマンドはそのポイントに関連しています。 「OK、このセットの次の文字の塊を教えて」または「文字列で見つかった整数を教えて」と言うと、それらは現在のインデックスから始まり、見つからないものが見つかるまで前進します一致。最初の文字がすでに一致していない場合、メソッドはNOを返し、インデックスは増加しません。

最初の例のコードは、 "(123)456-7890"の10進文字をスキャンしていますが、これは最初の文字で既に失敗しているため、scanCharactersFromSet:intoString:の呼び出しは、渡されたstrippedStringをそのままにし、NOを返します。コードは戻り値のチェックを完全に無視し、stripedStringを未割り当てのままにします。最初の文字が数字であったとしても、最初のダッシュまたは括弧またはそれ以外のものまで見つかった数字のみを返すため、そのコードは失敗します。

NSScannerを本当に使用したい場合は、そのようなものをループに入れ、NO戻り値をチェックし続けることができます。それが得られたら、scanLocationをインクリメントして再度スキャンできます。また、isAtEnd、およびyada yada yadaも確認する必要があります。要するに、仕事のための間違ったツール。マイケルのソリューションの方が優れています。

0
Jack Nutting

この一般的な操作を簡単にするために、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
Ryan

スイフト5

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

私は現在、最善の方法は次のとおりだと思います:

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