Espressione regolare per abbinare intero letterale

0

Domanda

Stavo pensando che l'analisi di una lista di interi (da una proprietà di stringa). Tuttavia, vorrei andare al di là positivi e negativi, per i valori decimali e analizzare qualsiasi stringa che indica un Java intero letterale (JLS 17) come può essere trovato nel codice sorgente. Allo stesso modo, vorrei essere indulgenti per quanto riguarda eventuali prefissi, separatori e appendici in tutto il interi stessi. In altre parole, io voglio trovare il loro utilizzo ripetute chiamate al Matcher.find().

C'è una espressione regolare che corrisponde a tutte le possibili Java intero letterali? Non ha bisogno di controllare i limiti superiore e inferiore.


Anche se ho fatto il collegamento esplicito per la giustizia, libertà e sicurezza, vi mostrerò alcuni validi e non validi numeri:

  • -1: il 1 è abbinato, ma il segno meno è un operatore unario (vado a regolare se necessario)
  • 0x00_00_00_0F: il valore di quindici è abbinata in cifre in esadecimale, con un carattere di sottolineatura per separare i due nibble
  • 0b0000_1111: il valore di quindici in binario corrisponde
  • 017: il valore ottale di quindici è abbinato
integer java literals regex
2021-11-23 21:48:28
3

Migliore risposta

4

Qualcosa di simile:

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

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

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

binario:
0[bB][01](?:_*[01])*[lL]?

Tutti insieme: (in freespacing modalità)

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

prova

2021-11-23 22:47:19

Ah, sì, che mi avrebbe un senso lungo. Non consentire più sottolinea però? Forse che ? dovrebbe essere un *?
Maarten Bodewes

@MaartenBodewes: Come ho capito il doc, sottolinea non dovrebbero essere contigous, ma forse t'è sbagliato? (in altre parole è 1____1 è permesso ?). Si noti che il gruppo all'interno del quale opzionale sottolineatura è, alla fine viene ripetuto.
Casimir et Hippolyte

Eh, c'è qualcuno che può riscrivere il regex? Mi è sembrato di essere in grado di aggiornare (la versione di prova era ancora ? invece di *)....
Maarten Bodewes

Grazie di nuovo, ho postato una risposta che analizza l'intero usando la sintassi delle espressioni regolari in base a spirito sulla tua regex.
Maarten Bodewes
0

Dopo la risposta da Casimiro ho deciso di prendere un po ' di più e implementato il codice effettivamente analizzare i numeri interi e, riportato qui di seguito. Include i più e meno simboli, anche se queste non fanno ufficialmente parte dell'intero, letterale, come descritto nel JLS; sono gli operatori unari.

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

Ho anche provato ad usare il codice per trovare più i numeri interi da una stringa, ma che non sono andate bene. Il problema è che alcune voci di valori letterali come 0__0 ho accettato come due valori letterali con valore pari a zero; non è esattamente ciò che si desidera. Quindi, si prega di utilizzare il regex solo per rilevare se una stringa è in realtà un intero e separare i numeri interi ad esempio utilizzando String.split(SEPARATOR_REGEX).

Abbastanza divertenti, il mio IDE Eclipse ha fatto accettare 0__0 come letterale, anche se ufficialmente non è compatibile con la JLS. Non biggy, ma strano che nessuno-la-meno.

2021-11-23 22:27:00

Rapidamente vista la tua risposta, mi dispiace troppo stanchi per andare più in profondità, ma: prestare attenzione a non utilizzare troppo cattura, in particolare, se non ne hanno bisogno. Non utilizzare gruppi di cattura (?:....) (cattura hanno un costo).
Casimir et Hippolyte

Faccio uso di non-gruppi di cattura, se possibile. Forse per convalidare l'intero numero intero potevo rimuovere alcuni; non ho bisogno di loro per la parte iniziale del match. O forse sono riuscito a rimuovere l'intero match iniziale e lasciare solo il ciclo che convalida tutti i formati possibili. Ma hey, alla fine stiamo cercando di abbinare i numeri interi, non pagine e pagine di testo...
Maarten Bodewes
-1

Bene.... in termini più semplici, in base 2, 8 e 10 numero potrebbe utilizzare lo stesso modello in quanto i loro valori sono tutti i caratteri numerici. MA, probabilmente vuole un'espressione per ogni tipo. Il problema è che non è chiaro il tuo intento. Io vado sul presupposto che l'espressione per convalidare quello che di base un particolare valore.

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

Per il ottali, decimali e valori, è necessario anteporre la tua espressione di cercare una opzionale carattere di segno "^[\\+|-]?". Per i valori esadecimali, se si prevede che i valori di a iniziare con "0x", io suggerisco di anteporre l'espressione con tali valori letterali.

2021-12-09 23:34:58

Senza caratteri di sottolineatura e non corrisponde alla reale interi. E, naturalmente, i confini (^$) non lavoro con la ricerca, ma è un inizio...
Maarten Bodewes

@MaartenBodewes Grazie. Concedo i caratteri di sottolineatura, ma cosa intendi che non corrisponde alla reale interi? Inoltre, non sapevo che i confini non funzionano con find. Così, grazie anche per questo.
hfontanez

Scusa, il mio male, intendevo dire che non corrisponde ai valori letterali, come indicato nel JLS, in cui è necessario avere il 0x o 0X per le esadecimali etc.
Maarten Bodewes

@MaartenBodewes tranne che ho scritto " se si prevede che i valori di a iniziare con "0x", io suggerisco di anteporre l'espressione con tali valori letterali"
hfontanez

In altre lingue

Questa pagina è in altre lingue

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