Регулярное выражение для соответствия целочисленному литералу

0

Вопрос

Я думал о разборе списка целых чисел (из строки свойства). Однако я хотел бы выйти за рамки только положительных и отрицательных десятичных значений и проанализировать любую строку, которая указывает на целочисленный литерал Java (JLS 17), как это можно найти в исходном коде. Аналогичным образом, я хотел бы быть снисходительным в отношении любых префиксов, разделителей и приложений вокруг самих целых чисел. Другими словами, я хочу найти их, используя повторные звонки вMatcher.find().

Существует ли регулярное выражение, которое соответствует всем возможным целочисленным литералам Java? Ему не нужно проверять верхнюю и нижнюю границы.


Несмотря на то, что я явно ссылался на JLS, я покажу некоторые действительные и недопустимые номера:

  • -1: в 1 соответствует, но минус-унарный оператор (я скорректирую, если необходимо)
  • 0x00_00_00_0F: значение пятнадцать сопоставляется в виде шестнадцатеричных цифр с подчеркиванием для разделения двух кусочков
  • 0b0000_1111: значение пятнадцать в двоичном формате соответствует
  • 017: восьмеричное значение пятнадцать соответствует
integer java literals regex
2021-11-23 21:48:28
3

Лучший ответ

4

Что-то вроде того:

десятичный:
(?:0|[1-9](?:_*[0-9])*)[lL]?

шестнадцатеричный:
0x[a-fA-F0-9](?:_*[a-fA-F0-9])*[lL]?

восьмеричный:
0[0-7](?:_*[0-7])*[lL]?

двоичный:
0[bB][01](?:_*[01])*[lL]?

Все вместе: (в режиме свободного пространства)

(?:
    0
    (?:
        x [a-fA-F0-9] (?: _* [a-fA-F0-9] )*
      |
        [0-7] (?: _* [0-7] )*
      |
        [bB] [01] (?: _* [01] )*
    )?
  |
    [1-9] (?: _* [0-9] )*
)
[lL]?

проверьте это

2021-11-23 22:47:19

Ах, да, это заставило бы меня проделать долгий путь. Однако допускает ли это несколько подчеркиваний? Может быть, это ? должно быть *?
Maarten Bodewes

@MaartenBodewes: Насколько я понимаю, в документе подчеркивания не должны быть смежными, но, возможно, я ошибаюсь? (другими словами, это 1____1 разрешено ?). Обратите внимание, что группа, внутри которой находится необязательное подчеркивание, в конечном итоге повторяется.
Casimir et Hippolyte

Ха, кто-нибудь может переписать это регулярное выражение? Мне показалось, что я смог его обновить (в тестовой версии все еще был ? вместо *)....
Maarten Bodewes

Еще раз спасибо, я опубликовал ответ, в котором также анализируется целое число с использованием синтаксиса регулярного выражения, основанного в духе вашего регулярного выражения.
Maarten Bodewes
0

После ответа Казимира я решил пойти немного дальше и реализовал некоторый код для фактического анализа целых чисел, который приведен ниже. Он включает символы минус и плюс, хотя официально они не являются частью целочисленного литерала, как описано в JLS; они являются унарными операторами.

package nl.owlstead.ifprops;

