2016-04-20 9 views
2

Ich möchte über eine Zeichenfolge mit einem Regex-Muster iterieren. Ich muss über die Übereinstimmungen sowie die Nicht-Übereinstimmungen zwischen ihnen iterieren und auf die Match-Informationen während der Iteration zugreifen.Gibt es eine Möglichkeit, während der Iteration mit `String # split 'auf die letzten Übereinstimmungsinformationen zuzugreifen?

Wenn ich nicht die Nicht-Übereinstimmungen zugreifen müssen, dann kann ich es tun mit String#scan:

"some string".scan(/(some pattern)|(another pattern)/) do 
    if $1 then ... 
    elsif $2 then ... 
    end 
end 

Aber ich brauche auch über die nicht passenden Teile zu durchlaufen, also muss ich wohl Verwenden Sie String#split. Aber String#split muss kein Block, und wenn ich each nachdem es verwenden möchte:

"some string".split(/((some pattern)|(another pattern))/).each do 
    ... 
end 

dann kann ich keinen Zugriff auf das Spiel Informationen in den Block. Ich möchte wie etwas tun:

"some string".split(/((some pattern)|(another pattern))/) do 
    if $2 then ... 
    elsif $3 then ... 
    else ... # access the non-matching part 
    end 
end 

Gibt es eine Möglichkeit das letzte Spiel auf Informationen zuzugreifen, während sie mit String#split Iterieren?

kann ich es Brute gewaltsam scan mit und das Hinzufügen von |(.*?) am Ende der Regex:

"some string".scan(/(some pattern)|(another pattern)|(.*?)/) do 
    if $1 then ... 
    elsif $2 then ... 
    elsif $3 then ... 
    end 
end 

aber ein nicht gieriges Spiel mit sehr ineffizient ist, und ich kann es nicht verwenden.

Antwort

2

Wenn Sie nur die Zeichenfolge ein Spiel zu einer Zeit mit match statt auf einmal wie mit scan verarbeiten, können Sie Daten aus pre_match in mit Ihren Ergebnissen injizieren:

def match_all(s, r) 
    match = s.match(r) 

    if match 
    pre_captures = [match.pre_match] + match.captures.map{nil} 
    captures = [nil] + match.captures 
    [pre_captures, captures] + match_all(match.post_match, r) 
    else 
    [[s]] 
    end 
end 

Diese Code-Transformationen die Eingabezeichenfolge in Tupel, die [unmatched data, first match group, second match group, etc...] und dann die Daten darstellen kann wiederholt werden, wie Sie wollen:

match_all("the match information in the block", /(at)|(in)/).each do |a, b, c| 
    if a 
    puts "(pre: #{a})" 
    elsif b 
    puts "(1st: #{b})" 
    elsif c 
    puts "(2nd: #{c})" 
    end 
end 

Welche Ausgänge:

(pre: the m) 
(1st: at) 
(pre: ch) 
(2nd: in) 
(pre: form) 
(1st: at) 
(pre: ion) 
(2nd: in) 
(pre: the block) 

Die gleiche Funktion auch iterativ wie so implementiert werden kann:

def match_all_iter(s, r) 
    s_mut = s 
    all_captures = [] 

    loop do 
    match = s_mut.match(r) 

    break unless match 

    pre_captures = [match.pre_match] + match.captures.map{nil} 
    captures = [nil] + match.captures 
    all_captures += [pre_captures, captures] 

    s_mut = match.post_match 
    end 

    all_captures += [[s_mut]] 
end 
+0

Es funktioniert, die Idee ist gut, aber es ist nicht bevorzugt, rekursive Aufruf wie folgt zu verwenden. Vielleicht können Sie eine ähnliche Sache stattdessen mit Iteration tun. – sawa

+1

@sawa iterative Version hinzugefügt – user12341234

2

Ich hatte die Idee, den nicht übereinstimmenden Teil und den passenden Teil pro Übereinstimmungszyklus zu durchlaufen. Dies verwendet scan anstelle von split und wird den Zweck erfüllen.

s = "some string" 
i = 0 
s.scan(/(some pattern)|(another pattern)|\z/) do 
    # Do something with the non-matching part 
    ... s[i...$~.begin(0)] ... # This corresponds to the string in between 
    i = $~.end(0) 
    # Do something with the matching part 
    if $1 then ... 
    elsif $2 then ... 
    end 
end 
Verwandte Themen