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?
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.
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);
}
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?
Vous pouvez utiliser des expressions régulières Java - veuillez consulter http://www.vogella.de/articles/JavaRegularExpressions/article.html
Merci...
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])));
}
}
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");
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.