comment utiliser l'expression régulière d'une ligne pour obtenir du contenu assorti

je suis un newbie de ruby, je veux savoir si je peux utiliser une seule ligne pour faire le travail.

prenez le 'search' de ce site par exemple. Lorsque l'Utilisateur a tapé [ruby] regex , je peux utiliser le code suivant pour obtenir l'étiquette et le mot-clé

'[ruby] regex' =~ /[(.*?)](.*)/
tag, keyword = , 

pouvons-nous l'écrire en une seule ligne?


mise à jour

Merci beaucoup! Je vous fais plus difficile et plus intéressant, que l'entrée contient plusieurs balises, comme:

[ruby] [regex] [rails] one line

est-il possible d'utiliser un code de ligne pour obtenir le tableau tags et le mot-clé? J'ai essayé, mais en vain.

28
demandé sur Freewind 2010-06-22 10:31:44

2 réponses

vous avez besoin de la méthode Regexp#match . Si vous écrivez /\[(.*?)\](.*)/.match('[ruby] regex') , vous obtiendrez un objet MatchData . Si nous appelons cet objet matches , alors, entre autres choses:

  • matches[0] renvoie la chaîne complète.
  • matches[n] renvoie le nième groupe de capture ( $n ).
  • matches.to_a renvoie un tableau composé de matches[0] à matches[N] .
  • matches.captures renvoie un tableau composé uniquement du groupe de capture ( matches[1] à matches[N] ).
  • matches.pre_match retourne tout avant la chaîne correspondante.
  • matches.post_match renvoie tout après la chaîne correspondante.

il y a plus de méthodes qui correspondent à d'autres variables spéciales, etc.; vous pouvez cocher la case MatchData docs pour plus d'. Ainsi, dans ce cas précis, tout ce que vous devez écrire est

tag, keyword = /\[(.*?)\](.*)/.match('[ruby] regex').captures

Edit 1: D'accord, pour votre tâche plus difficile, vous allez à la place vouloir la méthode String#scan , qui @Theo utilisé; cependant, nous allons utiliser un regex différent. Le code suivant devrait fonctionner:

# You could inline the regex, but comments would probably be nice.
tag_and_text = / \[([^\]]*)\] # Match a bracket-delimited tag,
                 \s*          # ignore spaces,
                 ([^\[]*) /x  # and match non-tag search text.
input        = '[ruby] [regex] [rails] one line [foo] [bar] baz'
tags, texts  = input.scan(tag_and_text).transpose

Le input.scan(tag_and_text) renvoie une liste de tag–search-couples de textes:

[ ["ruby", ""], ["regex", ""], ["rails", "one line "]
, ["foo", ""], ["bar", "baz"] ]

le transpose appelle flips que, de sorte que vous avez une paire consistant d'une liste d'étiquettes et d'une recherche-liste de texte:

[["ruby", "regex", "rails", "foo", "bar"], ["", "", "one line ", "", "baz"]]

vous pouvez alors faire ce que vous voulez avec les résultats. Je pourrais suggérer, par exemple

search_str = texts.join(' ').strip.gsub(/\s+/, ' ')

cela concaténera les morceaux de recherche avec des espaces simples, se débarrasser de l'espace principal et arrière whitespace, et remplacer les passages de plusieurs espaces avec un espace unique.

42
répondu Antal Spector-Zabusky 2017-05-23 10:30:46
'[ruby] regex'.scan(/\[(.*?)\](.*)/)

sera de retour

[["ruby", " regex"]]

vous pouvez en savoir plus sur String # scan ici: http://ruby-doc.org/core/classes/String.html#M000812 (en bref il renvoie un tableau de toutes les correspondances consécutives, le tableau extérieur dans ce cas est le tableau des correspondances, et l'intérieur est les groupes de capture de la correspondance unique).

pour faire la tâche vous pouvez la réécrire comme ceci (en supposant que vous n'aurez jamais qu'un match en la chaîne):

tag, keyword = '[ruby] regex'.scan(/\[(.*?)\](.*)/).flatten

selon exactement ce que vous voulez accomplir, vous pouvez modifier la regex

/^\s*\[(.*?)\]\s*(.+)\s*$/

qui correspond à l'ensemble de la chaîne de saisie, et ajuste quelques espaces du second groupe de capture. Ancrer le modèle au début et à la fin le rendra un peu plus efficace, et il évitera d'obtenir des correspondances fausses ou dupliquées dans certains cas (mais cela dépend beaucoup de l'entrée) -- cela garantit aussi que vous pouvez utilisez en toute sécurité le tableau retourné dans la tâche, parce qu'il n'aura jamais plus d'une correspondance.

quant à la question de suivi, c'est ce que je ferais:

def tags_and_keyword(input)
  input.scan(/^\s*\[(.+)\]\s+(.+)\s*$/) do |match|
    tags = match[0].split(/\]\s*\[/)
    line = match[1]
    return tags, line
  end
end

tags, keyword = tags_and_keyword('[ruby] [regex] [rails] one line')
tags # => ["ruby", "regex", "rails"]
keyword # => "one line"

il peut être réécrit en une ligne, mais je ne le ferais pas:

tags, keyword = catch(:match) { input.scan(/^\s*\[(.+)\]\s+(.+)\s*$/) { |match| throw :match, [match[0].split(/\]\s*\[/), match[1]] } }

ma solution suppose que toutes les étiquettes viennent avant le mot-clé, et qu'il n'y a qu'une seule étiquette/expression de mot-clé dans chaque entrée. Les premiers globs de capture toutes les étiquettes, mais ensuite j'ai divisé cette chaîne, donc c'est un processus en deux étapes (qui, comme @Tim l'a écrit dans son commentaire, est nécessaire sauf si vous avez un moteur capable d'appariement récursif).

11
répondu Theo 2010-06-22 13:12:41