web-dev-qa-db-fra.com

Vérifier si String correspond à un format String

En Java, comment déterminer si une chaîne correspond à un chaîne de formatage (c'est-à-dire: song%03d.mp3)?

En d'autres termes, comment implémenteriez-vous la fonction suivante?

/**
* @return true if formatted equals String.format(format, something), false otherwise.
**/
boolean matches(String formatted, String format);

Exemples:

matches("hello world!", "hello %s!"); // true
matches("song001.mp3", "song%03d.mp3"); // true
matches("potato", "song%03d.mp3"); // false

Peut-être y a-t-il un moyen de convertir une chaîne de format en une expression régulière?

Clarification

Le format String est un paramètre. Je ne le sais pas d'avance. song%03d.mp3 est juste un exemple. Il pourrait s'agir d'une autre chaîne de format.

Si cela peut aider, je peux supposer que la chaîne de format n'aura qu'un paramètre.

14
hpique

Je ne connais pas de bibliothèque qui fait ça. Voici un exemple comment convertir un modèle de format en regex. Notez que Pattern.quote est important pour gérer les expressions rationnelles accidentelles dans la chaîne de format.

// copied from Java.util.Formatter
// %[argument_index$][flags][width][.precision][t]conversion
private static final String formatSpecifier
    = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";

private static final Pattern formatToken = Pattern.compile(formatSpecifier);

public Pattern convert(final String format) {
    final StringBuilder regex = new StringBuilder();
    final Matcher matcher = formatToken.matcher(format);
    int lastIndex = 0;
    regex.append('^');
    while (matcher.find()) {
        regex.append(Pattern.quote(format.substring(lastIndex, matcher.start())));
        regex.append(convertToken(matcher.group(1), matcher.group(2), matcher.group(3), 
                                  matcher.group(4), matcher.group(5), matcher.group(6)));
        lastIndex = matcher.end();
    }
    regex.append(Pattern.quote(format.substring(lastIndex, format.length())));
    regex.append('$');
    return Pattern.compile(regex.toString());
}

Bien sûr, implémenter convertToken sera un défi. Voici quelque chose pour commencer:

private static String convertToken(String index, String flags, String width, String precision, String temporal, String conversion) {
    if (conversion.equals("s")) {
        return "[\\w\\d]*";
    } else if (conversion.equals("d")) {
        return "[\\d]{" + width + "}";
    }
    throw new IllegalArgumentException("%" + index + flags + width + precision + temporal + conversion);
}
9
Cephalopod

Il n'y a pas de moyen simple de faire cela. Une méthode simple consisterait à écrire du code qui convertit les chaînes de format (ou un sous-ensemble plus simple d’entre elles) en expressions rationnelles, puis à faire correspondre celles qui utilisent les classes d’expressions régulières standard.

Un meilleur moyen est probablement de repenser/refactoriser votre code. Pourquoi vous voulez ceci?

1
dtech

Vous pouvez utiliser des expressions régulières Java - veuillez consulter http://www.vogella.de/articles/JavaRegularExpressions/article.html

Merci...

1
Prabath Siriwardena

Etant donné que vous ne connaissez pas le format à l’avance, vous devrez écrire une méthode qui convertit une chaîne de format en une expression rationnelle. Pas trivial, mais possible. Voici un exemple simple pour les 2 cas de test que vous avez donnés:

public static String getRegexpFromFormatString(String format)
{
    String toReturn = format;

    // escape some special regexp chars
    toReturn = toReturn.replaceAll("\\.", "\\\\.");
    toReturn = toReturn.replaceAll("\\!", "\\\\!");

    if (toReturn.indexOf("%") >= 0)
    {
        toReturn = toReturn.replaceAll("%s", "[\\\\w]+"); //accepts 0-9 A-Z a-z _

        while (toReturn.matches(".*%([0-9]+)[d]{1}.*"))
        {
            String digitStr = toReturn.replaceFirst(".*%([0-9]+)[d]{1}.*", "$1");
            int numDigits = Integer.parseInt(digitStr);
            toReturn = toReturn.replaceFirst("(.*)(%[0-9]+[d]{1})(.*)", "$1[0-9]{" + numDigits + "}$3");
        }
    }

    return "^" + toReturn + "$";
}

et un code de test:

public static void main(String[] args) throws Exception
{
    String formats[] = {"hello %s!", "song%03d.mp3", "song%03d.mp3"};
    for (int i=0; i<formats.length; i++)
    {
        System.out.println("Format in [" + i + "]: " + formats[i]);
        System.out.println("Regexp out[" + i + "]: " + getRegexp(formats[i]));
    }

    String[] words = {"hello world!", "song001.mp3", "potato"};
    for (int i=0; i<formats.length; i++)
    {
        System.out.println("Word [" + i + "]: " + words[i] +
            " : matches=" + words[i].matches(getRegexpFromFormatString(formats[i])));
    }
}
1
JJ.

la classe string a la méthode matches, vous pouvez y passer une expression rationnelle. String.matches (String)

pour la regex, vous pouvez voir ceci: http://download.Oracle.com/javase/1,5.0/docs/api/Java/util/regex/Pattern.html
exemples:

 "song001.mp3" .matches ("song \\ d {3} \\. mp3"); 
0
rascio

Vous pouvez utiliser String.matches ; bien que vous deviez alors utiliser une expression régulière plutôt que la chaîne de format.

Il ne devrait pas être trop difficile de remplacer quelque chose comme% 03d par un équivalent regex\d {3}

Exemple: 

"song001.mp3" .matches ("song \\ d {3} \\. mp3") // True

"pomme de terre" .matches ("chanson \\ d {3} \\. mp3") // False

Si vous avez vraiment besoin de la chaîne de format, vous devez créer une fonction qui remplace le format par un équivalent de regex et échappe aux caractères réservés de regex. utilisez ensuite la fonction String.matches.

0
Yhn