Expressão Regular para correspondência literal inteiro

0

Pergunta

Eu estava pensando sobre análise de uma lista de inteiros (a partir de uma propriedade de seqüência de caracteres). No entanto, eu gostaria de ir além de apenas positivos e negativos valores decimais e analisar qualquer seqüência de caracteres que indica um Java inteiro literal (JLS 17) como pode ser encontrado no código-fonte. Da mesma forma, eu gostaria de ser leniente com relação a qualquer prefixos, separadores e apêndices em torno de inteiros de si. Em outras palavras, eu quero encontrá-los usando chamadas repetidas para Matcher.find().

Existe uma expressão regular que corresponde a todos os possíveis Java inteiros literais? Ele não precisa verificar os limites superiores e inferiores.


Mesmo que eu fiz explicitamente link para o JLS, eu vou mostrar alguns válidos e inválidos números:

  • -1: o 1 é correspondido, mas o sinal de menos é um operador unário (eu vou ajustar, se necessário)
  • 0x00_00_00_0F: o valor de quinze é correspondido como dígitos hexadecimais, com um carácter de sublinhado para separar os dois nibbles
  • 0b0000_1111: o valor de quinze em binário é correspondido
  • 017: o valor octal de quinze é correspondido
integer java literals regex
2021-11-23 21:48:28
3

Melhor resposta

4

Algo assim:

decimal:
(?:0|[1-9](?:_*[0-9])*)[lL]?

hexadecimal:
0x[a-fA-F0-9](?:_*[a-fA-F0-9])*[lL]?

octal:
0[0-7](?:_*[0-7])*[lL]?

binário:
0[bB][01](?:_*[01])*[lL]?

Todos juntos: (em freespacing modo)

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

o teste

2021-11-23 22:47:19

Ah, sim, que ia buscar-me um longo caminho. Não permitir que vários ressalta embora? Talvez que ? deve ser um *?
Maarten Bodewes

@MaartenBodewes: Como eu entendo o doc, sublinhados não são supostos para ser contigous, mas talvez eu estou errado? (em outras palavras, 1____1 permitida ?). Nota-se que o grupo dentro do qual o opcional sublinhado é, eventualmente, é repetido.
Casimir et Hippolyte

Ué, alguém pode reescrever essa regex? Eu parecia ser capaz de atualizá-lo (a versão de teste ainda teve ? em vez de a *)....
Maarten Bodewes

Obrigado mais uma vez, eu postei uma resposta que analisa o número inteiro usando a sintaxe de expressão regular com base em espírito no seu regex.
Maarten Bodewes
0

Após a resposta de Casimir eu decidi tomar um pouco mais e implementado algum código para realmente analisar os números inteiros, bem como, incluídas abaixo. Ele inclui o sinal de menos e mais símbolos, embora aqueles que são oficialmente não fazem parte do número inteiro literal, como descrito na JLS; eles são operadores unários.

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

Eu também tentei usar o código para encontrar vários números inteiros a partir de uma seqüência de caracteres, mas que não vão bem. O problema é que algumas inválido literais, tais como 0__0 foi aceito como dois literais com o valor zero; não é exatamente o que você deseja. Então, por favor, use o regex apenas para detectar se uma cadeia de caracteres, na verdade, é um número inteiro e separar os números inteiros e.g. usando String.split(SEPARATOR_REGEX).

Engraçado o meu Eclipse IDE fez aceitar 0__0 como um literal mesmo se ela é oficialmente não compatível para o JLS. Não mais importante, mas estranho nada-a-menos.

2021-11-23 22:27:00

Visualizar rapidamente a sua resposta, desculpe cansado demais para ir mais profundo, mas tome cuidado para não usar muito captura, em particular, se não precisamos deles. Uso não-captura de grupos (?:....) (captura de ter um custo).
Casimir et Hippolyte

Eu uso não-captura de grupos sempre que possível. Talvez para validar todo o número inteiro que eu poderia remover alguns; eu não preciso deles para a partida inicial. Ou talvez eu pudesse remover toda a partida inicial e deixar o loop que valida todos os formatos possíveis. Mas hey, no final, estamos tentando corresponder a números inteiros, e não páginas e páginas de texto...
Maarten Bodewes
-1

Bem.... em termos mais simples, base 2, 8, e 10 número poderia usar o mesmo padrão desde que seus valores são todos os caracteres numéricos. MAS, você provavelmente quer uma expressão para cada tipo. O problema é que você não deixou claro a sua intenção. Eu estou indo no pressuposto de que você quer a expressão para validar o que da base de dados de um determinado valor.

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

Para o octal e decimal valores, você tem de acrescentar antes a sua expressão para verificar se um sinal opcional personagem "^[\\+|-]?". Para valores hexadecimais, se você esperar que os valores para iniciar com "0x", eu sugiro que preceder a expressão com os valores literais.

2021-12-09 23:34:58

Sem sublinhados e não coincide com o real inteiros. E, claro, os limites (^$não trabalho com a localização, mas é um começo...
Maarten Bodewes

@MaartenBodewes Obrigado. Eu concedo-lhe os sublinhados, mas o que quer dizer que ela não corresponde ao real inteiros? Além disso, eu não sabia limites não trabalho com find. Então, muito obrigado para o bem.
hfontanez

Desculpe, foi mal, eu quis dizer que não coincide com o de literais como indicado na JLS, onde você precisa ter o 0x ou 0X para os hexadecimais etc.
Maarten Bodewes

@MaartenBodewes exceto eu escrevi " se você espera que os valores de começar com "0x", eu sugiro que preceder a expressão com os valores literais"
hfontanez

Em outros idiomas

Esta página está em outros idiomas

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................