Remova todos os números, exceto o NSString


157

Eu tenho um NSString (número de telefone) com alguns parênteses e hífens, pois alguns números de telefone são formatados. Como removeria todos os caracteres, exceto números, da string?

Respostas:


375

Pergunta antiga, mas que tal:

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

Explode a string de origem no conjunto de não-dígitos e, em seguida, remonta-os usando um separador de string vazio. Não é tão eficiente quanto escolher caracteres, mas é muito mais compacto no código.


6
Obrigado! Para outros iniciantes, você pode criar seu próprio costume NSCharacterSet fazendoNSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"charactersGoHere"]
guptron

1
Muito obrigado ! Só por curiosidade, você tem uma idéia de por que NSString *pureNumbers = [pureNumbers stringByTrimmingCharactersInSet: [NSCharacterSet decimalDigitCharacterSet] invertedSet]não está funcionando?
Thomas Besnehard

1
@Tommecpe stringByTrimmingCharactersInSet remove apenas do início e do fim da sequência, para que não afete após o primeiro caractere não correspondente ou antes do último caractere não correspondente.
simonobo

quero manter apenas números e alfabeto, como posso fazer isso?
Jacky

1
@ Jacky no exemplo acima, você substituiria [NSCharacterSet decimalDigitCharacterSet]por outro que contenha apenas números e letras. Você pode construir um, criando um NSMutableCharaterSete passar um decimalDigitCharacterSet, uppercaseLetterCharacterSete lowercaseLetterCharacterSetpara formUnionWithCharacterSet:. Observe que também letterCharacterSetinclui marcas, portanto, o uso de versões em maiúsculas e minúsculas.
kadam

75

Não é necessário usar uma biblioteca de expressões regulares, como as outras respostas sugerem - a classe que você procura é chamada NSScanner. É usado da seguinte maneira:

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"

EDITAR: Eu atualizei o código porque o original foi escrito em cima da minha cabeça e achei que seria suficiente apontar as pessoas na direção certa. Parece que as pessoas estão atrás do código e podem simplesmente copiar e colar diretamente no aplicativo.

Também concordo que a solução de Michael Pelz-Sherman é mais apropriada do que usar NSScanner, portanto, você pode dar uma olhada nisso.


+1 Boa resposta que aborda diretamente a pergunta. Editei minha resposta para defender essa abordagem, mas estou deixando a segunda metade como está, pois ainda é útil), que resolve o problema de formatar um número de telefone para exibição. (Em seguida, você pode deixar um comentário construtivo quando downvoting, se apenas para os leitores posteriores?)
Quinn Taylor

4
Talvez seja útil saber que existe um método NSCharacterSet + decimalDigitCharacterSet que fornecerá todos os dígitos decimais. Isso é um pouco diferente do que Nathan define, porque inclui todos os símbolos que representam números decimais, incluindo, por exemplo, dígitos em árabe-índico (١٢٣٤٥ etc). Dependendo do seu aplicativo, isso pode ocasionalmente ser um problema, mas geralmente é bom ou neutro e um pouco mais curto para digitar.
Rob Napier

Tenho certeza de que essa resposta não funciona e não é a abordagem correta para o problema. Se você realmente tentar o código como mostrado (primeiro adicionando um @ antes do primeiro parâmetro ao NSLog, tornando-o uma string objc), verá que ele imprime <null> ou trava. Por quê? Veja minha resposta abaixo.
22139 Jack Nutting

Não há necessidade de outra resposta - é para isso que servem os comentários. Atualizei a solução, incluindo uma referência à solução de Michael Pelz-Sherman.
197 de Nathan Vries

4
Isso é muito complicado.
ryyst

63

A resposta aceita é um exagero para o que está sendo perguntado. Isso é muito mais simples:

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

2
A resposta (atualmente) aceita é mais ou menos idêntica a esta, mas foi postada 13 meses antes.
Calebe

