Używanie wyrażeń regularnych do wyodrębniania wartości w Javie


169

Mam kilka ciągów w szorstkiej formie:

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

Chcę wyodrębnić tekst w [pewnej liczbie] za pomocą klas Java Regex.

Wiem mniej więcej, jakiego wyrażenia regularnego chcę użyć (chociaż wszystkie sugestie są mile widziane). To, co mnie naprawdę interesuje, to wywołania języka Java, które pobierają ciąg wyrażenia regularnego i używają go w danych źródłowych w celu uzyskania wartości [pewna liczba].

EDYCJA: Dodam, że interesuje mnie tylko jeden [jakiś numer] (w zasadzie pierwsza instancja). Ciągi źródłowe są krótkie i nie będę szukał wielokrotnych wystąpień [jakaś liczba].


11
... a teraz idę na badania. Zobaczmy, czy SO może uzyskać dla mnie odpowiedź, zanim sam to wymyślę. :-P
Craig Walker,

to były pytania na rozmowę kwalifikacyjną w firmie bankowej / inwestycyjnej / handlowej dotyczącej inżynierii oprogramowania, prawda? : P
ennth

@ennth Nie, nawet nie blisko! Był przeznaczony do produkcji kodu na małej stronie internetowej ... wiele księżyców temu.
Craig Walker

1
cholernie dobrze, zadano mi prawie to samo dokładne pytanie na egzaminie z kodowania JP Morgan Chase Software Engineering zaledwie kilka dni temu: P
ennth

Odpowiedzi:


316

Pełny przykład:

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

Ponieważ szukasz pierwszej liczby, możesz użyć takiego wyrażenia regularnego:

^\D+(\d+).*

i m.group(1)zwróci Ci pierwszy numer. Pamiętaj, że liczby ze znakiem mogą zawierać znak minus:

^\D+(-?\d+).*

62
Nie zapomnij ponownie użyć obiektu Patter. Kompilowanie wzorca zajmuje dużo czasu.
Rastislav Komara

14
Zgoda. Zwykle definiowałbym wzorzec jako prywatny statyczny końcowy wzorzec PATTERN = Pattern.compile ("..."); Ale to tylko ja.
Allain Lalonde

6
możemy po prostu użyć Pattern p = Pattern.compile ("\\ d +");
javaMan

15
Bez wyjaśnienia to zła odpowiedź.
Martin Spamer

Możesz również ponownie użyć dopasowującego. Wywołaj metodę reset () Matchera między każdym użyciem. Jeśli udostępniasz element dopasowujący w wielu współbieżnych wątkach, zsynchronizuj operację.
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());
        }
    }
}

Wynik:

1234
789
2345

Pytanie konkretnie dotyczy tylko PIERWSZEGO wystąpienia liczb.
NoBrainer

34

Allain ma w zasadzie kod java, więc możesz tego użyć. Jednak jego wyrażenie pasuje tylko wtedy, gdy twoje liczby są poprzedzone tylko strumieniem znaków słownych.

"(\\d+)"

powinien być w stanie znaleźć pierwszy ciąg cyfr. Nie musisz określać, co jest przed nim, jeśli masz pewność, że będzie to pierwszy ciąg cyfr. Podobnie nie ma sensu określać, co jest po nim, chyba że tego chcesz. Jeśli chcesz tylko numer i jesteś pewien, że będzie to pierwszy ciąg jednej lub więcej cyfr, to wszystko, czego potrzebujesz.

Jeśli spodziewasz się, że zostanie on przesunięty spacjami, określenie będzie jeszcze wyraźniejsze

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

może być lepiej.

Jeśli potrzebujesz wszystkich trzech części, wystarczy:

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

