Expression régulière correspondant littéral entier

0

La question

Je pensais à de l'analyse d'une liste d'entiers (à partir d'une propriété de chaîne). Cependant, je tiens à aller au-delà de positif et de négatif valeurs décimales et analyser toute chaîne de caractères qui indique un Java entier littéral (JLS 17) que l'on trouve dans le code source. De même, je tiens à être indulgent en ce qui concerne les préfixes, des séparateurs et des annexes autour de la entiers eux-mêmes. En d'autres termes, je veux les trouver en utilisant les appels répétés à la Matcher.find().

Est-il une expression régulière qui correspond à tous les possibles Java les littéraux entiers? Il n'a pas besoin de vérifier les limites supérieure et inférieure.


Même si j'ai fait explicitement le lien de la JLS, je vais vous montrer quelques valides et non valides numéros:

  • -1: l' 1 est adapté, mais au moins, c'est un opérateur unaire (je vais l'ajuster si nécessaire)
  • 0x00_00_00_0F: la valeur de quinze correspondance est que les chiffres hexadécimaux, avec un trait de soulignement pour séparer les deux amuse-gueules
  • 0b0000_1111: la valeur de quinze en binaire est assortie d'
  • 017: la valeur octale de quinze ans, est assortie d'
integer java literals regex
2021-11-23 21:48:28
3

La meilleure réponse

4

Quelque chose comme ça:

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

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

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

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

Tous ensemble: (en freespacing mode)

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

tester

2021-11-23 22:47:19

Ah, ouais, ce serait me faire un long chemin. Elle permet de multiples souligne cependant? Peut-être que ? devrait être un *?
Maarten Bodewes

@MaartenBodewes Que j'ai compris de la doc, des traits de soulignement ne sont pas censés être des états contigus, mais peut-être que je suis mal? (en d'autres mots est 1____1 autorisé ?). Notez que le groupe à l'intérieur de laquelle l'option trait de soulignement, est, est, éventuellement répété.
Casimir et Hippolyte

Hein, quelqu'un peut-il réécrire que les regex? Il me semblait être en mesure de le mettre à jour (la version de test avait encore la ? au lieu de l' *)....
Maarten Bodewes

Merci encore, j'ai posté une réponse qui analyse l'entier à l'aide de la syntaxe d'expression régulière en fonction de l'esprit sur votre regex.
Maarten Bodewes
0

Après la réponse de Casimir, j'ai décidé de prendre un peu plus loin et la mise en œuvre du code de réellement analyser les entiers ainsi, inclus ci-dessous. Il comprend le moins et plus de symboles, même si ceux-ci ne sont pas officiellement partie de l'entier littéral comme décrit dans la JLS; ils sont des opérateurs unaires.

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

J'ai aussi essayé d'utiliser le code pour trouver plusieurs nombres entiers à partir d'une chaîne, mais qui ne vont pas bien. Le problème est que certains invalides littéraux tels que 0__0 j'ai été accepté comme deux littéraux avec la valeur zéro; pas exactement ce que vous voulez. Veuillez utiliser les regex pour détecter si une chaîne est en fait un entier et séparer les nombres entiers en utilisant par exemple String.split(SEPARATOR_REGEX).

C'est marrant, mon IDE Eclipse n'a accepter 0__0 comme un littéral, même si elle n'est pas officiellement compatible avec les JLS. Pas un biggy, mais bizarre aucun-le-moins.

2021-11-23 22:27:00

Visualiser rapidement votre réponse, désolé trop fatigué pour aller plus profond, mais: prenez soin de ne pas utiliser trop de capture, en particulier si vous n'avez pas besoin. L'utilisation non-capture de groupes (?:....) (capture ont un coût).
Casimir et Hippolyte

Je ne l'utilisation de non-capture de groupes lorsque cela est possible. Peut-être afin de valider l'ensemble entier je pouvais enlever un peu; je n'ai pas besoin d'eux pour le match initial. Ou peut-être que je pourrais supprimer le premier match et il suffit de laisser la boucle qui valide tous les formats possibles. Mais bon, à la fin, nous essayons de faire correspondre des nombres entiers, pas des pages et des pages de texte...
Maarten Bodewes
-1

Bien.... en termes plus simples, la base 2, 8, et 10 nombre pourrait utiliser le même modèle puisque leurs valeurs sont toutes des caractères numériques. MAIS, vous voulez probablement une expression pour chaque type. Le problème, c'est que vous n'avez pas votre intention. Je vais sur l'hypothèse que vous voulez l'expression de valider ce que la base de la valeur particulière est.

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

Pour l'octal et des valeurs décimales, vous devez ajouter votre expression pour vérifier un signe optionnel caractère "^[\\+|-]?". Pour les valeurs en hexadécimal, si vous vous attendez à ce que les valeurs de commencer par "0x", je propose d'ajouter de l'expression de ces valeurs littérales.

2021-12-09 23:34:58

Pas de souligne et il ne correspond pas au réel entiers. Et bien sûr, les limites (^$) ne fonctionnerait pas avec la trouver, mais c'est un début...
Maarten Bodewes

@MaartenBodewes Merci. Je vous l'accorde les traits de soulignement, mais que voulez-vous dire qu'il ne correspond pas au réel entiers? Aussi, je ne savais pas les limites ne fonctionnent pas avec find. Donc, merci à vous aussi, d'ailleurs.
hfontanez

Désolé, mon mal, je voulais dire qu'il ne correspond pas à la littéraux comme indiqué dans le JLS, où vous avez besoin d'avoir le 0x ou 0X pour hexadécimaux etc.
Maarten Bodewes

@MaartenBodewes sauf que moi j'ai écrit: "si vous vous attendez à ce que les valeurs de commencer par "0x", je propose d'ajouter de l'expression de ces valeurs littérales"
hfontanez

Dans d'autres langues

Cette page est dans d'autres langues

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