2010-02-18 4 views
2

Je dois analyser une chaîne et de capturer certaines valeurs:Cette RegEx capture un nombre incorrect de groupes

FREQ = HEBDOMADAIRE; WKST = MO; BYDAY = 2TU, 2WE

Je veux capturer 2 groupes:

grp 1: 2, 2 
grp 2: TU, WE 

Les nombres représentent des intervalles. TU, WE représente les jours de la semaine. J'ai besoin des deux.

Je suis en utilisant ce code:

private final static java.util.regex.Pattern regBYDAY = java.util.regex.Pattern.compile(".*;BYDAY=(?:([+-]?[0-9]*)([A-Z]{2}),?)*.*"); 

String rrule = "FREQ=WEEKLY;WKST=MO;BYDAY=2TU,2WE"; 
java.util.regex.Matcher result = regBYDAY.matcher(rrule); 
if (result.matches()) 
{ 
    int grpCount = result.groupCount(); 
    for (int i = 1; i < grpCount; i++) 
    { 
     String g = result.group(i); 
     ... 
    } 
} 

grpcount == 2 - pourquoi? Si je lis correctement la documentation de Java (ce petit peu) je devrais obtenir 5? 0 = l'expression entière, 1,2,3,4 = mes captures 2,2, TU et WE.

résultat.group (1) == "2";

Je suis un programmeur C# avec très peu d'expérience java donc j'ai testé le RegEx dans le "Regular Expression Workbench" - un excellent programme C# pour tester RegEx. Là mon RegEx fonctionne bien.

https://code.msdn.microsoft.com/RegexWorkbench

RegExWB:

.*;BYDAY=(?:([+-]?[0-9]*)([A-Z]{2}),?)*.* 

Matching: 
FREQ=WEEKLY;WKST=MO;BYDAY=22TU,-2WE,+223FR 
    1 => 22 
    1 => -2 
    1 => +223 
    2 => TU 
    2 => WE 
    2 => FR 
+0

Je ne suis pas sûr de ce que les outils sont disponibles en Java pour faire ce dont vous avez besoin, mais un indice est que votre regex n'a que 2 groupes de capture: '([+ -]? [0-9] *)' et '([AZ] {2})' – Johrn

+0

Oui - c'est vrai. Un pour Interval, un pour Weekday. En C# (.net) quand une regex a plus de hits, j'obtiens un tableau de résultats dans mon groupe de capture. Comme dans l'exemple RegExWB. Le groupe 1 a trois visites, le groupe 2 aussi. Est-ce aussi possible avec Java? – Arthur

Répondre

1

Vous pouvez également utiliser cette approche pour augmenter la lisibilité et jusqu'à certain point indepe ndence de la mise en œuvre à l'aide d'un sous-ensemble regexp plus commun

final Pattern re1 = Pattern.compile(".*;BYDAY=(.*)"); 
final Pattern re2 = Pattern.compile("(?:([+-]?[0-9]*)([A-Z]{2}),?)"); 

final Matcher matcher1 = re1.matcher(rrule); 
if (matcher1.matches()) { 
    final String group1 = matcher1.group(1); 
    Matcher matcher2 = re2.matcher(group1); 
    while(matcher2.find()) { 
     System.out.println("group: " + matcher2.group(1) + " " + 
        matcher2.group(2)); 
    } 
} 
+0

Ahhhh - Je pense que c'est ce que je cherche. Je vais essayer demain, maintenant je vais chercher une pizza et une bière. C'est le soir ici ... – Arthur

+0

Fonctionne parfaitement! Je vous remercie! find() était la chose qui me manquait. – Arthur

0

Je suis un peu rouillé, mais je propose de "mises en garde". Tout d'abord, regexp (s) viennent dans divers dialectes. Il existe un livre fantastique sur O'Reilly à ce sujet, mais il est possible que votre utilitaire C# applique des règles légèrement différentes.

À titre d'exemple, j'ai utilisé un similaire (mais différent tool) et a découvert qu'il a fait analyser differenty choses ...

d'abord, il a rejeté votre regexp (peut-être une faute de frappe?) La première « * » n'a pas de sens, sauf si vous mettez un point (.) devant. Comme ceci:

.*;BYDAY=(?:([+-]?[0-9]*)([A-Z]{2}),?)*.* 

Maintenant, il a été accepté, mais il "alignait" seulement 2/WE part, et "sauté" la paire 2/TU.

(je vous suggère de lire sur la correspondance gourmande et non avide de comprendre un peu mieux

Par conséquent, je mis à jour votre modèle comme suit:.

.*;BYDAY=(?:([+-]?[0-9]*)([A-Z]{2}),?),(?:([+-]?[0-9]*)([A-Z]{2}),?)*.* 

Et maintenant il fonctionne et capte correctement 2 , TU, 2 et WE.

peut-être que cette aide?

+0

THX - Oui, il y a une erreur Copy & Past - votre hypothèse concernant le premier RegEx est correcte - je mettrai à jour ma question. Mais: La façon dont je comprends votre RegEx, il suppose qu'il y a toujours deux parties en semaine. Mais 1 à 6 sont autorisés (ou 0 à 7 mais cela n'a aucun sens). J'ai besoin de capturer un nombre variable de jours de la semaine avec Intervalles. Comme l'exemple de code par rapport à l'exemple RegExWB. – Arthur

+0

Pour être précis, ce RegEx doit utiliser le dialecte Java java.util.regex.Pattern: http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html – Powerlord

+0

ne fonctionne pas pour votre deuxième cas: 'FREQ = HEBDOMADAIRE; WKST = MO; BYDAY = 22TU, -2WE, + 223FR' –

1

Votre regex fonctionne de la même en Java comme il le fait en C#; c'est juste que dans Java vous pouvez seulement accéder à la capture finale pour chaque groupe. En fait, .NET est l'un des deux seuls artefacts regex que je connaisse qui permettent de récupérer des captures intermédiaires (Perl 6 étant l'autre).

C'est probablement la façon la plus simple de faire ce que vous voulez en Java:

String s= "FREQ=WEEKLY;WKST=MO;BYDAY=22TU,-2WE,+223FR"; 
Pattern p = Pattern.compile("(?:;BYDAY=|,)([+-]?[0-9]+)([A-Z]{2})"); 
Matcher m = p.matcher(s); 
while (m.find()) 
{ 
    System.out.printf("Interval: %5s, Day of Week: %s%n", 
        m.group(1), m.group(2)); 
} 

est ici l'équivalent du code C#, si vous êtes intéressé:

string s = "FREQ=WEEKLY;WKST=MO;BYDAY=22TU,-2WE,+223FR"; 
Regex r = new Regex(@"(?:;BYDAY=|,)([+-]?[0-9]+)([A-Z]{2})"); 
foreach (Match m in r.Matches(s)) 
{ 
    Console.WriteLine("Interval: {0,5}, Day of Week: {1}", 
        m.Groups[1], m.Groups[2]); 
} 
Questions connexes