2010-11-22 4 views
5

J'essaie d'utiliser des expressions rationnelles Java pour correspondre à un modèle qui s'étend sur plusieurs lignes. Le modèle a une ligne qui commence par « A » suivi d'exactement 50 caractères, puis une ou plusieurs lignes commençant par « B » suivi d'exactement 50 caractères:Java Expression régulière pour trouver plusieurs lignes d'une longueur spécifique

A... // exactly 50 chars after the A 
B... 
B... 

Java expressions régulières ne semblent pas soutenir ceci cependant.

Voici une expression rationnelle qui fonctionne pour une A et une ligne B:

A.{50}[\\n[\\n\\r]]B.{50}[\\n[\\n\\r]] 

est ici le même regexp modifié pour trouver une ou plusieurs lignes B:

A.{50}[\\n[\\n\\r]][B.{50}[\\n[\\n\\r]]]+ 

Cette expression rationnelle ne trouve que le personnage B de tête sur la première ligne B, cependant. J'utilise [\\n[\\r\\n]] pour gérer les nouvelles lignes DOS et UNIX. L'activation du mode MULTILINE n'affecte pas les résultats.

Le problème semble être lorsque j'utilise les parenthèses avec '+' pour transformer l'expression rationnelle pour une ligne B en une classe de caractères qui peut capturer plusieurs lignes.

Y at-il quelque chose à propos des expressions rationnelles Java qui ne permettent pas le '.' caractère ou les accolades pour spécifier une longueur de ligne exacte?

+0

Il y a une ligne A avec 50 caractères suivant le 'A', puis plusieurs lignes B avec 50 caractères suivant le premier 'B'. stackoverflow n'a pas conservé les nouvelles lignes entre les lignes A et B que j'ai montrées ci-dessus. –

Répondre

0

Dans le regex suivant:

(A[^\r\n]{50}(\r\n|\n))(B[^\r\n]{50}(\r\n|\n))+ 

J'utilisé [^\r\n] pour correspondre à tout caractère non \r ou \n. Vous pouvez le remplacer par [\d] si vous avez des chiffres, par exemple.

Voir http://www.myregextester.com/?r=b7c3ca56

Dans l'exemple, l'expression régulière correspond à tous, sauf la dernière ligne.

+0

Cela semble fonctionner. –

0

Pour gérer Unix et Dos de style nouvelle ligne, vous pouvez utiliser:

\\r?\\n 

également votre façon de regrouper un ou plusieurs B lignes est incorrecte, vous utilisez [] pour le regroupement, vous devez utiliser (?: ) à la place.

Essayez cette regex:

A.{50}\\r?\\n(?:B.{50}(?:\\r?\\n)?)+ 

Regex tested here

+0

Juste pour le plaisir, puisque vous avez posté une version de rubis. Voici une grande version en python d'un testeur de regex http://www.pythonregex.com/ – Falmarri

+0

Merci. Cela semble fonctionner. –

0

Cela devrait fonctionner:

String input = "A1234567890\nA12345678\nA12345678\nB12345678\nA123456\nA1234567\nZA12345678\nB12345678\nA12345678\nB12345678\nB12345678\nB12345678\nB1234567\nA12345678\nB12345678"; 

String regex = "^A.{8}$((\\r|\\r\\n|\\n)^B.{8}$)+(\\r|\\r\\n|\\n|\\z)"; 

Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE); 
Matcher matcher = pattern.matcher(input); 

while (matcher.find()) { 
System.out.println("matches from " + matcher.start() + " to " + matcher.end()); 
} 

Note:

  1. l'utilisation de ^, $ et MULTILINE pour éviter pour correspondre à la ligne commençant par "ZA".
  2. l'utilisation de (\\r|\\r\\n|\\n) pour faire correspondre les lignes unix, windows et les anciennes mac-os.
  3. l'utilisation de (\\r|\\r\\n|\\n|\\z) pour correspondre à la dernière ligne B sans fin de ligne

Opsss, je 8 au lieu de 50 pour augmenter la lisibilité.

+0

Devrait être 10 au lieu de 8. –

+0

Non, la première ligne est un exemple de ligne qui ne correspond pas. – andcoz

0

Le point et les accolades fonctionnent correctement; c'est le reste de votre regex qui est faux. Check this out:

Pattern p = Pattern.compile("^A.{50}(?:(?:\r\n|[\r\n])B.{50})+$"); 

(?:\r\n|[\r\n]) correspond à une séquence de CRLF, CR uniquement ou LF uniquement. (J'aurais pu utiliser deux barres obliques inversées comme vous l'avez fait, mais cela fonctionne aussi). Si vous utilisez l'expression rationnelle pour extraire des correspondances d'un texte plus volumineux, vous devrez le compiler en mode MULTILINE afin que les ancres ^ et $ puissent correspondre aux limites de la ligne. Si elle est supposée correspondre à une chaîne entière, laissez-la dans le mode par défaut afin qu'elle ne corresponde qu'au début et à la fin de la chaîne.

0

La bonne façon de faire correspondre une séquence de saut de ligne est:

"(?:(?>\\u000D\\u000A)|[\\u000A\\u000B\\u000C\\u000D\\u0085\\u2028\\u2029)" 

qui est dans la notation chaîne slackbashy Java, bien sûr, tout comme vous pourriez passer à Pattern.compile. langues plus raisonnable vous permettent de vous en tirer avec simplement ceci:

(?:(?>\x0D\x0A)|\v) 

Mais alors, les expressions rationnelles de Java ont jamais été quelque chose comme raisonnable, et même cela est en fait un euphémisme pour how bad they really are. Le poor support for whitespace detection de Java est l'un des innombrables problèmes de ses regex.

Bonne chance: vous en aurez besoin. ☹

0

Cela devrait fonctionner aussi:

Pattern regex = Pattern.compile("^A.{50}$\\s+(?:^B.{50}$\\s*)+(?:^|\\z)", Pattern.MULTILINE); 

Le raisonnement derrière cela est que ^ matchs au début de la ligne, $ matchs à la fin de la ligne, avant un (en option) caractère de nouvelle ligne, et \s correspond à l'espace qui inclut \r et \n. Comme nous l'utilisons entre $ et ^, il ne peut correspondre qu'aux caractères de nouvelle ligne et non aux autres espaces.

Le (?:^|\\z) est utilisé pour s'assurer que nous ne correspondons pas accidentellement à des espaces de début dans la ligne suivant la dernière répétition de la ligne B. Si les lignes ne commencent jamais par des espaces, vous pouvez supprimer ce bit.

Questions connexes