J'ai toujours pensé qu'une assertion look-behind dans l'API regex de Java (et beaucoup d'autres langues d'ailleurs) doit avoir une longueur évidente. Ainsi, les quantificateurs STAR et PLUS ne sont pas autorisés à l'intérieur look-behinds.Regex look-behind sans longueur maximale évidente en Java
L'excellente ressource en ligne regular-expressions.info semble confirmer (certaines) mes hypothèses:
« [...] Java prend les choses un peu plus loin en permettant la répétition finie Vous encore ne pouvez pas utiliser l'étoile. ou plus, mais vous pouvez utiliser le point d'interrogation et les accolades avec le paramètre max spécifié. Java reconnaît le fait que la répétition finie peut être réécrite comme une alternance de chaînes avec différentes, mais des longueurs fixes. Malheureusement, le JDK 1.4 et 1.5 ont quelques bugs lorsque vous utilisez l'alternance à l'intérieur lookbehind. Ces ont été corrigés dans JDK 1.6. [...] »
En utilisant les accolades fonctionne aussi longtemps que la longueur totale de la gamme des caractères à l'intérieur du regard-behind est inférieure ou égale à Integer.MAX_VALUE donc. ces expressions rationnelles sont valables:
"(?<=a{0," +(Integer.MAX_VALUE) + "})B"
"(?<=Ca{0," +(Integer.MAX_VALUE-1) + "})B"
"(?<=CCa{0," +(Integer.MAX_VALUE-2) + "})B"
Mais ce ne sont pas:
"(?<=Ca{0," +(Integer.MAX_VALUE) +"})B"
"(?<=CCa{0," +(Integer.MAX_VALUE-1) +"})B"
Cependant, je ne comprends pas e e suivant:
Quand je lance un test en utilisant * et + quantificateurs dans un tout va bien regarder en arrière, (voir la sortie Test 1 et Test 2).
Mais, quand j'ajoute un caractère unique au début de l'regarder en arrière de Test 1 et Test 2, il brise (voir la sortie Test 3).
Faire les gourmands * de Test 3 réticents n'a pas d'effet, il se casse encore (voir Test 4).
Voici le harnais de test:
public class Main {
private static String testFind(String regex, String input) {
try {
boolean returned = java.util.regex.Pattern.compile(regex).matcher(input).find();
return "testFind : Valid -> regex = "+regex+", input = "+input+", returned = "+returned;
} catch(Exception e) {
return "testFind : Invalid -> "+regex+", "+e.getMessage();
}
}
private static String testReplaceAll(String regex, String input) {
try {
String returned = input.replaceAll(regex, "FOO");
return "testReplaceAll : Valid -> regex = "+regex+", input = "+input+", returned = "+returned;
} catch(Exception e) {
return "testReplaceAll : Invalid -> "+regex+", "+e.getMessage();
}
}
private static String testSplit(String regex, String input) {
try {
String[] returned = input.split(regex);
return "testSplit : Valid -> regex = "+regex+", input = "+input+", returned = "+java.util.Arrays.toString(returned);
} catch(Exception e) {
return "testSplit : Invalid -> "+regex+", "+e.getMessage();
}
}
public static void main(String[] args) {
String[] regexes = {"(?<=a*)B", "(?<=a+)B", "(?<=Ca*)B", "(?<=Ca*?)B"};
String input = "CaaaaaaaaaaaaaaaBaaaa";
int test = 0;
for(String regex : regexes) {
test++;
System.out.println("********************** Test "+test+" **********************");
System.out.println(" "+testFind(regex, input));
System.out.println(" "+testReplaceAll(regex, input));
System.out.println(" "+testSplit(regex, input));
System.out.println();
}
}
}
La sortie:
********************** Test 1 **********************
testFind : Valid -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = true
testReplaceAll : Valid -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = CaaaaaaaaaaaaaaaFOOaaaa
testSplit : Valid -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = [Caaaaaaaaaaaaaaa, aaaa]
********************** Test 2 **********************
testFind : Valid -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = true
testReplaceAll : Valid -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = CaaaaaaaaaaaaaaaFOOaaaa
testSplit : Valid -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = [Caaaaaaaaaaaaaaa, aaaa]
********************** Test 3 **********************
testFind : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6
(?<=Ca*)B
^
testReplaceAll : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6
(?<=Ca*)B
^
testSplit : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6
(?<=Ca*)B
^
********************** Test 4 **********************
testFind : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7
(?<=Ca*?)B
^
testReplaceAll : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7
(?<=Ca*?)B
^
testSplit : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7
(?<=Ca*?)B
^
Ma question est peut-être évident, mais je vais demander encore: Quelqu'un peut-il me expliquer pourquoi Test 1 et échec, et Test 3 et ne pas? Je m'attendais à tous les échecs, pas la moitié d'entre eux au travail et la moitié d'entre eux pour échouer.
Merci.
PS. J'utilise: Java version 1.6.0_14
Il ne me venait pas à l'esprit de regarder la source de Pattern ... Silly moi. Je vous remercie! Cela prend tout son sens maintenant. –
L'une des nombreuses raisons pour lesquelles je ne peux pas vivre sans Eclipse est mon doigt qui claque ctrl-click (qui, si vous n'utilisez pas Eclipse, signifie "ouvrir le fichier source où ce nom est défini"). –
Merci, je n'ai jamais pris la peine d'attacher la source à Eclipse. Je le ferai maintenant pour sûr. Merci. –