import java.math.BigInteger;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class JavaIntegerParser {
    private static final Pattern BINARY = Pattern.compile("(0b)([01](?:_*[01])*)(L?)", Pattern.CASE_INSENSITIVE);
    private static final Pattern OCTAL = Pattern.compile("(0)([0-7](?:_*[0-7])*)(L?)", Pattern.CASE_INSENSITIVE);
    private static final Pattern DECIMAL = Pattern.compile("()(0|(?:[1-9](?:_*[0-9])*))(L?)", Pattern.CASE_INSENSITIVE);
    private static final Pattern HEXADECIMAL = Pattern.compile("(0x)([0-9a-f](?:_*[0-9a-f])*)(L?)", Pattern.CASE_INSENSITIVE);
   
    // NOTE: OCTAL should be before DECIMAL if this is used to find the pattern
    private static final Pattern SIGNED_INTEGER_LITERAL = Pattern.compile(
            "(?:([+-])\\s*)?(" + 
            BINARY + "|" + OCTAL + "|" + DECIMAL + "|" + HEXADECIMAL + 
            ")", Pattern.CASE_INSENSITIVE);
        
    public static int parseJavaInteger(String javaInteger) throws NumberFormatException {
        BigInteger value = parseIntegerAsBigInt(javaInteger);
        try {
            return value.intValueExact();
        } catch (@SuppressWarnings("unused") ArithmeticException e) {
            throw new NumberFormatException("Number is not between Integer.MIN_VALUE and Integer.MAX_VALUE");
        }
    }
    
    public static long parseJavaLong(String javaLong) throws NumberFormatException {
        BigInteger value = parseIntegerAsBigInt(javaLong);
        try {
            return value.longValueExact();
        } catch (@SuppressWarnings("unused") ArithmeticException e) {
            throw new NumberFormatException("Number is not between Integer.MIN_VALUE and Integer.MAX_VALUE");
        }
    }

    private static BigInteger parseIntegerAsBigInt(String javaLiteral) {
        Matcher intMatcher = SIGNED_INTEGER_LITERAL.matcher(javaLiteral);
        if (!intMatcher.matches()) {
            throw new NumberFormatException(javaLiteral + " is not recognized as a Java integer literal");
        }
        
        String signGroup = intMatcher.group(1);
        String prefixAndValueGroup = intMatcher.group(2);
        String radixGroup = "";
        String valueGroup = "";
        // String longGroup = "";
        List<Pattern> patterns = List.of(BINARY, OCTAL, DECIMAL, HEXADECIMAL);
        for (Pattern pattern : patterns) {
            Matcher specificMatcher = pattern.matcher(prefixAndValueGroup);
            if (specificMatcher.matches()) {
                radixGroup = specificMatcher.group(1);
                valueGroup = specificMatcher.group(2);
                // longGroup = specificMatcher.group(3);
                break;
            }
        }
        
        if (valueGroup == null) {
            throw new RuntimeException("Number both matches but doesn't contain a value (parser error)");
        }

        BigInteger sign = signGroup != null && signGroup.matches("-") ? BigInteger.ONE.negate() : BigInteger.ONE; 
        
        int radix;
        switch (radixGroup.toLowerCase()) {
        case "0b":
            radix = 2;
            break;
        case "0":
            radix = 8;
            break;
        case "":
            radix = 10;
            break;
        case "0x":
            radix = 16;
            break;
        default:
            throw new RuntimeException();
        }
 
        BigInteger value = new BigInteger(valueGroup.replaceAll("_", ""), radix).multiply(sign);
        return value;
    }
}

Я также попытался использовать код для поиска нескольких целых чисел из строки, но это не сработало. Проблема в том, что некоторые недопустимые литералы, такие как 0__0 был принят как два литерала с нулевым значением; не совсем то, что вы хотите. Поэтому, пожалуйста, используйте регулярное выражение только для определения того, является ли строка на самом деле целым числом, и разделите целые числа, например, с помощью String.split(SEPARATOR_REGEX).

Забавно, что моя среда разработки Eclipse приняла 0__0 в буквальном смысле, даже если он официально не соответствует JLS. Не большой, но тем не менее странный.

2021-11-23 22:27:00

Быстро просмотрите свой ответ, извините, что слишком устал, чтобы углубляться, но: будьте осторожны, не используйте слишком много снимков, в частности, если они вам не нужны. Используйте группы без захвата (?:....) (захваты имеют определенную стоимость).
Casimir et Hippolyte

Я использую группы без захвата, где это возможно. Возможно, для проверки всего целого числа я мог бы удалить несколько; они мне не нужны для первоначального сопоставления. Или, может быть, я мог бы удалить все начальное совпадение и просто оставить цикл, который проверяет все возможные форматы. Но эй, в конце концов, мы пытаемся сопоставить целые числа, а не страницы и страницы текста...
Maarten Bodewes
-1

Ну ... проще говоря, базовые числа 2, 8 и 10 могут использовать один и тот же шаблон, поскольку все их значения являются числовыми символами. НО вам, вероятно, нужно выражение для каждого типа. Проблема в том, что вы не прояснили свои намерения. Я исхожу из предположения, что вы хотите, чтобы выражение проверяло, на какой основе находится конкретное значение.

String base10Regex = "[0-9]+";
String base2Regex = "[0-1]+";
String base8Regex = "[0-7]+";
String base16Regex = "^[0-9A-F]+$";

Для восьмеричных и десятичных значений вам нужно будет добавить свое выражение, чтобы проверить наличие необязательного символа знака "^[\\+|-]?". Для шестнадцатеричных значений, если вы ожидаете, что значения будут начинаться с "0x", я предложу добавить в выражение эти буквальные значения.

2021-12-09 23:34:58

Никаких подчеркиваний, и это не соответствует фактическим целым числам. И, конечно, границы (^$) не будет работать с find, но это только начало...
Maarten Bodewes

@MaartenBodewes Спасибо тебе. Я даю вам подчеркивание, но что вы имеете в виду, что оно не соответствует фактическим целым числам? Кроме того, я не знал, что границы не работают с find. Так что спасибо вам и за это.
hfontanez

Извините, мой плохой, я имел в виду, что он не соответствует литералам, указанным в JLS, где вам нужно иметь 0x или 0X для шестнадцатеричных и т. Д.
Maarten Bodewes

@MaartenBodewes за исключением того, что я написал: "если вы ожидаете, что значения будут начинаться с "0x", я предложу добавить в выражение эти буквальные значения".
hfontanez

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................