No momento em que respondi, não tinha essa resposta. Embora pareça que a resposta atual já tenha sido proposta e eu a perdi: web.archive.org/web/20101115214033/http://stackoverflow.com/…
Yacine Filali

30

Isso é ótimo, mas o código não funciona para mim no iPhone 3.0 SDK.

Se eu definir strippedString como você mostra aqui, recebo um BAD ACCESS errorao tentar imprimi-lo após a scanCharactersFromSet:intoStringchamada.

Se eu fizer assim:

NSMutableString *strippedString = [NSMutableString stringWithCapacity:10];

Acabo com uma string vazia, mas o código não falha.

Eu tive que recorrer ao bom e velho C:

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

Estou executando o 3.0 e isso funciona para mim. A resposta mais popular de Vries não funcionou.
Neo42

A resposta número um não funcionará para mim. O scanner para quando chega a () ou - Esta resposta funciona muito bem !! C bom e velho !! Obrigado
Jeff

2
Observe apenas que o caractere '+' deve ser permitido para o número de telefone.
Prcela

27

Embora essa seja uma pergunta antiga com respostas funcionais, perdi o suporte ao formato internacional . Com base na solução de simonobo, o conjunto de caracteres alterado inclui um sinal de mais "+". Os números de telefone internacionais também são suportados por esta alteração.

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

As expressões Swift são

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

Que gera +12345671000 como um formato de número de telefone internacional comum.


2
Esta é a melhor solução da lista, especialmente se você precisar do sinal de mais para números de telefone internacionais.
UXUiOS

Por alguma razão, o uso de um conjunto de caracteres invertido me assusta tanto quanto o desempenho. Alguém sabe se esse é um medo infundado?
precisa saber é o seguinte

Este funcionou! Você poderia explicar o seu funcionamento? @alex
Jayprakash Dubey,

11

Aqui está a versão Swift disso.

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

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

11

Versão rápida da resposta mais popular:

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

Edit: Sintaxe para Swift 2

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

Edit: Sintaxe para Swift 3

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

existe uma maneira de manter o símbolo decimal separado? O ponto (ou vírgula) como uma função das configurações padrão do dispositivo? Sua solução de eliminar tudo mas os números
Nicholas

5

Obrigado pelo exemplo. Falta apenas uma coisa para o incremento do scanLocation, caso um dos caracteres em originalString não seja encontrado dentro do objeto CharacterSet de números. Eu adicionei uma instrução else {} para corrigir isso.

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

Aceita apenas número de celular

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

3

Pode valer a pena notar que a resposta aceita componentsSeparatedByCharactersInSet:e componentsJoinedByString:baseada não é uma solução eficiente em memória. Aloca memória para o conjunto de caracteres, para uma matriz e para uma nova sequência. Mesmo que sejam apenas alocações temporárias, o processamento de muitas seqüências dessa maneira pode preencher rapidamente a memória.

Uma abordagem mais amigável à memória seria operar uma cópia mutável da string em vigor. Em uma categoria sobre 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];
}

A criação de perfil das duas abordagens mostrou isso usando cerca de 2/3 a menos de memória.


2

Você pode usar expressão regular em sequência mutável:

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

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

1

Construiu a solução principal como uma categoria para ajudar com problemas mais amplos:

Interface:

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

Implementação:

@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

Uso:

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

1

Swift 3

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

Isso irá aparar apenas caracteres sem dígitos desde o início e o final.
Shebuka 13/03/19

1

swift 4.1

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

0

Hum. A primeira resposta parece totalmente errada para mim. O NSScanner é realmente destinado à análise. Ao contrário do regex, ele faz com que você analise a string, um pedaço minúsculo de cada vez. Você o inicializa com uma sequência e mantém um índice de quanto tempo ela foi obtida; Esse índice é sempre o seu ponto de referência, e quaisquer comandos que você der são relativos a esse ponto. Você diz: "ok, me dê o próximo pedaço de caracteres deste conjunto" ou "me dê o número inteiro encontrado na string", e eles começam no índice atual e avançam até encontrar algo que não Combine. Se o primeiro caractere já não corresponder, o método retornará NO e o índice não será incrementado.