EDYCJA Wyrażenia podane przez Allaina i Jacka sugerują, że musisz określić jakiś podzbiór niecyfrowych, aby przechwycić cyfry . Jeśli powiesz silnikowi wyrażeń regularnych, którego szukasz \d, zignoruje on wszystko przed cyframi. Jeśli wyrażenie J lub A pasuje do twojego wzorca, to całe dopasowanie równa się ciągowi wejściowemu . I nie ma powodu, aby to określać. Prawdopodobnie spowalnia czysty mecz, jeśli nie jest całkowicie ignorowany.


możesz przetestować hipotezę Axemansa, uruchamiając przykładowy test i sprawdzając wydajność jego rozwiązania w porównaniu z A / J.
anjanb

Nie musisz określać początku i końca łańcucha. W przeciwnym razie elementy takie jak 124xxx123xxx byłyby dopasowane, mimo że nie pasują do jego składni? Czy może ^ i $ są ukryte?
Allain Lalonde

Allain, twoje też by się nie udało. Ty i Jack zakładacie, że cyfry będą poprzedzać znaki niecyfrowe. Albo robią, albo nie. W takim przypadku żadne z tych wyrażeń nie przeanalizuje tego wiersza. Powtarzam to zgodnie z opisem , wystarczy wzór cyfr.
Axeman

11

Oprócz Pattern , klasa String w języku Java ma również kilka metod, które mogą współpracować z wyrażeniami regularnymi, w Twoim przypadku kod będzie wyglądał następująco:

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

gdzie \\Djest znakiem niecyfrowym.


10

W Javie 1.4 i nowszych:

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);
}

8

Ta funkcja zbiera wszystkie pasujące sekwencje z łańcucha. W tym przykładzie pobiera wszystkie adresy e-mail z ciągu.

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;
}

Za message = "adf@gmail.com, <another@osiem.osiem>>>> lalala@aaa.pl"to utworzy Listę 3 elementów.


3

Spróbuj zrobić coś takiego:

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

if (m.find()) {
    System.out.println(m.group(1));
}

3
-1. Ponieważ .+łapczywie pochłania postacie, \d+przechwytuje tylko "3"from "123". Ponadto, wewnątrz literałów łańcuchowych, musisz uciec przed ukośnikiem odwrotnym (Twój przykład się nie skompiluje).
Bart Kiers,

3

Proste rozwiązanie

// 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
}

Rozwiązanie w klasie użytkowej

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);

1

Spójrz, możesz to zrobić za pomocą StringTokenizera

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);
}

Ponieważ bierzemy te dane liczbowe do trzech różnych zmiennych, możemy użyć tych danych w dowolnym miejscu kodu (do dalszego wykorzystania)


0

A może [^\\d]*([0-9]+[\\s]*[.,]{0,1}[\\s]*[0-9]*).*myślę, że zajmie się liczbami z częścią ułamkową. Dołączyłem spacje i ,jako możliwy separator. Próbuję wyciągnąć liczby z ciągu, w tym liczby zmiennoprzecinkowe, i biorąc pod uwagę, że użytkownik może popełnić błąd i uwzględnić spacje podczas wpisywania liczby.


0

Czasami można użyć prostej metody .split („REGEXP”) dostępnej w java.lang.String. Na przykład:

String input = "first,second,third";

//To retrieve 'first' 
input.split(",")[0] 
//second
input.split(",")[1]
//third
input.split(",")[2]

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);
}

1
Edytuj, podając więcej informacji. Odpowiedzi zawierające tylko kod i „wypróbuj to” są odradzane, ponieważ nie zawierają treści, które można przeszukiwać, i nie wyjaśniają, dlaczego ktoś powinien „spróbować tego”. Dokładamy wszelkich starań, aby być źródłem wiedzy.
Brian Tompsett - 汤 莱恩

1
Głosuj przeciw po prostu za powtarzanie poprawnych odpowiedzi, które zostały udzielone dawno temu bez dodawania żadnej dodatkowej wartości
Forage

-1

jeśli czytasz z pliku, to może ci pomóc

              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());
         }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.