Usando expressões regulares para extrair um valor em Java

169

Eu tenho várias strings na forma bruta:

[some text] [some number] [some more text]

Eu quero extrair o texto em [algum número] usando as classes Java Regex.

Eu sei aproximadamente que expressão regular eu quero usar (embora todas as sugestões sejam bem-vindas). O que realmente me interessa são as chamadas Java para pegar a sequência regex e usá-la nos dados de origem para produzir o valor de [some number].

EDIT: Devo acrescentar que só estou interessado em um único [algum número] (basicamente, a primeira instância). As strings de origem são curtas e não vou procurar várias ocorrências de [algum número].

Craig Walker
fonte
11
... e agora vou pesquisar. Vamos ver se o SO pode obter uma resposta para mim antes que eu descubra. :-P
Craig Walker
essa foi uma entrevista em uma empresa bancária / de investimento / comercial para engenharia de software, não foi? : P
ennth 8/06
@ennth Não, nem mesmo perto! Era para código de produção em um site de pequenas empresas ... muitas luas atrás.
Craig Walker
1
malditamente bem, me fizeram a mesma pergunta exata em um exame de codificação da JP Morgan Chase Software Engineering há apenas alguns dias: P
ennth

Respostas:

316

Exemplo completo:

private static final Pattern p = Pattern.compile("^([a-zA-Z]+)([0-9]+)(.*)");
public static void main(String[] args) {
    // create matcher for pattern p and given string
    Matcher m = p.matcher("Testing123Testing");

    // if an occurrence if a pattern was found in a given string...
    if (m.find()) {
        // ...then you can use group() methods.
        System.out.println(m.group(0)); // whole matched expression
        System.out.println(m.group(1)); // first expression from round brackets (Testing)
        System.out.println(m.group(2)); // second one (123)
        System.out.println(m.group(3)); // third one (Testing)
    }
}

Como você está procurando o primeiro número, pode usar esse regexp:

^\D+(\d+).*

e m.group(1)retornará o primeiro número. Observe que os números assinados podem conter um sinal de menos:

^\D+(-?\d+).*
Allain Lalonde
fonte
62
Não se esqueça de reutilizar o objeto Patter. A compilação de padrões leva uma quantidade enorme de tempo.
Rastislav Komara 26/10/08
14
Acordado. Normalmente, eu definiria o padrão como um padrão estático privado final PATTERN = Pattern.compile ("..."); Mas sou só eu.
Allain Lalonde
6
podemos simplesmente usar Pattern p = Pattern.compile ("\\ d +");
Javaman
15
Sem explicação, essa é uma resposta ruim.
Martin Spamer 24/05
Você também pode reutilizar o Matcher. Chame o método reset () do Matcher entre cada uso. Se você estiver compartilhando o correspondente entre vários threads simultâneos, sincronize a operação.
Marquez
41
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex1 {
    public static void main(String[]args) {
        Pattern p = Pattern.compile("\\d+");
        Matcher m = p.matcher("hello1234goodboy789very2345");
        while(m.find()) {
            System.out.println(m.group());
        }
    }
}

Resultado:

1234
789
2345
javaMan
fonte
A pergunta pede especificamente apenas a PRIMEIRA ocorrência de números.
NoBrainer
34

Allain basicamente tem o código java, então você pode usá-lo. No entanto, a expressão dele corresponde apenas se seus números forem precedidos apenas por um fluxo de caracteres de palavras.

"(\\d+)"

deve ser capaz de encontrar a primeira sequência de dígitos. Você não precisa especificar o que está antes, se tiver certeza de que será a primeira sequência de dígitos. Da mesma forma, não há como especificar o que está depois, a menos que você queira. Se você deseja apenas o número e tem certeza de que será a primeira sequência de um ou mais dígitos, é tudo o que precisa.

Se você espera que seja compensado por espaços, tornará ainda mais distinto especificar

"\\s+(\\d+)\\s+"

pode ser melhor.

Se você precisar das três partes, isso fará:

"(\\D+)(\\d+)(.*)"

EDITAR As expressões dadas por Allain e Jack sugerem que você precisa especificar algum subconjunto de não-dígitos para capturar dígitos . Se você informar o mecanismo regex que está procurando \d, ele ignorará tudo antes dos dígitos. Se a expressão de J ou A se encaixa no seu padrão, a correspondência inteira é igual à sequência de entrada . E não há razão para especificá-lo. Provavelmente retarda uma partida limpa, se não for totalmente ignorada.

Axeman
fonte
você pode testar a hipótese de Axemans executando um teste de amostra e verificando o desempenho de sua solução vs. A / J.
Anjanb 26/10/08
Você não precisa especificar o início e o fim da string. Caso contrário, coisas como 124xxx123xxx seriam correspondidas mesmo que não se encaixem na sintaxe dele? Ou ^ e $ estão implícitos?
Allain Lalonde
Allain, o seu também falharia. Você e Jack supõem que caracteres que não sejam dígitos precedem os dígitos. Eles fazem ou não. Nesse caso, nenhuma dessas expressões analisará essa linha. Repito que, conforme especificado , o padrão para os dígitos é suficiente.
Axeman
11

Além do Pattern , a classe Java String também possui vários métodos que podem trabalhar com expressões regulares; no seu caso, o código será:

"ab123abc".replaceFirst("\\D*(\\d*).*", "$1")

onde \\Dé um caractere sem dígito.

Vitalii Fedorenko
fonte
10

No Java 1.4 e superior:

String input = "...";
Matcher matcher = Pattern.compile("[^0-9]+([0-9]+)[^0-9]+").matcher(input);
if (matcher.find()) {
    String someNumberStr = matcher.group(1);
    // if you need this to be an int:
    int someNumberInt = Integer.parseInt(someNumberStr);
}
Jack Leow
fonte
8

Esta função coleta todas as seqüências correspondentes da string. Neste exemplo, todos os endereços de email são retirados da string.

static final String EMAIL_PATTERN = "[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
        + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})";

public List<String> getAllEmails(String message) {      
    List<String> result = null;
    Matcher matcher = Pattern.compile(EMAIL_PATTERN).matcher(message);

    if (matcher.find()) {
        result = new ArrayList<String>();
        result.add(matcher.group());

        while (matcher.find()) {
            result.add(matcher.group());
        }
    }

    return result;
}

Para message = "[email protected], <[email protected]>>>> [email protected]"isso irá criar lista de 3 elementos.

LukaszTaraszka
fonte
3

Tente fazer algo assim:

Pattern p = Pattern.compile("^.+(\\d+).+");
Matcher m = p.matcher("Testing123Testing");

if (m.find()) {
    System.out.println(m.group(1));
}
Tint Naing Win
fonte
3
-1. Como .+avidamente consome caracteres, \d+apenas captura o "3"de "123". Além disso, dentro de literais de string, você precisa escapar da barra invertida (seu exemplo não será compilado).
Bart Kiers
3

Solução Simples

// Regexplanation:
// ^       beginning of line
// \\D+    1+ non-digit characters
// (\\d+)  1+ digit characters in a capture group
// .*      0+ any character
String regexStr = "^\\D+(\\d+).*";

// Compile the regex String into a Pattern
Pattern p = Pattern.compile(regexStr);

// Create a matcher with the input String
Matcher m = p.matcher(inputStr);

// If we find a match
if (m.find()) {
    // Get the String from the first capture group
    String someDigits = m.group(1);
    // ...do something with someDigits
}

Solução em uma classe Util

public class MyUtil {
    private static Pattern pattern = Pattern.compile("^\\D+(\\d+).*");
    private static Matcher matcher = pattern.matcher("");

    // Assumptions: inputStr is a non-null String
    public static String extractFirstNumber(String inputStr){
        // Reset the matcher with a new input String
        matcher.reset(inputStr);

        // Check if there's a match
        if(matcher.find()){
            // Return the number (in the first capture group)
            return matcher.group(1);
        }else{
            // Return some default value, if there is no match
            return null;
        }
    }
}

...

// Use the util function and print out the result
String firstNum = MyUtil.extractFirstNumber("Testing4234Things");
System.out.println(firstNum);
Acéfalo
fonte
1

Olha, você pode fazê-lo usando StringTokenizer

String str = "as:"+123+"as:"+234+"as:"+345;
StringTokenizer st = new StringTokenizer(str,"as:");

while(st.hasMoreTokens())
{
  String k = st.nextToken();    // you will get first numeric data i.e 123
  int kk = Integer.parseInt(k);
  System.out.println("k string token in integer        " + kk);

  String k1 = st.nextToken();   //  you will get second numeric data i.e 234
  int kk1 = Integer.parseInt(k1);
  System.out.println("new string k1 token in integer   :" + kk1);

  String k2 = st.nextToken();   //  you will get third numeric data i.e 345
  int kk2 = Integer.parseInt(k2);
  System.out.println("k2 string token is in integer   : " + kk2);
}

Como estamos levando esses dados numéricos para três variáveis ​​diferentes, podemos usá-los em qualquer lugar do código (para uso posterior)

shounak
fonte
0

Que tal [^\\d]*([0-9]+[\\s]*[.,]{0,1}[\\s]*[0-9]*).*eu acho que iria cuidar de números com parte fracionária. Incluí espaços em branco e incluí ,como possível separador. Estou tentando obter os números de uma string, incluindo flutuadores, e levando em consideração que o usuário pode cometer um erro e incluir espaços em branco ao digitar o número.

arturo
fonte
0

Às vezes, você pode usar o método simples .split ("REGEXP") disponível em java.lang.String. Por exemplo:

String input = "first,second,third";

//To retrieve 'first' 
input.split(",")[0] 
//second
input.split(",")[1]
//third
input.split(",")[2]
user1722707
fonte
0
Pattern p = Pattern.compile("(\\D+)(\\d+)(.*)");
Matcher m = p.matcher("this is your number:1234 thank you");
if (m.find()) {
    String someNumberStr = m.group(2);
    int someNumberInt = Integer.parseInt(someNumberStr);
}
Mohammadreza Tavakoli
fonte
1
Edite com mais informações. As respostas somente código e "tente isso" são desencorajadas, porque não contêm conteúdo pesquisável e não explicam por que alguém deveria "tentar fazer isso". Nós fazemos um esforço aqui para ser um recurso para o conhecimento.
Brian Tompsett -
1
Downvote para apenas repetindo respostas corretas que foram dadas há muito tempo sem acrescentar qualquer valor adicional
forragem
-1

se você estiver lendo do arquivo, isso poderá ajudá-lo

              try{
             InputStream inputStream = (InputStream) mnpMainBean.getUploadedBulk().getInputStream();
             BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
             String line;
             //Ref:03
             while ((line = br.readLine()) != null) {
                if (line.matches("[A-Z],\\d,(\\d*,){2}(\\s*\\d*\\|\\d*:)+")) {
                     String[] splitRecord = line.split(",");
                     //do something
                 }
                 else{
                     br.close();
                     //error
                     return;
                 }
             }
                br.close();

             }
         }
         catch (IOException  ioExpception){
             logger.logDebug("Exception " + ioExpception.getStackTrace());
         }
buscador
fonte