Использование регулярных выражений для извлечения значения в Java

169

У меня есть несколько строк в грубой форме:

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

Я хочу извлечь текст в [некоторое количество], используя классы Java Regex.

Я примерно знаю, какое регулярное выражение я хочу использовать (хотя все предложения приветствуются). Что меня действительно интересует, так это вызовы Java, чтобы взять строку регулярного выражения и использовать ее в исходных данных для получения значения [some number].

РЕДАКТИРОВАТЬ: я должен добавить, что меня интересует только один [некоторый номер] (в основном, первый экземпляр). Исходные строки короткие, и я не собираюсь искать несколько вхождений [некоторого числа].

Крейг Уокер
источник
11
... и теперь я отправляюсь на исследование. Посмотрим, сможет ли SO получить для меня ответ, прежде чем я сам это выясню. :-P
Крейг Уокер
это были вопросы об интервью в банковской / инвестиционной / торговой компании по разработке программного обеспечения, не так ли? : P
11
@ennth Нет, даже близко! Это было для производственного кода на небольшом веб-сайте ... много лун назад.
Крейг Уокер
1
черт возьми, мне задали почти тот же самый вопрос на экзамене по программированию JP Morgan Chase Software Engineering всего несколько дней назад: P
en

Ответы:

316

Полный пример:

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

Поскольку вы ищете первый номер, вы можете использовать такое регулярное выражение:

^\D+(\d+).*

и m.group(1)вернет вам первый номер. Обратите внимание, что числа со знаком могут содержать знак минус:

^\D+(-?\d+).*
Аллен Лалонд
источник
62
Не забудьте повторно использовать объект Patter. Компиляция скороговорки занимает огромное количество времени.
Растислав Комара
14
Согласовано. Обычно я определяю шаблон как частный статический окончательный шаблон PATTERN = Pattern.compile ("..."); Но это только я.
Аллен Лалонд
6
мы можем просто использовать Pattern p = Pattern.compile ("\\ d +");
javaMan
15
Без объяснения это плохой ответ.
Мартин Спамер
Вы также можете повторно использовать Matcher. Вызывайте метод reset () Matcher между каждым использованием. Если вы используете совместное использование в нескольких параллельных потоках, вы должны синхронизировать операцию.
Маркес,
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());
        }
    }
}

Вывод:

1234
789
2345
javaMan
источник
Вопрос конкретно касается только ПЕРВОГО появления чисел.
NoBrainer
34

У Аллена в основном есть код Java, так что вы можете его использовать. Однако его выражение совпадает только в том случае, если вашим числам предшествует только поток слов.

"(\\d+)"

должен быть в состоянии найти первую строку цифр. Вам не нужно указывать, что перед ним, если вы уверены, что это будет первая строка цифр. Кроме того, нет смысла указывать, что после этого, если вы этого не хотите. Если вам просто нужен номер, и вы уверены, что это будет первая строка из одной или нескольких цифр, то это все, что вам нужно.

Если вы ожидаете, что он будет смещен пробелами, это сделает его еще более отчетливым

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

может быть лучше.

Если вам нужны все три части, это будет делать:

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

РЕДАКТИРОВАТЬ Выражения, данные Алленом и Джеком, предполагают, что вам нужно указать некоторое подмножество нецифровых чисел, чтобы захватить цифры . Если вы скажете движку регулярных выражений, что ищете, \dон будет игнорировать все до цифр. Если выражение J или А в приспосабливает свой образец, то весь матч равен в строке ввода . И нет никаких оснований указывать это. Это, вероятно, замедляет чистое совпадение, если не полностью игнорируется.

лесоруб
источник
Вы можете проверить гипотезу Аксеманса, выполнив пробный тест и проверив производительность его решения против A / J.
anjanb
Не нужно указывать начало и конец строки. В противном случае такие вещи, как 124xxx123xxx будут сопоставлены, даже если это не вписывается в его синтаксис? Или ^ и $ неявные?
Аллен Лалонд
Аллен, у тебя тоже не получится. Вы и Джек предполагаете, что цифры не должны начинаться с цифр. Они либо делают, либо нет. В этом случае ни одно из этих выражений не будет анализировать эту строку. Я повторяю, что, как указано , шаблон для цифр достаточно.
Axeman
11

В дополнение к Pattern класс Java String также имеет несколько методов, которые могут работать с регулярными выражениями, в вашем случае код будет:

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

где \\Dне цифра

Виталий Федоренко
источник
10

В Java 1.4 и выше:

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

Эта функция собирает все подходящие последовательности из строки. В этом примере он берет все адреса электронной почты из строки.

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

Для message = "adf@gmail.com, <another@osiem.osiem>>>> lalala@aaa.pl"этого создадим список из 3 элементов.

LukaszTaraszka
источник
3

Попробуйте сделать что-то вроде этого:

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

if (m.find()) {
    System.out.println(m.group(1));
}
Тинт Наинг Вин
источник
3
-1. Потому что .+жадно потребляет персонажей, \d+только захватывает "3"из "123". Кроме того, внутри строковых литералов вы должны экранировать обратную косую черту (ваш пример не скомпилируется).
Барт Киерс
3

Простое решение

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

Решение в классе 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);
NoBrainer
источник
1

Смотри, ты можешь сделать это используя 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);
}

Поскольку мы берем эти числовые данные в трех различных переменных, мы можем использовать эти данные в любом месте кода (для дальнейшего использования)

shounak
источник
0

Как насчет того, [^\\d]*([0-9]+[\\s]*[.,]{0,1}[\\s]*[0-9]*).*я думаю, что он позаботится о числах с дробной частью. Я включил пробелы и включил в ,качестве возможного разделителя. Я пытаюсь получить числа из строки, включая числа с плавающей запятой, и с учетом того, что пользователь может ошибиться и включить пробелы при наборе номера.

Артуро
источник
0

Иногда вы можете использовать простой метод .split ("REGEXP"), доступный в java.lang.String. Например:

String input = "first,second,third";

//To retrieve 'first' 
input.split(",")[0] 
//second
input.split(",")[1]
//third
input.split(",")[2]
user1722707
источник
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
Пожалуйста, отредактируйте с дополнительной информацией. Ответы «только код» и «попробуй это» не приветствуются, потому что они не содержат контента для поиска и не объясняют, почему кто-то должен «попробовать это». Мы прилагаем все усилия, чтобы быть источником знаний.
Брайан Томпсетт - 莱恩 莱恩
1
Понижение за просто повторение правильных ответов, которые давались давным-давно, без добавления дополнительной ценности
корм
-1

если вы читаете из файла, то это может помочь вам

              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());
         }
ищущий
источник