O código no primeiro exemplo está varrendo "(123) 456-7890" quanto a caracteres decimais, que já falham desde o primeiro caractere; portanto, a chamada para scanCharactersFromSet: intoString: deixa o strippedString transmitido sozinho e retorna NO; O código ignora totalmente a verificação do valor de retorno, deixando a strippedString não atribuída. Mesmo se o primeiro caractere fosse um dígito, esse código falharia, pois retornaria apenas os dígitos encontrados até o primeiro traço ou parêntese ou o que quer.

Se você realmente quisesse usar o NSScanner, poderia colocar algo assim em um loop e continuar verificando o valor de retorno NO; se conseguir, poderá incrementar o scanLocation e verificar novamente; e você também precisa verificar isAtEnd e yada yada yada. Em suma, ferramenta errada para o trabalho. A solução de Michael é melhor.


0

Para quem procura extração de telefone, é possível extrair os números de telefone de um texto usando o NSDataDetector, por exemplo:

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

Eu criei uma categoria no NSString para simplificar essa operação comum.

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

Eu acho que atualmente a melhor maneira é:

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

0

Se você deseja apenas pegar os números da sequência, certamente pode usar expressões regulares para analisá-los. Para fazer regex no Objective-C, confira RegexKit . Editar: Como o @Nathan aponta, usar o NSScanner é uma maneira muito mais simples de analisar todos os números de uma string. Eu não estava totalmente ciente dessa opção, então adereços para ele por sugeri-la. (Eu nem gosto de usar regex, então prefiro abordagens que não as exijam.)

Se você deseja formatar números de telefone para exibição, vale a pena dar uma olhada no NSNumberFormatter . Sugiro que você leia esta pergunta relacionada ao SO para obter dicas sobre como fazê-lo. Lembre-se de que os números de telefone são formatados de maneira diferente, dependendo do local e / ou local.


Oh, as horas dolorosas que passei desenvolvendo bons formatadores e analisadores de números de telefone. Os encadeamentos vinculados são um bom começo, mas o caso geral de formatar números de telefone globais para exibição é um longo caminho e, conforme observado nos encadeamentos vinculados, a Apple não fornece acesso aos formatadores de números de telefone do Catálogo de Endereços e muito inconsistente em como os números de telefone são apresentados na API do Catálogo de endereços. A única coisa mais difícil do que formatar um número de telefone para exibição é determinar se dois números de telefone são iguais. Pelo menos a pergunta do OP é o mais fácil dos problemas.
9139 Rob Napier

Acredito que esses links para formatar números de telefone sejam enganosos, a menos que você esteja satisfeito com uma implementação primitiva centrada nos EUA. Em vez de um formatador de número de telefone localizado apropriado da Apple, a única maneira de fazer isso corretamente é copiar os modelos de formatação do dispositivo (UIPhoneFormats.plist no OS 2.x) e reproduzir os modelos você mesmo, dependendo da localidade dos usuários. Esta é uma tarefa não trivial.
197 de Nathan Nathan Vries

Por isso mencionei a localização dos formatadores de números. Não pretendi postar nenhuma forma de solução completa para isso - é uma discussão muito mais longa e faria mais sentido fazer dela uma pergunta SO separada.
Quinn Taylor

0

Swift 5

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

-1

Com base na resposta de Jon Vogel, aqui é como uma extensão Swift String, juntamente com alguns testes básicos.

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

E alguns testes comprovando pelo menos a funcionalidade básica:

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

Isso responde à pergunta do OP, mas poderia ser facilmente modificado para deixar caracteres relacionados ao número de telefone como ",; * # +"


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

];

Mantenha simples!


3
isso apenas aparará esses caracteres do começo e do fim.
raidfive
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.