Vous semblez mal comprendre comment les classes de caractères (par ex. [a-f0-9]
ou [^aeiouy]
). /[^abcd]/
ne pas nier le modèle abcd
, il dit "correspondre à tout caractère qui n'est pas 'a'
ou 'b'
ou 'c'
ou 'd'
".
Si vous voulez faire correspondre la négation d'un motif, utilisez la construction /(?!pattern)/
. C'est une correspondance de largeur nulle - ce qui signifie qu'elle ne correspond à aucun caractère, elle correspond à une position. Semblable à la façon dont /^/
et /$/
correspondent au début et à la fin d'une chaîne, ou /\b/
correspond à la limite d'un mot. Par exemple: /(?!xx)/
correspond à chaque position où le motif "xx" ne démarre pas.
En général, après avoir utilisé une négation de modèle, vous devez faire correspondre un caractère pour avancer dans la chaîne.
Donc, pour utiliser votre modèle:
form = /\/\*\s+(\w+)\s+\((\d{4})\)[0]{2}\s+\*\//m
form_and_fields = /#{form}((?:(?!#{form}).)+)/m
text.scan(form_and_fields)
De l'intérieur (je vais utiliser (?#comments)
)
(?!#{form})
votre modèle original nie, il correspond à une position où votre modèle original ne peut pas démarrer.
(?:(?!#{form}).)+
signifie correspondre à un caractère après cela, et essayez à nouveau, autant de fois que possible, mais au moins une fois. (?:(?#whatever))
est une parenthèse non-capturant - bon pour le groupement.
En RIR, cela donne:
irb> text.scan(form_and_fields)
=> [["F004", "0309", " \n /* field 1 */ \n /* field 2 */ \n ", nil, nil], ["F004", "0409", " \n /* field 1 */ \n /* field 2 */ \n", nil, nil]]
Les nil
s viennent des groupes de capture supplémentaires en form
qui sont utilisés dans le modèle niée (?!#{form})
et donc ne pas saisir quoi que ce soit sur un match réussi.
Cela pourrait être nettoyé quelques-uns:
form_and_fields = /#{form}\s*(.+?)\s*(?:(?=#{form})|\Z)/m
text.scan(form_and_fields)
Maintenant, au lieu d'un négatif zéro préanalyse largeur, nous utilisons un zéro avant positive largeur (?=#{form})
pour correspondre à la position de l'occurrence suivante form
. Donc, dans cette regex, nous correspondons à tout jusqu'à l'occurrence suivante de form
(sans, y compris la prochaine occurrence dans notre match). Cela nous permet de découper des espaces autour des champs. Nous devons également vérifier pour le cas où nous frappons la fin de la chaîne - /\Z/
, puisque cela pourrait arriver aussi.
En irb:
irb> text.scan(form_and_fields)
=> [["F004", "0309", "/* field 1 */ \n /* field 2 */", "F004", "0409"], ["F004", "0409", "/* field 1 */ \n /* field 2 */", nil, nil]]
Notez maintenant que les deux derniers champs sont peuplés la première fois - b/c la capture parens dans le zéro avant positive largeur trouvé quelque chose, même si elle n'a pas été marquée comme "consommé" pendant le processus - c'est pourquoi ce bit pourrait être réapparu pour la deuxième fois.
bonne explication des raisons pour lesquelles son chemin échoue . Je fais cela que le fait d'être à la fois avec l'ensemble de la chose niée est excessif pour ce (et la plupart) des scénarios, cependant. – singpolyma
D'accord. Mais je pensais que c'était un moment propice à l'apprentissage. – rampion
Merci, vous avez fait un excellent travail en expliquant cela. J'ai besoin d'utiliser la négation parce que le texte entre les formulaires n'est pas uniforme, l'exemple était trop simplifié. –