2010-01-06 4 views
7

Je dois diviser une chaîne en une liste de parties dans Ruby, mais j'ai besoin d'ignorer des choses à l'intérieur des paramètres. Par exemple:Fractionner une chaîne dans Ruby, en ignorant le contenu des parenthèses?

A +4, B +6, C (hello, goodbye) +5, D +3 

J'aimerais la liste résultant être:

[0]A +4 
[1]B +6 
[2]C (hello, goodbye) +5 
[3]D +3 

Mais je ne peux pas simplement diviser sur des virgules, parce que ce serait diviser le contenu des parenthèses. Existe-t-il un moyen de diviser les choses sans pré-analyser les virgules dans les accolades dans quelque chose d'autre?

Merci.

+0

Méfiez-vous des visages Frowny :-(déconner l'analyse syntaxique –

Répondre

13

Essayez ceci:

s = 'A +4, B +6, C (hello, goodbye) +5, D +3' 
tokens = s.scan(/(?:\(.*?\)|[^,])+/) 
tokens.each {|t| puts t.strip} 

Sortie:

A +4 
B +6 
C (hello, goodbye) +5 
D +3 

Une brève explication:

(?:  # open non-capturing group 1 
    \(  # match '(' 
    .*?  # reluctatly match zero or more character other than line breaks 
    \)  # match ')' 
    |  # OR 
    [^,]  # match something other than a comma 
)+   # close non-capturing group 1 and repeat it one or more times 

Une autre option est de diviser sur une virgule suivie de quelques espaces que lorsque le premier parenthèse qui peut être vu en regardant vers l'avant est une parenthèse ouvrante (ou pas de parenthèse du tout: ie. la fin de la chaîne):

s = 'A +4, B +6, C (hello, goodbye) +5, D +3' 
tokens = s.split(/,\s*(?=[^()]*(?:\(|$))/) 
tokens.each {|t| puts t} 

produira la même sortie, mais je trouve le plus propre méthode scan.

+0

# => [ "A +4", "B +6", "C (! bonjour, au revoir) +5 "," D +3 "] Cela me semble parfait, peut-être #trim pour enlever les espaces environnants –

+0

:) déjà vu les espaces et ajouté' trim' –

+0

Grande réponse, merci: – Colen

5
string = "A +4, B +6, C (hello, goodbye) +5, D +3" 
string.split(/ *, *(?=[^\)]*?(?:\(|$))/) 
# => ["A +4", "B +6", "C (hello, goodbye) +5", "D +3"] 

Comment cette regex fonctionne:

/ 
    *, *  # find comma, ignoring leading and trailing spaces. 
    (?=   # (Pattern in here is matched against but is not returned as part of the match.) 
    [^\)]*? # optionally, find a sequence of zero or more characters that are not ')' 
    (?:  # <non-capturing parentheses group> 
     \(  #  left paren ')' 
     |  #  - OR - 
     $  #  (end of string) 
    ) 
) 
/
+0

Cela peut être un peu énigmatique sans une explication pour le fan de regex timide au cœur de l'OP est probablement! :) Mais une bonne solution néanmoins. –

+0

Comment ça marche? Je n'ai pas trouvé de bonne documentation sur la façon dont regex a travaillé avec split - comme Bart K. dit que je ne suis pas très bon avec regexes – Colen

+0

@Colen, j'ai posté une regex très similaire comme une deuxième solution, y compris une explication. –

Questions connexes